Does C# have extension properties?
C是否具有扩展属性?
例如,我可以在
不,它们不存在于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月的博客中写到:
- 为什么没有扩展属性?–在编码方面的惊人冒险
目前,Roslyn编译器仍然不支持它。
直到现在,扩展属性还没有被认为有足够的价值,不足以包含在C标准的早期版本中。C 7和C 8.0将其视为提案的拥护者,但它尚未发布,最重要的是,即使已经有了一个实现,他们也希望从一开始就实现它。
但它会…C 7工作列表中有一个扩展成员项,因此它可能在不久的将来得到支持。可在相关项下的GitHub上找到扩展属性的当前状态。
然而,还有一个更具前景的主题是"扩展一切",重点放在属性、静态类甚至字段上。
此外,您还可以使用变通方法如本文所述,您可以使用
它与简单的语法结构稍有不同,因为它可以在类中存储数据时,将扩展属性(如
EDOCX1[1])定义为扩展方法
EDOCX1[2]的别名。
我希望C 7将提供一个完整的功能扩展,所有的东西(属性和字段),但是在这一点上,只有时间可以证明。
因为明天的软件将来自社区,所以您可以自由地做出贡献。
更新日期:2016年8月
正如Dotnet团队在C 7.0和Mads Torgensen的评论中发布的一样:
Extension properties: we had a (brilliant!) intern implement them over
the summer as an experiment, along with other kinds of extension
members. We remain interested in this, but it’s a big change and we
need to feel confident that it’s worth it.
似乎扩展属性和其他成员仍然是很好的候选者,可以在将来的Roslyn版本中包含,但可能不是7.0版本。
更新日期:2017年5月
扩展成员已作为扩展的副本关闭,所有问题也已关闭。主要讨论的实际上是广义上的类型可扩展性。此功能现在作为建议在此处跟踪,已从7.0里程碑中删除。
更新:2017年8月-C 8.0提议功能
虽然它仍然只是一个提议的特性,但我们现在对它的语法有了更清晰的了解。请记住,这也是扩展方法的新语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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播客中所述,扩展将没有任何状态(因此它不能向类中添加私有实例成员),这意味着您将无法添加链接到实例的私有实例数据。为此调用的原因是它意味着要管理内部字典,这可能很困难(内存管理等)。为此,您仍然可以使用前面描述的
语法仍有可能发生变化,这意味着这个问题。例如,EDCOX1的5度可以被EDCOX1(6)所代替,一些EDCOX1可能更自然,更不相关。
更新2018年12月-角色、扩展和静态接口成员
由于一些被解释为GitHub票据末尾的缺点,扩展的所有内容都没有到达C 8.0。因此,对改进设计进行了探索。在这里,Mads Torgensen解释了什么是角色和扩展,以及它们的区别:
Roles allow interfaces to be implemented on specific values of a given
type. Extensions allow interfaces to be implemented on all values of a
given type, within a specific region of code.
在两个用例中,可以在先前建议的一个部分看到它。扩展的新语法如下:
1 2 3 4 5 6 7 8 9 10 |
那么你就可以这样做了:
1 2 3 4 | foreach (byte b in 0x_3A_9E_F1_C5_DA_F7_30_16ul) { WriteLine($"{e.Current:X}"); } |
对于静态接口:
1 2 3 4 5 | public interface IMonoid<T> where T : IMonoid<T> { static T operator +(T t1, T t2); static T Zero { get; } } |
在
1 2 3 4 | public extension IntMonoid of int : IMonoid<int> { public static int Zero => 0; } |
更新(感谢@chaost指出此更新):
Mads Torgersen:"Extension everything didn’t make it into C# 8.0. It got"caught up", if you will, in a very exciting debate about the further future of the language, and now we want to make sure we don’t add it in a way that inhibits those future possibilities. Sometimes language design is a very long game!"
来源:https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0中的评论部分/
我不再计算这些年来我打开这个问题的次数,希望看到这个问题得以实现。
好吧,我们终于可以欢呼雀跃了!微软将在他们即将发布的C 8版本中对此进行介绍。
所以与其这样做…
1 2 3 4 5 6 7 | public static class IntExtensions { public static bool Even(this int value) { return value % 2 == 0; } } |
我们终于可以这样做了…
1 2 3 4 | public extension IntExtension extends int { public bool Even => this % 2 == 0; } |
资料来源:https://blog.ndepend.com/c-8-0-features-seven-future/
如@psyonity所述,您可以使用ConditionalWeakTable向现有对象添加属性。结合动态Expandoobject,您可以在几行中实现动态扩展属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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注释中有一个用法示例:
1 2 3 4 5 6 7 8 9 10 11 | 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 |
因为我最近需要这个,我在下面的文章中研究了答案的来源:
通过添加属性扩展类
并创建了一个更动态的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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)