C#有扩展属性吗?

时间:2009-03-06 14:30:45

标签: c# properties extension-methods

C#是否具有扩展属性?

例如,我是否可以向名为DateTimeFormatInfo的{​​{1}}添加一个扩展属性,该属性将返回ShortDateLongTimeFormat

6 个答案:

答案 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)

目前,Roslyn编译器仍然不支持开箱即用......

到目前为止,扩展属性的价值还不足以包含在以前版本的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;
}

来源:https://blog.ndepend.com/c-8-0-features-glimpse-future/

答案 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 &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

答案 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