C# 有扩展属性吗?
例如,我可以将扩展属性添加到DateTimeFormatInfo
调用ShortDateLongTimeFormat
which 会返回ShortDatePattern + " " + LongTimePattern
吗?
C# 有扩展属性吗?
例如,我可以将扩展属性添加到DateTimeFormatInfo
调用ShortDateLongTimeFormat
which 会返回ShortDatePattern + " " + LongTimePattern
吗?
到目前为止,扩展属性的价值还不足以包含在以前版本的 C# 标准中。C# 7和C# 8.0已将此视为提案冠军,但尚未发布,最重要的是,即使已经有实现,他们也希望从一开始就做好。
C# 7 工作列表中有一个扩展成员项,因此它可能会在不久的将来得到支持。扩展属性的当前状态可以在Github 的相关项下找到。
然而,还有一个更有希望的主题是“扩展一切”,特别关注属性和静态类甚至字段。
如本文所述,您可以使用TypeDescriptor
在运行时将属性附加到对象实例的功能。但是,它没有使用标准属性的语法。
它与仅仅添加了定义扩展属性的可能性的语法糖有点不同,例如string Data(this MyClass instance)
作为扩展方法的别名,string GetData(this MyClass instance)
因为它将数据存储到类中。
我希望 C#7 将提供一个全功能的扩展一切(属性和字段),但是在这一点上,只有时间会证明一切。
并随时做出贡献,因为明天的软件将来自社区。
更新:2016 年 8 月
正如 dotnet 团队发布了 C# 7.0 中的新功能以及来自Mads 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 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 相关性较低的部分。
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;
}
不,它们在 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 月的博客中谈到了这一点:
更新(感谢@chaost指出此更新):
Mads Torgersen: “扩展一切并没有进入 C# 8.0。如果你愿意的话,它在关于语言未来的非常激动人心的辩论中被“赶上了”,现在我们要确保我们不会以一种抑制未来可能性的方式添加它。有时语言设计是一场漫长的游戏!
来源:https : //blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/ 中的评论部分
我停止计算多年来我打开这个问题的次数,希望看到这个问题得到实施。
好吧,终于我们都可以欢欣鼓舞了!Microsoft 将在其即将发布的 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;
}
正如@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
因为我最近需要这个,所以我查看了答案的来源:
并创建了一个更动态的版本:
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)