ChatGPT解决这个技术问题 Extra ChatGPT

C# 有扩展属性吗?

C# 有扩展属性吗?

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

我想在 Nullable 上添加一个名为 IsNull 的扩展方法,它只会返回!有值。 .IsNull() 肯定不如 .IsNull 漂亮
我发现这对三元运算符 ? 很有用
我希望它模仿 Java 的 enum,它可以具有属性和方法。 C# 的 enum 不能有属性或方法,但您可以在它们上创建扩展方法。这个问题对我很有用,不应该关闭。
尽管正如许多人所说,目前还没有计划将其添加到语言中,但没有理由不能这样做。 F# 不仅具有扩展属性,而且对我来说也具有静态扩展,这一事实证明它至少是一个好主意。
应该有一个

F
Fab

目前,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) 之类的扩展属性定义为扩展方法的别名的可能性的语法糖有点不同
{ 4} 因为它将数据存储到类中。

我希望 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 月更新 - 角色、扩展和静态界面成员

Extension Everything 未能进入 C# 8.0,因为在此 GitHub ticket 结束时解释了一些缺点。因此,进行了改进设计的探索。 Here,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;
}

这是我在 StackExchange 上关注的最有用的答案之一。不断更新状态并让每个人都知道回到这个问题,提供讨论和历史的可靠链接。
你能保持最新状态真是太好了-谢谢
不幸的是,截至此评论,角色、扩展和静态接口成员仅标记为 C# 11 :(
“扩展所有内容”提案似乎已关闭,并且此票证中似乎有延续:github.com/dotnet/csharplang/issues/192
2022有什么更新吗?
J
JaredPar

不,它们在 C# 3.0 中不存在,也不会在 4.0 中添加。它在 C# 的功能需求列表中,因此可能会在将来添加。

此时你能做的最好的就是GetXXX风格的扩展方法。


与通用属性类似:您必须使用 'GetXXX<>' 语法。
好吧,我就是这么想的。 @Jay,是的,我也讨厌那个,呵呵。尤其是无法拥有通用索引器...叹息
链接到功能列表?
版本 6.0 和 7.0 怎么样?
A
Arsen Khachaturyan

不,它们不存在。

我知道 C# 团队曾经考虑过它们(或者至少 Eric Lippert 曾经考虑过)——以及扩展构造函数和运算符(这些可能需要一段时间才能让你明白,但是很酷......)但是,我没有没有看到任何证据表明它们将成为 C# 4 的一部分。

编辑:它们没有出现在 C# 5 中,截至 2014 年 7 月,它看起来也不会出现在 C# 6 中。

Eric Lippert,微软 C# 编译器团队的首席开发人员,截至 2012 年 11 月,他在 2009 年 10 月发表了关于此的博文:

为什么没有扩展属性? – 精彩的编码冒险


是的,他们仍然可以隐藏该字段 - 设置单个属性可能会在下面设置两个属性,反之亦然。 (想象一下具有普通 Size 属性和 Width/Height 扩展属性的东西,反之亦然。)我怀疑它们作为只读属性会更有用。
您不能绑定到扩展方法......能够添加自己的数据绑定属性在许多情况下可能会有所帮助。
@leppie - 我认为属性扩展的价值最有利于 bool 和 string 属性。最后去掉 ()更加更具可读性。我个人知道,我编写的扩展中至少有 90% 属于这两种类型。
为了举例说明为什么这很有用,我有一个 EFCF 模型。在某些类中,我具有用于返回格式化信息的只读属性:FullName = FirstName + LastNameShortName = FirstName + LastName[0]。我想添加更多这些属性,但我不想“弄脏”实际的类。在这种情况下,一个只读的扩展属性是完美的,因为我可以添加功能,保持主类干净,并且仍然可以在 UI 中公开我想要公开的信息。
@JonSkeet:你说得对,我最终通过创建自己的类来做我想做的事,然后包装所有相关的密封类方法和属性,然后提供 static implicit operator FileInfo(FileInfoEx fex) 它返回我包含的 FileInfo 对象。这有效地让我将 FileInfoEx 视为继承自 FileInfo,即使该类是密封的。
K
Korayem

更新(感谢 @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;
}

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


本周宣布了 C# 8.0 features,但遗憾的是我没有看到任何内容。
@MateoTorres-Ruiz 来自“Mads Torgersen”(C# 开发人员)的评论,回复有人询问(3 天前):“扩展一切都没有进入 C# 8.0。如果你愿意的话,它被“赶上了” ,在关于语言未来的非常激动人心的辩论中,现在我们要确保我们不会以抑制这些未来可能性的方式添加它。有时语言设计是一场非常漫长的游戏!”感觉不好..(在评论部分的 Korayems 链接上阅读此内容)
r
realbart

正如@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

最佳答案
@N73k 不,根本不是最佳答案。对于动态属性的性能开销是多少,您有丝毫线索吗?我们使用 C# 之类的语言是因为它们是强类型的,并且为了提高性能,它们会编译为本机代码。动态属性使所有这些无效。
问题是这是否可能,而不是您是否应该:-) 但是,我相信 ConditionalWeakTable 存在有效的用例,尽管它会影响性能。
C
Community

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

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)


对不起,虽然这看起来很彻底,但它与扩展属性无关,只是显示扩展方法。