C#是否具有扩展属性?
例如,我是否可以向名为DateTimeFormatInfo
的{{1}}添加一个扩展属性,该属性将返回ShortDateLongTimeFormat
?
答案 0 :(得分:412)
它们在C#3.0中不存在,不会在4.0中添加。它位于C#的功能需求列表中,因此可能会在将来添加。
此时,您可以做的最好的是GetXXX样式扩展方法。
答案 1 :(得分:259)
不,它们不存在。
我知道C#团队正在考虑他们(或者至少是Eric Lippert) - 以及扩展构造函数和运算符(这些可能需要一段时间才能完成,但很酷......)但是,我没有看到任何证据表明他们将成为C#4的一部分。
编辑:他们没有出现在C#5中,截至2014年7月,它看起来也不会出现在C#6中。
Eric Lippert,微软C#编译团队的首席开发人员,截至2012年11月,在2009年10月发表了关于此事的博客:
答案 2 :(得分:206)
到目前为止,扩展属性的价值还不足以包含在以前版本的C#标准中。 C#7 和 C#8.0 已将此视为提案冠军,但尚未发布,最重要的是因为即使已经有实施,他们也想要实现从一开始就是。
extension members 中有 C# 7 work list 项,因此可能会在不久的将来支持该项。可以在Github under the related item上找到扩展属性的当前状态。
然而,有一个更有希望的主题是"extend everything",特别关注特性和静态类甚至字段。
如此article中所述,您可以使用TypeDescriptor
功能在运行时将属性附加到对象实例。但是,它没有使用标准属性的语法
它与语法糖略有不同,增加了定义像string Data(this MyClass instance)
这样的扩展属性作为扩展方法
string GetData(this MyClass instance)
的别名的可能性,因为它将数据存储到类中。
我希望C#7能提供全功能的扩展(属性和字段),但是在这一点上,只有时间会证明。
随着明天的软件来自社区,随时可以做出贡献。
更新:2016年8月
由于dotnet团队发布了what's new in C# 7.0以及Mads Torgensen的评论:
扩展属性:我们有一个(辉煌!)实习生实施它们 夏天作为实验,以及其他类型的延伸 成员。我们仍然对此感兴趣,但这是一个很大的变化,我们 需要确信这是值得的。
似乎扩展属性和其他成员仍然是未来发布的Roslyn中的优秀候选者,但可能不是7.0版本。
更新:2017年5月
The extension members已被关闭为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月 - 角色,扩展程序和静态界面成员
扩展所有没有进入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;
}
答案 3 :(得分:24)
更新(感谢@chaost指出此更新):
Mads Torgersen:&#34;扩展一切都没有进入C#8.0。如果你愿意的话,它会在一场关于语言未来发展的非常激动人心的辩论中“陷入困境”,现在我们要确保我们不会以抑制未来可能性的方式添加它。有时语言设计是一个非常长的游戏!&#34;
来源: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;
}
答案 4 :(得分:5)
正如@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
答案 5 :(得分:2)
因为我最近需要这个,所以我查看了答案的来源:
c# extend class by adding properties
并创建了一个更动态的版本:
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)