C # uzantı özelliklerine sahip mi?


768

C # uzantı özelliklerine sahip mi?

Mesela ben bir uzantısı özelliği ekleyebilir DateTimeFormatInfodenilen ShortDateLongTimeFormatdönecekti hangi ShortDatePattern + " " + LongTimePattern?


14
Ben sadece dönecekti Nullable <T> üzerinde IsNull adlı bir uzantı yöntemi eklemek istedim! HasValue. .IsNull () kesinlikle daha az güzel .IsNull daha
Ken

1
Bunu üçlü operatör için yararlı buluyorum?
PedroC88

2
Bunu enumözellikleri ve yöntemleri olabilir Java's taklit etmek istedim . C # 'ın enumözellikleri veya yöntemleri olamaz , ancak üzerinde uzantı yöntemleri oluşturabilirsiniz. Bu soru benim için yararlıydı ve kapatılmamalıdır.
Ian McLaird

Birçok kişinin söylediği gibi, şu anda bunu dile eklemek için herhangi bir plan bulunmamakla birlikte, bunun yapılamamasının bir nedeni yoktur. F # 'ın sadece uzantı özelliklerine değil, aynı zamanda statik uzantılara sahip olması da en azından iyi bir fikir olduğunu kanıtlıyor.
Richiban

2
Bir tane yapılmalı
Rootel

Yanıtlar:


365

Şu an için hala Roslyn derleyicisi tarafından kutudan çıkarılmıyor ...

Şimdiye kadar, uzantı özellikleri C # standardının önceki sürümlerine dahil edilecek kadar değerli görülmüyordu. C # 7 ve C # 8.0 bunu teklif şampiyonu olarak gördüler, ancak henüz yayınlanmadı, çünkü zaten bir uygulama olsa bile, en baştan doğru yapmak istiyorlar.

Fakat olacak ...

C # 7 çalışma listesinde bir uzantı üyeleri öğesi vardır, bu nedenle yakın gelecekte desteklenebilir. Extension özelliğinin geçerli durumu Github'da ilgili öğenin altında bulunabilir .

Bununla birlikte, özellikle özelliklere ve statik sınıflara ve hatta alanlara odaklanarak "her şeyi genişlet" olan daha da umut verici bir konu var .

Ayrıca bir geçici çözüm kullanabilirsiniz

Bu belirtildiği gibi makalesinde , kullanabileceğiniz TypeDescriptorçalışma zamanında bir nesne örneğine bir öznitelik eklemek için yeteneği. Ancak, standart özelliklerin sözdizimini kullanmaz.
Sadece sözdizimsel şekerden biraz farklıdır, verileri sınıfa depolarken
string Data(this MyClass instance)uzatma yöntemi için bir takma ad gibi genişletilmiş bir özellik tanımlama olanağı ekler
string GetData(this MyClass instance).

Umarım C # 7 tam özellikli bir uzantı sağlar her şey (özellikler ve alanlar), ancak bu noktada, sadece zaman söyleyecektir.

Ve yarının yazılımı topluluktan geleceği için katkıda bulunmaktan çekinmeyin.

Güncelleme: Ağustos 2016

Dotnet ekibi C # 7.0'daki yenilikleri ve Mads Torgensen'in bir yorumundan yayınladı :

Uzatma özellikleri: yaz boyunca onları bir deney olarak uygulayan (parlak!) Bir stajyerimiz vardı. Bununla ilgilenmeye devam ediyoruz, ancak bu büyük bir değişiklik ve buna değdiğinden emin olmamız gerekiyor.

Uzatma mülkleri ve diğer üyeler, Roslyn'in gelecekteki bir sürümüne dahil edilmesi için hala iyi adaylar gibi görünüyor, ancak belki de 7.0 değil.

Güncelleme: Mayıs 2017

Uzatma üyeleri arasında kopyası olarak kapatıldı uzatma herşey konuyla çok kapalıdır. Ana tartışma aslında geniş anlamda Tip genişletilebilirliği hakkındaydı. Özellik şimdi bir teklif olarak izleniyorve 7,0 kilometre taşından kaldırıldı.

Güncelleme: Ağustos, 2017 - C # 8.0 önerilen özellik

Hala önerilen bir özellik olarak kalmasına rağmen , şimdi sözdizimi ne olacağına dair daha net bir görüşe sahibiz. Bunun, uzantı yöntemleri için de yeni sözdizimi olacağını unutmayın:

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;

Kısmi sınıflara benzer, ancak farklı bir montajda ayrı bir sınıf / tür olarak derlenir. Bu yolla statik üyeler ve işleçler de ekleyebileceğinizi unutmayın. Mads Torgensen podcast'inde de belirtildiği gibi , uzantının herhangi bir durumu olmaz (bu nedenle sınıfa özel yönetim ortamı üyeleri ekleyemez) , yani yönetim ortamına bağlı özel yönetim ortamı verileri ekleyemezsiniz . Bunun nedeni, dahili sözlükleri yönetmek anlamına gelmesi ve zor olabilmesidir (bellek yönetimi, vb ...). Bunun için, daha önce açıklanan TypeDescriptor/ ConditionalWeakTabletekniğini kullanmaya devam edebilirsiniz ve özellik uzantısıyla, güzel bir özellik altında gizler.

Sözdizimi hala bu sorunu ima ettiği için değişebilir . Örneğin, bazıları daha doğal ve daha az java ile ilgili hissedebilecekleri extendsile değiştirilebilir for.

Aralık 2018 Güncellemesi - Roller, Uzantılar ve statik arayüz üyeleri

Uzantı , GitHub biletinin sonu olarak açıklanan bazı dezavantajlar nedeniyle C # 8.0'a her şeyi yapmadı . Yani, tasarımı geliştirmek için bir keşif vardı. Burada Mads Torgensen, rollerin ve uzantıların ne olduğunu ve nasıl farklı olduklarını açıklıyor :

Roller, arabirimlerin belirli bir türdeki belirli değerlere uygulanmasına izin verir. Uzantılar, arabirimlerin belirli bir kod bölgesinde belirli bir türdeki tüm değerlere uygulanmasına izin verir.

İki kullanım durumunda önceki teklifin bir bölümünde görülebilir. Uzantı için yeni sözdizimi şöyle olacaktır:

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));
        }
    }
}

o zaman bunu yapabilirsiniz:

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

Ve statik bir arayüz için :

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

Üzerinde bir extension özelliği ekleyin intve intas IMonoid<int>:

public extension IntMonoid of int : IMonoid<int>
{
    public static int Zero => 0;
}

57
Bu StackExchange üzerinde takip ettiğim en yararlı cevaplardan biridir. Durumla sürekli güncelleme yapmak ve buna geri dönen herkesi bilgilendirmek, tartışma ve tarihe sağlam bağlantılar sağlamak.
19:05

25
Bunu güncel tutmanız müthiş - teşekkürler
David Thielen

1
Ne yazık ki bu yorumdan itibaren roller, uzantılar ve statik arayüz üyeleri sadece C # 11 için işaretleniyor :(
Ian Kemp

436

Hayır, C # 3.0'da mevcut değiller ve 4.0'a eklenmeyecekler. Gelecekte C # için eklenebilecek özellik listesinde yer alır.

Bu noktada yapabileceğiniz en iyi şey GetXXX tarzı uzantı yöntemleridir.


3
Genel özelliklere benzer şekilde: 'GetXXX <>' sözdizimini kullanmanız gerekir.
Jay Bazuzi

3
tamam, ben de öyle düşünmüştüm. @Jay, evet, ben de nefret ediyorum hehe. Özellikle jenerik bir endeksleyiciye sahip olamama ... iç çekiş
Svish

75
Özellik istekleri listesine bağlantı?
Dan Esparza

2
6.0 ve 7.0 sürümleri ne olacak?
Falk

2
Bu konuda 2020'den itibaren güncelleme var mı?
Çad

265

Hayır, yoklar.

C # ekibinin onları bir noktada (veya en azından Eric Lippert olduğu) düşündüğünü biliyorum - uzatma kurucuları ve operatörleri ile birlikte (bunlar başınızı döndürmek biraz zaman alabilir, ancak havalı ...) Ancak, C # 4'ün bir parçası olacağına dair bir kanıt görmedim.


DÜZENLEME: C # 5'de görünmediler ve Temmuz 2014 itibariyle C # 6'da görünecek gibi görünmüyor.

Kasım 2012'de Microsoft'taki C # derleyici ekibinin Baş Geliştiricisi Eric Lippert , Ekim 2009'da bunun hakkında blog yazdı:


2
Evet ve alanı yine de gizleyebilirler - tek bir özellik ayarlamak, altında iki özellik ayarlayabilir veya tersi de geçerlidir. (Normal Size özelliği ve Genişlik / Yükseklik uzantısı özelliklerine sahip bir şey düşünün veya tam tersi.) Yine de, salt okunur olanlar olarak daha yararlı olurlar.
Jon Skeet

23
Uzantı yöntemlerine bağlanamazsınız ... veri bağlama için kendi özelliklerinizi ekleyebilmek birçok durumda yardımcı olabilir.
Nick

3
@leppie - Özellik uzantılarının değeri bence en çok bool ve string özelliklerine fayda sağlayacaktır. ()Sonunda kurtulmak çok daha okunabilir. Şahsen benim için, yazdığım uzantıların en az% 90'ı bu 2 türden.
Kod Maverick

4
Bunun neden faydalı olabileceğine dair bir örnek vermek için bir EFCF modelim var. Bazı sınıflarda biçimlendirilmiş bilgileri döndürmek için kullandığım salt okunur özellikler var: FullName= FirstName + LastName, ShortName= FirstName + LastName[0]. Bu özelliklerin daha fazla eklemek istiyorum, ama gerçek sınıfları "kirli" istemiyorum. Bu durumda, salt okunur olan bir extension özelliği mükemmeldir, çünkü işlevselliği ekleyebilir, ana sınıfı temiz tutabilirim ve yine de kullanıcı arayüzünde göstermek istediğim bilgileri açığa çıkarabilirim.
Gup3rSuR4c

4
@JonSkeet: Haklısın, kendi sınıfımı oluşturarak, ardından ilgili tüm mühürlü sınıf yöntemlerini ve özelliklerini sararak, sonra da static implicit operator FileInfo(FileInfoEx fex)içerdiğim FileInfo nesnesini döndürerek sağlayarak istediğimi yaptım. Bu etkili bir şekilde FileInfoEx, o sınıf kapalı olsa bile FileInfo devralmış gibi davranmama izin verir.
Steve L

27

Güncelleme ( bu güncellemeyi gösterdiği için @ chaost'a teşekkürler ):

Mads Torgersen: "Uzatma her şeyi C # 8.0'a dönüştürmedi. Dilin ilerideki geleceği hakkında çok heyecan verici bir tartışmada" yakalandı "ve şimdi yapmadığımızdan emin olmak istiyoruz bu gelecekteki olasılıkları engelleyecek şekilde ekleyin. Bazen dil tasarımı çok uzun bir oyun! "

Kaynak: https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/ adresindeki yorumlar bölümü


Yıllar boyunca kaç kez bu soruyu hayata geçirdiğini umarak açtığımı saymayı bıraktım.

Sonunda hepimiz sevinebiliriz! Microsoft bunu yaklaşan C # 8 sürümlerinde tanıtacaktır.

Yani bunu yapmak yerine ...

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

Sonunda böyle yapabiliriz ...

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

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


3
Bu hafta C # 8.0 özellikleri açıklandı ve ne yazık ki bunların hiçbirini görmedim.
Mateo Torres-Ruiz

1
@ MateoTorres-Ruiz 'Mads Torgersen' 'den (C # dev) bir yorum, (3 gün önce) hakkında soru soran birine cevap: "Uzatma her şeyi C # 8.0'a dönüştürmedi. Eğer" yakalandı " , dilin ilerideki geleceği hakkında çok heyecan verici bir tartışmada ve şimdi bu gelecekteki olasılıkları engelleyecek şekilde eklemediğimizden emin olmak istiyoruz. Bazen dil tasarımı çok uzun bir oyun! " Feels bad .. (Bunu Korayems linkinde, yorum bölümünde okuyun)
Chaost

8

@ Bölümü'nde belirtildiği gibi, varolan nesnelere özellik eklemek için conditionalWeakTable'ı kullanabilirsiniz. Dinamik ExpandoObject ile birlikte birkaç satırda dinamik uzantı özellikleri uygulayabilirsiniz:

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());
    }
}

Kullanım örneği xml açıklamalarındadır:

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

En iyi cevap
N73k

1

Son zamanlarda buna ihtiyacım olduğu için, cevabın kaynağına baktım:

c # özellikleri ekleyerek sınıfı genişletir

ve daha dinamik bir sürüm oluşturdu:

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;
    }
}

Muhtemelen çok geliştirilebilir (adlandırma, dize yerine dinamik), şu anda CF 3.5'te hacky ConditionalWeakTable ( https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 ) ile birlikte kullanıyorum


Maalesef, bu çok kapsamlı görünse de, uzantı özellikleriyle ilgisi yoktur, ancak yalnızca uzantı yöntemlerini gösterir.
Viking
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.