C#通过添加属性扩展类

21
在C#中,是否可以通过添加属性来扩展类,而不仅仅是添加函数。例如:我依赖于一个标准的DLL库,但供应商不想修改它。在整个代码中,我已经广泛使用了DataCell类,并且现在才意识到需要向其中添加一个额外的属性。创建一个继承自该类的新扩展类似乎行不通,因为需要进行大量重写。 DataCell [元数据]
public class DataCell : Message
{
public int Field1;
public int Field2;
public DataCell()
{
 ..
} 
..
}

基本上我想要在这个类中添加一个公共整数 Flags; 以便我现在可以不重写任何内容,执行 (new DataCell).Flags = 0x10;


1
你不能拥有扩展属性,但你可以继承一个类并添加属性。 - Sayse
在这种情况下,“extend”的意思是什么?你有源代码吗?还是想使用扩展属性(你不能)?或者从DataCell继承可以吗? - Tim
所谓扩展,是指我在使用供应商的.dll库中大量使用了这个类。创建像ExtDataCell:DataCell这样的类不是一个选项,因为我基本上需要重写.dll库。因此,我希望能够以某种方式实现这样的操作,使得来自供应商.dll的DataCell可以拥有属性Flags,而它目前并没有(而无需修改供应商dll)。 - Vans S
3个回答

19

首先,你应该重新考虑你的方法。但是如果一切都失败了,以下是你可以勉强给密封类添加属性的方法:

using System;
using System.Runtime.CompilerServices;

namespace DataCellExtender
{

    #region sample 3rd party class
    public class DataCell
    {
        public int Field1;
        public int Field2;
    }
    #endregion

    public static class DataCellExtension
    {
        //ConditionalWeakTable is available in .NET 4.0+
        //if you use an older .NET, you have to create your own CWT implementation (good luck with that!)
        static readonly ConditionalWeakTable<DataCell, IntObject> Flags = new ConditionalWeakTable<DataCell, IntObject>();

        public static int GetFlags(this DataCell dataCell) { return Flags.GetOrCreateValue(dataCell).Value; }

        public static void SetFlags(this DataCell dataCell, int newFlags) { Flags.GetOrCreateValue(dataCell).Value = newFlags; }

        class IntObject
        {
            public int Value;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var dc = new DataCell();
            dc.SetFlags(42);
            var flags = dc.GetFlags();
            Console.WriteLine(flags);
        }
    }
}
请您不要这样做,除非确实必须这样。如果有更干净的解决方案而您选择了这种有点繁琐的方法,未来维护此代码的人可能会对您表示不满。

2
这绝对是一个有趣的解决方案。我会先探索更干净的选项,但至少值得考虑一下。 - Jon Skeet
1
@JonSkeet啊,是的,我忘了明确说明这有多糟糕,不应该这样做。我会编辑一下的。 - dss539
哇,这个问题已经有1万次的浏览量了。我接受了这个作为最佳答案,因为它似乎是我当时所需要的。不过性能方面还不确定。 - Vans S
+100的要点是,如果您将一个对象的引用作为键存储在“ConditionalWeakTable”中,则如果所有对象实例在其他地方被删除,则该条目将被删除。这使我们能够将元数据与密封类相关联,使其看起来像元数据是类的一部分,但不必处理在其被删除后删除该元数据的复杂性。 https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 ,更多信息请参见 https://dev59.com/i2Ml5IYBdhLWcg3wXF9f - Zack Morris
1
@ZackMorris 是的。你可以代表其他对象保留一些数据。你可以通过扩展方法访问数据。你可以通过删除相关对象来“神奇地”擦除数据。这很hacky,但它能够工作。 :) 我不认为Belding先生会赞同。 - dss539
谢谢,稍作修改后它对我有用了(对于泛型类型(Node<>等),通过添加dynamic关键字): private static readonly ConditionalWeakTable<dynamic, IntObject> Heights = new ConditionalWeakTable<dynamic, IntObject>(); - vCillusion

9

您可以通过继承一个类并仅向其中添加字段/属性来扩展它(虽然我会不建议使用公共字段,如您的示例所示)。但是,除非其他代码 使用 您的新类,否则这些字段将不存在于创建的对象中。例如,如果其他代码具有:

DataCell cell = new DataCell();

那么它就不会有你的Field1Field2字段。

如果基类的每个实例都应该具有这些字段,您最好想办法更改基类而不是扩展它。

如果您想知道是否可以以添加扩展方法的方式添加“扩展字段”(例如public static void Foo(this DataCell cell)),那么不,这是不可能的。


问题在于这个类被供应商提供的另一个类广泛使用。因此,通过继承来扩展该类需要扩展一堆其他类并进行大量重写。好的,谢谢。 - Vans S
@VansS:对我来说仍然不清楚基类是否应该具有额外字段,或者谁拥有基类。但是你不能向你不拥有的类中添加字段。 - Jon Skeet

5

有两种方法可以向现有类添加属性:

  1. 添加部分类,但这对您不起作用,因为部分类应该在同一个程序集中。

  2. 继承此类到另一个类中,据我所知这将是更好的解决方案。

但不能像扩展方法一样使用扩展属性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接