Apakah C # memiliki properti ekstensi?

769

Apakah C # memiliki properti ekstensi?

Misalnya, dapatkah saya menambahkan properti ekstensi ke yang DateTimeFormatInfodipanggil ShortDateLongTimeFormatyang akan kembali ShortDatePattern + " " + LongTimePattern?

Svish
sumber
14
Saya ingin menambahkan metode ekstensi yang disebut IsNull di Nullable <T> yang hanya akan kembali! HasValue. .IsNull () jelas kurang cantik dari .IsNull
Ken
1
Saya menemukan ini berguna untuk operator ?
trinary
2
Saya ingin ini meniru Java enumyang dapat memiliki properti dan metode. C # enumtidak dapat memiliki properti atau metode, tetapi Anda dapat membuat metode ekstensi. Pertanyaan ini bermanfaat bagi saya, dan tidak boleh ditutup.
Ian McLaird
Meskipun, seperti yang dikatakan banyak orang, saat ini tidak ada rencana untuk menambahkan ini ke bahasa, tidak ada alasan itu tidak bisa dilakukan. Fakta bahwa F # tidak hanya memiliki properti ekstensi tetapi ekstensi statis juga bagi saya membuktikan bahwa itu setidaknya ide yang baik.
Richiban
2
Seharusnya dibuat satu
Rootel

Jawaban:

366

Untuk saat ini masih belum didukung oleh kompiler Roslyn ...

Sampai sekarang, properti ekstensi tidak dianggap cukup berharga untuk dimasukkan dalam versi standar C # sebelumnya. C # 7 dan C # 8.0 telah melihat ini sebagai juara proposal tetapi belum dirilis, terutama karena bahkan jika sudah ada implementasi, mereka ingin memperbaikinya sejak awal.

Tapi itu akan ...

Ada item anggota ekstensi dalam daftar kerja C # 7 sehingga dapat didukung dalam waktu dekat. Status properti ekstensi saat ini dapat ditemukan di Github di bawah item terkait .

Namun, ada topik yang bahkan lebih menjanjikan yaitu "memperluas segalanya" dengan fokus terutama pada properti dan kelas statis atau bahkan bidang.

Apalagi Anda bisa menggunakan solusi

Seperti yang ditentukan dalam artikel ini , Anda dapat menggunakan TypeDescriptorkemampuan untuk melampirkan atribut ke instance objek saat runtime. Namun, itu tidak menggunakan sintaks dari properti standar.
Ini sedikit berbeda dari hanya gula sintaksis yang menambahkan kemungkinan untuk mendefinisikan properti yang diperluas seperti
string Data(this MyClass instance)sebagai alias untuk metode ekstensi
string GetData(this MyClass instance)karena menyimpan data ke dalam kelas.

Saya berharap bahwa C # 7 akan memberikan segala fitur ekstensi lengkap (properti dan bidang), namun pada saat itu, hanya waktu yang akan mengatakan.

Dan jangan ragu untuk berkontribusi karena perangkat lunak masa depan akan datang dari komunitas.

Pembaruan: Agustus 2016

Ketika tim dotnet menerbitkan apa yang baru di C # 7.0 dan dari komentar Mads Torgensen :

Properti ekstensi: kami memiliki magang (brilian!) Menerapkannya selama musim panas sebagai percobaan, bersama dengan jenis anggota ekstensi lainnya. Kami tetap tertarik pada ini, tetapi ini adalah perubahan besar dan kami perlu merasa yakin bahwa itu layak dilakukan.

Tampaknya properti ekstensi dan anggota lainnya, masih merupakan kandidat yang baik untuk disertakan dalam rilis Roslyn di masa depan, tetapi mungkin bukan yang 7.0.

Pembaruan: Mei 2017

Anggota ekstensi telah ditutup sebagai duplikat dari semua masalah ekstensi yang ditutup juga. Diskusi utama sebenarnya tentang Jenis ekstensibilitas dalam arti luas. Fitur ini sekarang dilacak di sini sebagai proposal dan telah dihapus dari 7,0 tonggak sejarah .

Pembaruan: Agustus, 2017 - C # 8.0 fitur yang diusulkan

Meskipun masih tetap hanya fitur yang diusulkan , kami sekarang memiliki pandangan yang lebih jelas tentang apa yang akan menjadi sintaksnya. Ingatlah bahwa ini juga akan menjadi sintaks baru untuk metode ekstensi:

public interface IEmployee 
{
    public decimal Salary { get; set; }
}

public class Employee
{
    public decimal Salary { get; set; }
}

public extension MyPersonExtension extends Person : IEmployee
{
    private static readonly ConditionalWeakTable<Person, Employee> _employees = 
        new ConditionalWeakTable<Person, Employee>();


    public decimal Salary
    {
        get 
        {
            // `this` is the instance of Person
            return _employees.GetOrCreate(this).Salary; 
        }
        set 
        {
            Employee employee = null;
            if (!_employees.TryGetValue(this, out employee)
            {
                employee = _employees.GetOrCreate(this);
            }
            employee.Salary = value;
        }
    }
}

IEmployee person = new Person();
var salary = person.Salary;

Mirip dengan kelas parsial, tetapi dikompilasi sebagai kelas / jenis yang terpisah di majelis yang berbeda. Perhatikan bahwa Anda juga dapat menambahkan anggota dan operator statis dengan cara ini. Seperti disebutkan dalam podcast Mads Torgensen , ekstensi tidak akan memiliki status apa pun (sehingga tidak dapat menambahkan anggota instance pribadi ke kelas) yang berarti Anda tidak akan dapat menambahkan data instance pribadi yang ditautkan ke instance . Alasan yang digunakan untuk itu adalah menyiratkan untuk mengelola kamus internal dan itu bisa sulit (manajemen memori, dll ...). Untuk ini, Anda masih bisa menggunakan TypeDescriptor/ ConditionalWeakTableteknik yang dijelaskan sebelumnya dan dengan ekstensi properti, menyembunyikannya di bawah properti yang bagus.

Sintaks masih dapat berubah karena menyiratkan masalah ini . Misalnya, extendsdapat diganti dengan foryang beberapa mungkin merasa lebih alami dan kurang terkait java.

Pembaruan Desember 2018 - Peran, Ekstensi, dan anggota antarmuka statis

Perpanjangan semuanya tidak berhasil mencapai C # 8.0, karena beberapa kelemahan dijelaskan sebagai akhir dari tiket GitHub ini . Jadi, ada eksplorasi untuk meningkatkan desain. Di sini , Mads Torgensen menjelaskan apa peran dan ekstensi dan bagaimana perbedaannya:

Peran memungkinkan antarmuka untuk diimplementasikan pada nilai-nilai spesifik dari tipe yang diberikan. Ekstensi memungkinkan antarmuka untuk diterapkan pada semua nilai dari jenis yang diberikan, dalam wilayah kode tertentu.

Hal ini dapat dilihat pada sepersekian proposal sebelumnya dalam dua kasus penggunaan. The sintaks baru untuk perpanjangan akan seperti ini:

public extension ULongEnumerable of ulong
{
    public IEnumerator<byte> GetEnumerator()
    {
        for (int i = sizeof(ulong); i > 0; i--)
        {
            yield return unchecked((byte)(this >> (i-1)*8));
        }
    }
}

maka Anda akan dapat melakukan ini:

foreach (byte b in 0x_3A_9E_F1_C5_DA_F7_30_16ul)
{
    WriteLine($"{e.Current:X}");
}

Dan untuk antarmuka statis :

public interface IMonoid<T> where T : IMonoid<T>
{
    static T operator +(T t1, T t2);
    static T Zero { get; }
}

Menambahkan properti ekstensi pada intdan memperlakukan intsebagai IMonoid<int>:

public extension IntMonoid of int : IMonoid<int>
{
    public static int Zero => 0;
}
Hebat
sumber
58
Ini adalah salah satu jawaban paling berguna yang pernah saya ikuti di StackExchange. Terus memperbarui dengan status dan membuat semua orang mendapat informasi yang kembali ke ini, memberikan tautan yang kuat ke diskusi dan sejarah.
bdrelling
25
Sungguh luar biasa bahwa Anda terus memperbarui ini - terima kasih
David Thielen
1
Sayangnya pada komentar ini, peran, ekstensi dan anggota antarmuka statis hanya ditandai untuk C # 11 :(
Ian Kemp
436

Tidak, mereka tidak ada di C # 3.0 dan tidak akan ditambahkan dalam 4.0. Itu ada di daftar fitur yang diinginkan untuk C # sehingga dapat ditambahkan di masa mendatang.

Pada titik ini yang terbaik yang dapat Anda lakukan adalah metode ekstensi gaya GetXXX.

JaredPar
sumber
3
Demikian pula dengan properti generik: Anda harus menggunakan sintaks 'GetXXX <>'.
Jay Bazuzi
3
ok, itulah yang saya pikirkan. @ Jay, ya, aku juga benci itu, hehe. Terutama ketidakmampuan untuk memiliki pengindeks generik ... sigh
Svish
75
Tautan ke daftar keinginan fitur?
Dan Esparza
2
Bagaimana dengan Versi 6.0 dan 7.0?
Falk
2
Adakah pembaruan tentang ini pada tahun 2020?
Chad
265

Tidak, mereka tidak ada.

Saya tahu bahwa tim C # sedang mempertimbangkan mereka pada satu titik (atau setidaknya Eric Lippert) - bersama dengan konstruktor ekstensi dan operator (mereka mungkin membutuhkan waktu untuk menggerakkan kepala Anda, tetapi keren ...) Namun, saya belum tidak melihat bukti bahwa mereka akan menjadi bagian dari C # 4.


EDIT: Mereka tidak muncul di C # 5, dan pada Juli 2014 sepertinya tidak akan berada di C # 6 juga.

Eric Lippert , Pengembang Utama pada tim penyusun C # di Microsoft melalui November 2012, membuat blog tentang ini pada Oktober 2009:

Jon Skeet
sumber
2
Ya, dan mereka masih bisa menyembunyikan bidang - pengaturan satu properti mungkin menetapkan dua properti di bawahnya, atau sebaliknya. (Bayangkan sesuatu dengan properti Ukuran normal, dan properti ekstensi Lebar / Tinggi, atau sebaliknya.) Namun, mereka akan lebih berguna sebagai properti hanya-baca, saya kira.
Jon Skeet
23
Anda tidak dapat mengikat metode ekstensi ... bisa menambahkan properti Anda sendiri untuk penyatuan data bisa membantu dalam banyak situasi.
Nick
3
@ leppie - Nilai ekstensi properti akan menguntungkan properti bool dan string yang paling saya pikir. Menyingkirkan ()pada akhirnya jauh lebih mudah dibaca. Saya tahu secara pribadi, setidaknya 90% dari ekstensi yang saya tulis adalah dari 2 jenis itu.
Code Maverick
4
Untuk memberikan contoh mengapa ini berguna, saya memiliki model EFCF. Di beberapa kelas saya memiliki properti hanya baca yang saya gunakan untuk mengembalikan informasi yang diformat: FullName= FirstName + LastName, ShortName= FirstName + LastName[0]. Saya ingin menambahkan lebih banyak properti ini, tetapi saya tidak ingin "mengotori" kelas sebenarnya. Dalam hal ini properti ekstensi, yang hanya baca, sangat sempurna karena saya dapat menambahkan fungsionalitas, menjaga kelas utama tetap bersih, dan masih mengekspos informasi yang ingin saya paparkan di UI.
Gup3rSuR4c
4
@ JonSkeet: Anda benar, saya akhirnya melakukan apa yang saya inginkan dengan membuat kelas saya sendiri, kemudian membungkus semua metode dan properti kelas tersegel yang relevan, kemudian memberikan static implicit operator FileInfo(FileInfoEx fex)yang mengembalikan objek FileInfo saya yang terkandung. Ini secara efektif memungkinkan saya memperlakukan FileInfoEx seolah-olah mewarisi dari FileInfo, meskipun kelas itu disegel.
Steve L
27

Pembaruan (terima kasih kepada @chaost untuk menunjukkan pembaruan ini):

Mads Torgersen: "Perpanjangan segalanya tidak membuatnya menjadi C # 8.0. Itu" terperangkap ", jika Anda mau, dalam debat yang sangat menarik tentang masa depan bahasa yang lebih jauh, dan sekarang kami ingin memastikan bahwa kami tidak tambahkan dengan cara yang menghambat kemungkinan di masa depan. Kadang-kadang desain bahasa adalah permainan yang sangat panjang! "

Sumber: bagian komentar di https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/


Saya berhenti menghitung berapa kali selama bertahun-tahun saya membuka pertanyaan ini dengan harapan telah melihat ini diterapkan.

Yah, akhirnya kita semua bisa bersukacita! Microsoft akan memperkenalkan ini dalam rilis C # 8 mendatang.

Jadi alih-alih melakukan ini ...

public static class IntExtensions
{
   public static bool Even(this int value)
   {
        return value % 2 == 0;
   }
}

Kami akhirnya akan bisa melakukannya seperti ...

public extension IntExtension extends int
{
    public bool Even => this % 2 == 0;
}

Sumber: https://blog.ndepend.com/c-8-0-features-glimpse-future/

Korayem
sumber
3
Minggu ini fitur C # 8.0 diumumkan dan sayangnya saya tidak melihat ini.
Mateo Torres-Ruiz
1
@ MateoTorres-Ruiz Sebuah komentar dari 'Mads Torgersen' (C # dev), menjawab kepada seseorang yang bertanya tentang hal itu (3 hari yang lalu): "Perpanjangan semuanya tidak membuatnya menjadi C # 8.0. Ia" tertangkap ", jika Anda mau , dalam debat yang sangat menarik tentang masa depan bahasa yang lebih jauh, dan sekarang kami ingin memastikan kami tidak menambahkannya dengan cara yang menghambat kemungkinan masa depan itu. Terkadang desain bahasa adalah permainan yang sangat panjang! " Terasa buruk .. (Baca ini di tautan Korayems, di bagian komentar)
Chaost
8

Seperti @Psyonity disebutkan, Anda bisa menggunakan conditionalWeakTable untuk menambahkan properti ke objek yang ada. Dikombinasikan dengan ExpandoObject dinamis, Anda dapat menerapkan properti ekstensi dinamis dalam beberapa baris:

using System.Dynamic;
using System.Runtime.CompilerServices;

namespace ExtensionProperties
{
    /// <summary>
    /// Dynamically associates properies to a random object instance
    /// </summary>
    /// <example>
    /// var jan = new Person("Jan");
    ///
    /// jan.Age = 24; // regular property of the person object;
    /// jan.DynamicProperties().NumberOfDrinkingBuddies = 27; // not originally scoped to the person object;
    ///
    /// if (jan.Age &lt; jan.DynamicProperties().NumberOfDrinkingBuddies)
    /// Console.WriteLine("Jan drinks too much");
    /// </example>
    /// <remarks>
    /// If you get 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create' you should reference Microsoft.CSharp
    /// </remarks>
    public static class ObjectExtensions
    {
        ///<summary>Stores extended data for objects</summary>
        private static ConditionalWeakTable<object, object> extendedData = new ConditionalWeakTable<object, object>();

        /// <summary>
        /// Gets a dynamic collection of properties associated with an object instance,
        /// with a lifetime scoped to the lifetime of the object
        /// </summary>
        /// <param name="obj">The object the properties are associated with</param>
        /// <returns>A dynamic collection of properties associated with an object instance.</returns>
        public static dynamic DynamicProperties(this object obj) => extendedData.GetValue(obj, _ => new ExpandoObject());
    }
}

Contoh penggunaan ada di komentar xml:

var jan = new Person("Jan");

jan.Age = 24; // regular property of the person object;
jan.DynamicProperties().NumberOfDrinkingBuddies = 27; // not originally scoped to the person object;

if (jan.Age < jan.DynamicProperties().NumberOfDrinkingBuddies)
{
    Console.WriteLine("Jan drinks too much");
}

jan = null; // NumberOfDrinkingBuddies will also be erased during garbage collection
realbart
sumber
Jawaban terbaik
N73k
1

Karena saya baru-baru ini membutuhkan ini, saya melihat sumber jawabannya di:

c # memperpanjang kelas dengan menambahkan properti

dan membuat versi yang lebih dinamis:

public static class ObjectExtenders
{
    static readonly ConditionalWeakTable<object, List<stringObject>> Flags = new ConditionalWeakTable<object, List<stringObject>>();

    public static string GetFlags(this object objectItem, string key)
    {
        return Flags.GetOrCreateValue(objectItem).Single(x => x.Key == key).Value;
    }

    public static void SetFlags(this object objectItem, string key, string value)
    {
        if (Flags.GetOrCreateValue(objectItem).Any(x => x.Key == key))
        {
            Flags.GetOrCreateValue(objectItem).Single(x => x.Key == key).Value = value;
        }
        else
        {
            Flags.GetOrCreateValue(objectItem).Add(new stringObject()
            {
                Key = key,
                Value = value
            });
        }
    }

    class stringObject
    {
        public string Key;
        public string Value;
    }
}

Ini mungkin dapat ditingkatkan banyak (penamaan, dinamis, bukan string), saya saat ini menggunakan ini di CF 3.5 bersama-sama dengan ConditionalWeakTable hacky ( https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 )

Psyonity
sumber
Maaf, tetapi meskipun ini terlihat sangat menyeluruh, ini tidak ada hubungannya dengan properti ekstensi, tetapi hanya menunjukkan metode ekstensi.
Viking