目前 Roslyn 编译器仍然不支持开箱即用...
到目前为止,扩展属性的价值还不足以包含在以前版本的 C# 标准中。 C# 7 和 C# 8.0 已将其视为提案冠军,但尚未发布,主要是因为即使已经有实现,他们也想实现从一开始。
但它会...
C# 7 work list 中有一个 extension members 项,因此在不久的将来可能会支持它。扩展属性的当前状态可以在Github under the related item找到。
不过,还有一个更有希望的话题,那就是"extend everything",特别关注属性和静态类甚至字段。
此外,您可以使用解决方法
正如article 中所指定的,您可以使用TypeDescriptor 功能在运行时将属性附加到对象实例。但是,它没有使用标准属性的语法。
它与仅仅增加了定义扩展属性的可能性有点不同,例如
string Data(this MyClass instance) 作为扩展方法
string GetData(this MyClass instance) 的别名,因为它将数据存储到类中。
我希望 C#7 将提供一个功能齐全的扩展一切(属性和字段),但是在这一点上,只有时间会证明一切。
请随时贡献自己的力量,因为明天的软件将来自社区。p>
更新:2016 年 8 月
正如 dotnet 团队发布的 what's new in C# 7.0 和来自 Mads Torgensen 的评论:
扩展属性:我们有一个(很棒的!)实习生来实现它们
夏天作为一个实验,以及其他类型的扩展
成员。我们仍然对此感兴趣,但这是一个很大的变化,我们
需要确信这是值得的。
似乎扩展属性和其他成员仍然是包含在 Roslyn 未来版本中的不错候选者,但可能不是 7.0 版本。
更新:2017 年 5 月
The extension members 已作为 extension everything issue 的副本关闭,extension everything issue 也已关闭。
主要讨论实际上是关于广义上的类型可扩展性。
该功能现已被 here as a proposal 跟踪,并已从 7.0 milestone 中删除。
更新: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 podcast 中提到的,扩展不会有任何状态(因此它不能将私有实例成员添加到类中),这意味着您将无法添加链接到实例的私有实例数据。为此调用的原因是它意味着管理内部字典并且可能很困难(内存管理等)。
为此,您仍然可以使用前面描述的TypeDescriptor/ConditionalWeakTable 技术并使用属性扩展,将其隐藏在一个不错的属性下。
语法仍可能发生变化,正如issue 所暗示的那样。例如,extends 可以替换为 for,有些人可能会觉得更自然,与 Java 的相关性更少。
2018 年 12 月更新 - 角色、扩展和静态界面成员
Extension Everything 没有进入 C# 8.0,因为在 GitHub ticket 的结尾处解释了一些缺点。因此,进行了改进设计的探索。 Here,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;
}