C#有扩展属性吗?

C#有扩展属性吗?

例如,我可以向DateTimeFormatInfo添加一个名为ShortDateLongTimeFormat的扩展属性,该属性将返回ShortDatePattern + " " + LongTimePattern吗?

264860 次浏览

不,它们在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#版本(也许是所有?)已经将其视为提案冠军,但它还没有发布,最重要的是因为即使已经有了实现,他们也想让它正确。

但也许会的…有一天…

2022年更新:

这些功能似乎仍在讨论这里

此外,您可以使用变通方法

如本文章中所述,您可以使用TypeDescriptor功能在运行时将属性附加到对象实例。但是,它不使用标准属性的语法。
它与语法糖有点不同,添加了一种可能性来定义扩展属性,如
string Data(this MyClass instance)作为扩展方法
string GetData(this MyClass instance)的别名,因为它将数据存储到类中。

我希望C#很快就会提供一个功能齐全的扩展(属性和字段),但是在这一点上,只有时间才能证明。

并随时做出贡献,因为明天的软件将来自社区。

后处理办法

如果允许这样做,您还可以在编译后使用张贴锋利莫诺.塞西尔等工具(或类似的代码/IL重写工具)在程序集中的类上动态添加属性。

但是,正如开发人员在以上讨论中解释的那样,重写代码不会让编译器知道你的意图,因此它可能会在优化结果代码方面失败。作为一种语言功能,预计结果会更好。

一点历史

C#7工作列表中有一个扩展成员项,所以我认为它可能会在不久的将来得到支持。扩展属性的当前状态可以在相关项目下的Github上找到。

然而,还有一个更有前途的主题是“扩展一切”,重点是属性和静态类甚至字段。

更新:2016年8月

正如dotnet团队发布的什么是新的C#7.0Mads 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 Personreturn _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的相关性更小。

2018年12月更新-角色、扩展和静态接口成员

扩展一切没有进入C#8.0,因为在github工单的结尾解释了一些缺点。因此,有一个改进设计的探索。这里,Mads Torgensen解释了角色和扩展是什么以及它们如何不同:

角色允许在给定的特定值上实现接口类型。扩展允许在a的所有值上实现接口给定类型,在特定的代码区域内。

它可以在两个用例中看到之前的提案的拆分。新的扩展语法如下所示:

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

因为我最近需要这个,我查看了答案的来源:

c#通过添加属性来扩展类

并创建了一个更动态的版本:

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中使用它和一个哈奇的条件WeakTable(https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4

正如@Husonity提到的,您可以使用条件WeakTable向现有对象添加属性。结合动态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

更新(感谢@张超指出此更新):

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/