在C#中重写/添加对象的方法/字段

3
我有一个可以为我创建和设置对象的库,然后我可以使用这些对象来做事情。 假设我得到了类“A”的对象“a”。 所以我想覆盖那个特定对象中的一个方法,我不想改变它类的代码,因为那需要改变这个库。 在Ruby中,我可以使用Singleton Classes来实现这个,就像这样:
class FirstClass
    def test
        p "test"
    end
end

o = FirstClass.new

class << o
    def test
        p "overridden"
    end
    def extraMethod
        p "ok"
    end
end

o.test # prints "overridden"
o.extraMethod

现在我该如何使用C#实现同样的功能呢?

更新

最终我没有使用我提交的答案,因为它太丑陋了,而且需要将基类中的所有私有字段更改为保护或公共字段,以便它们存在于派生类中,因此我可以从基类复制值到派生类。

我最终采用的方法是将一个派生自基类的类型传递给库,并更改库,以便使用以下方式创建实例:

(A)Activator.CreateInstance(mytype, arguments);

你为什么要那个?这没有意义。 - Federico Berasategui
3个回答

4

C#不支持类型的运行时扩展,除非通过动态机制。

最接近的选择可能是使用带有动态的ExpandoObject:

dynamic o = new ExpandoObject();
o.a = 10;

o.ExtraMethod = new Action( () => Console.WriteLine("ok") );

// Invoke
o.ExtraMethod();

说实话,这并不是使用C#的典型方法。

1

在C#中,通过使用委托,可以在运行时“覆盖方法”(请注意引号)。

public class SomeClass
{
    public SomeClass()
    {
        //set the default
        OverridableMethod = () => MessageBox.Show("Default!");
    }

    public void StandardMethod()
    {
        //Call it.
        OverridableMethod();
    }

    public Action OverridableMethod {get;set;}
}

使用方法:

var some1 = new SomeClass();
some1.StandardMethod(); //Shows "Default!"  
some1.OverridableMethod(); //Shows "Default!"

var some2 = new SomeClass {OverridableMethod = () => MessageBox.Show("Override!!")};
some2.StandardMethod(); //Shows "Override!"  
some2.OverridableMethod(); //Shows "Override!"

@SpaceMonkey 如果你需要这种可扩展性,那么在编写初始代码时应该考虑到它。现在没有其他办法了。 - Federico Berasategui

0

实际上有一种丑陋的方法可以做到这一点。

我曾考虑使用继承和向下转型,但向下转型是无效的。 然而,您可以使用反射来实现相同的结果。

static void Main()
        {
            A a = new A();
            a.FieldA = 999; // change the object
            a.test(); // "This is A and FieldA is 999"

            //B b = (B)a; // => Error: Invalid you cannot downcast !

            // using reflection we turn "a" into an instance of B that is a copy of the old "a"
            a = new B(a);
            a.test(); // This will call the method in B: "Calling the new method"  "This is B and FieldA is 999 and FieldB is 10"

            // a.newMethod(); => Error cannot access this method because "a" is declared as an instance of A (even though it's actually of B now)

            B b = (B)a; // Now downcasting works fine (because A is an instance of B actually)

            b.newMethod(); // works as expected: "This is B and FieldA is 999 and FieldB is 10"
        }



        class A
        {
            public int FieldA;

            public A()
            {
                FieldA = 100;
            }

            virtual public void test()
            {
                Console.WriteLine("This is A and FieldA is {0}", FieldA);
            }
        }

        class B : A
        {
            int FieldB;
            public B(A a)
            {
                // copy all fields
                foreach (FieldInfo pi in typeof(A).GetFields())
                    GetType().GetField(pi.Name).SetValue
                       (this, pi.GetValue(a));

                // add this field:
                FieldB = 10;
            }

            // We can override methods
            override public void test()
            {
                Console.WriteLine("Calling the new method:");
                newMethod();
            }

            // Add a new method but it will only be visible after casting A to B
            public void newMethod()
            {
                Console.WriteLine("This is B, FieldA is {0} and FieldB is {1}", FieldA, FieldB);
            }
        }

所以我已经重载了“a”中的方法并添加了新字段。

我意识到这并不能像Ruby中那样实现,但至少我可以重载方法并添加可在我正在重载的方法中使用的方法/字段。


非常丑陋和无用(我认为)的做事方式。如果基类没有要重写的虚方法,则唯一的方法是使用扩展方法。如果您可以修改基类,请直接将这些方法作为空实现的虚拟方法。 - MikeSW
1
@MikeSW 扩展方法无法被覆盖。 - SpaceMonkey
扩展方法是在无法重写方法时添加行为的唯一方法。 - MikeSW
@MikeSW 我同意,它看起来很丑。 - SpaceMonkey
@MikeSW 但问题就在于,扩展方法并没有向该类添加行为。它是一种完全独立的方法,仅从语法上看起来属于该类,实际上并不属于。这与像Ruby中OP的示例在运行时实际更改类的公共API非常不同,或者有时在JavaScript中完成的操作也非常不同。 - Servy
我没有说它实际上修改了基类定义,但扩展方法是在.NET中添加行为到一个不能被修改(覆盖)或继承的基类的方式。C#不是Ruby也不是一种动态语言。你最多只能使用轻量级代码生成在运行时生成新的方法,并将动态方法附加到现有类型上,但你必须有非常充分的理由这样做,因为这并不是一个轻松的事情。 - MikeSW

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