C#有擴展屬性嗎? git
例如,我能夠向DateTimeFormatInfo
添加一個名爲ShortDateLongTimeFormat
的擴展屬性,它將返回ShortDatePattern + " " + LongTimePattern
嗎? github
到目前爲止,擴展屬性被認爲不足以包含在之前版本的C#標準中。 C#7和C#8.0已將此視爲提案冠軍,但還沒有發佈,最重要的是由於即便已經有實施,他們也但願從一開始就作到正確。 dom
C#7工做列表中有一個擴展成員項,所以可能在不久的未來支持它。 擴展屬性的當前狀態能夠在相關項下的Github上找到。 ui
然而,有一個更有但願的主題是「擴展一切」 ,重點是特別是屬性和靜態類甚至字段。 this
如本文所述 ,您可使用TypeDescriptor
功能在運行時將屬性附加到對象實例。 可是,它沒有使用標準屬性的語法。
它與語法糖略有不一樣,增長了定義擴展屬性的可能性
string Data(this MyClass instance)
做爲擴展方法的別名
string GetData(this MyClass instance)
由於它將數據存儲到類中。 spa
我但願C#7能提供全功能的擴展(屬性和字段),可是在這一點上,只有時間會證實。 設計
並隨意貢獻,由於明天的軟件未來自社區。 code
更新:2016年8月 orm
隨着dotnet團隊發佈了C#7.0中的新內容以及Mads Torgensen的評論: xml
擴展屬性:咱們有一個(輝煌!)實習生在夏天實施它們做爲實驗,以及其餘類型的擴展成員。 咱們仍然對此感興趣,但這是一個很大的變化,咱們須要確信這是值得的。
彷佛擴展屬性和其餘成員仍然是將來發布的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;
與部分類相似,但在不一樣的程序集中編譯爲單獨的類/類型。 請注意,您也能夠經過這種方式添加靜態成員和運算符。 如Mads Torgensen播客中所述,擴展將不具備任何狀態(所以它沒法將私有實例成員添加到類中),這意味着您將沒法添加連接到該實例的私有實例數據 。 爲此調用的緣由是它意味着管理內部詞典而且可能很難(內存管理等)。 爲此,您仍然可使用前面描述的TypeDescriptor
/ ConditionalWeakTable
技術和屬性擴展,將其隱藏在一個不錯的屬性下。
語法仍然會發生變化,由於這意味着這個問題 。 例如, extends
能夠替換for
一些可能感受更天然且更少與Java相關的extends
。
更新2018年12月 - 角色,擴展和靜態接口成員
擴展一切都沒有進入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}"); }
對於靜態接口 :
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; }
由於我最近須要這個,因此我查看了答案的來源:
並建立了一個更動態的版本:
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; } }
它可能會改進不少(命名,動態而不是字符串),我目前在CF 3.5中使用它與hacky ConditionalWeakTable( https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 )
正如@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 < 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
更新(感謝@chaost指出此更新):
Mads Torgersen: 「擴展全部內容都沒有進入C#8.0。若是你願意的話,它會被」遇上「,在一場關於該語言將來發展的激動人心的辯論中,如今咱們要確保咱們不要以一種抑制將來可能性的方式添加它。有時語言設計是一個很是漫長的遊戲!「
來源: https : //blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/中的評論部分
多年來我一直打開這個問題,但願可以看到這個問題,我已經中止計算了多少次。
好吧,最後咱們都歡喜! 微軟將在他們即將發佈的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/
不,它們不存在於C#3.0中,也不會在4.0中添加。 它位於C#的功能需求列表中,所以可能會在未來添加。
此時,您能夠作的最好的是GetXXX樣式擴展方法。