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样式扩展方法。