簡體   English   中英

C# 有擴展屬性嗎?

[英]Does C# have extension properties?

C# 有擴展屬性嗎?

例如,我可以向DateTimeFormatInfo添加一個名為ShortDateLongTimeFormat的擴展屬性,它會返回ShortDatePattern + " " + LongTimePattern嗎?

目前,Roslyn 編譯器仍然不支持開箱即用...

到目前為止,擴展屬性的價值還不足以包含在以前版本的 C# 標准中。 C# 7C# 8.0已將此視為提案冠軍,但尚未發布,主要是因為即使已經實施,他們也希望從一開始就做好。

但它會...

C# 7 工作列表中有一個擴展成員項,因此它可能會在不久的將來得到支持。 擴展屬性的當前狀態可以在Github 的相關項目下找到。

然而,還有一個更有希望的主題是“擴展一切” ,特別關注屬性和 static 類甚至字段。

此外,您可以使用解決方法

本文所述,您可以使用TypeDescriptor功能在運行時將屬性附加到 object 實例。 但是,它沒有使用標准屬性的語法。
它與僅僅增加了定義擴展屬性的可能性的語法糖有點不同
string Data(this MyClass instance)作為擴展方法的別名
string GetData(this MyClass instance) ,因為它將數據存儲到 class 中。

我希望 C#7 將提供一個全功能的擴展一切(屬性和字段),但是在這一點上,只有時間會證明一切。

並隨時做出貢獻,因為明天的軟件將來自社區。

更新:2016 年 8 月

正如 dotnet 團隊發布了 C# 7.0 中的新功能以及來自Mads Torgensen的評論:

擴展屬性:我們有一個(出色的)實習生在夏天將它們作為實驗來實現。 與其他類型的擴展成員一起,我們對此仍然感興趣。 但這是一個很大的變化,我們需要確信這是值得的。

似乎擴展屬性和其他成員仍然是包含在 Roslyn 未來版本中的不錯候選者,但可能不是 7.0 版本。

更新:2017 年 5 月

擴展成員已關閉,作為擴展所有問題的副本,該問題也已關閉。 主要討論實際上是關於廣義上的類型可擴展性。 該功能現在在此處作為提案進行跟蹤,並已從7.0 里程碑中刪除。

更新:2017 年 8 月 - C# 8.0 建議功能

雖然它仍然只是一個提議的功能,但我們現在對它的語法有了更清晰的認識。 請記住,這也將是擴展方法的新語法:

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;

類似於部分類,但在不同的程序集中編譯為單獨的類/類型。 請注意,您還可以通過這種方式添加 static 成員和運算符。 正如Mads Torgensen 播客中提到的,該擴展不會有任何 state(因此它不能將私有實例成員添加到類),這意味着您將無法添加鏈接到實例的私有實例數據 為此調用的原因是它意味着管理內部字典並且可能很困難(內存管理等)。 為此,您仍然可以使用前面描述的TypeDescriptor / ConditionalWeakTable技術,並使用屬性擴展,將其隱藏在一個不錯的屬性下。

正如這個問題所暗示的那樣,語法仍然會發生變化。 例如, extends可以替換for一些可能感覺更自然且與 java 相關的更少。

2018 年 12 月更新 - 角色、擴展和 static 接口成員

擴展一切都沒有使它成為 C# 8.0,因為一些缺點解釋為這個GitHub 票的結尾。 因此,進行了改進設計的探索。 在這里,Mads Torgensen 解釋了什么是角色和擴展以及它們之間的區別:

角色允許在給定類型的特定值上實現接口。 擴展允許在特定代碼區域內對給定類型的所有值實現接口。

可以從兩個用例中的先前提案的拆分中看出。 擴展的新語法如下:

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

那么你就可以做到這一點:

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

對於static 接口

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

int上添加擴展屬性並將int視為IMonoid<int>

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

不,它們在 C# 3.0 中不存在,並且不會在 4.0 中添加。 它在 C# 的功能列表中,因此可能會在將來添加。

此時你能做的最好的就是GetXXX風格的擴展方法。

不,它們不存在。

我知道 C# 團隊曾經考慮過它們(或者至少 Eric Lippert 曾經考慮過) - 以及擴展構造函數和運算符(這些可能需要一段時間才能讓你明白,但很酷......)但是,我沒有沒有看到任何證據表明它們將成為 C# 4 的一部分。


編輯:它們沒有出現在 C# 5 中,截至 2014 年 7 月,它看起來也不會出現在 C# 6 中。

Eric Lippert是微軟 C# 編譯器團隊的首席開發人員,截至 2012 年 11 月,他在 2009 年 10 月發表了關於此的博文:

更新(感謝@chaost指出此更新):

Mads Torgersen: “將所有內容都沒有擴展到 C# 8.0。如果你願意的話,它在關於語言未來的非常激動人心的辯論中被“趕上了”,現在我們要確保我們不會以一種抑制未來可能性的方式添加它。有時語言設計是一場漫長的游戲!

來源: https 中的評論部分://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/


我停止計算多年來我打開這個問題的次數,希望看到這個問題得到實施。

好吧,我們終於可以歡欣鼓舞了。 Microsoft 將在其即將發布的 C# 8 版本中引入此功能。

因此,與其這樣做...

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

我們終於可以這樣做了……

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

資料來源: https://blog.ndepend.com/c-8-0-features-glimpse-future/

正如@Psyonity 提到的,您可以使用 conditionalWeakTable 向現有對象添加屬性。 結合動態 ExpandoObject,您可以用幾行代碼實現動態擴展屬性:

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

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

因為我最近需要這個,所以我查看了答案的來源:

c# 通過添加屬性擴展 class

並創建了一個更動態的版本:

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

It can probably be improved a lot (naming, dynamic instead of string), I currently use this in CF 3.5 together with a hacky ConditionalWeakTable ( https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 )

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM