【问题标题】:C# override/add methods/fields to an objectC# 覆盖/添加方法/字段到对象
【发布时间】:2013-05-11 16:00:35
【问题描述】:

我有一个为我创建和设置对象的库,然后我可以使用这些对象来做事。

假设我得到了“A”类的对象“a”

所以我想重写该特定对象中的方法,我不想更改其类的代码,因为这需要更改库。

在 Ruby 中,我可以使用单例类来做到这一点,例如:

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# 中做同样的事情?

更新

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

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

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

【问题讨论】:

  • 你要那个干什么?这没有意义。

标签: c# .net ruby inheritance subclass


【解决方案1】:

C# 不(直接)支持类型的运行时扩展,除了通过dynamic 机制。

最接近的选择可能是将ExpandoObjectdynamic 一起使用:

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

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

// Invoke
o.ExtraMethod();

话虽如此,这不是使用 C# 的典型方式。

【讨论】:

    【解决方案2】:

    在 C# 中使用 Delegates 可以在运行时“覆盖方法”(注意引号)

    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 如果您需要这种可扩展性,您应该在编写初始代码时考虑到它。现在没有办法了。
    【解决方案3】:

    实际上有一个丑陋的方法来做到这一点。

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

    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 扩展方法无法覆盖。
    • 当您无法覆盖方法时,扩展方法是添加行为的唯一方法。
    • @MikeSW 我同意它很丑
    • @MikeSW 仅此而已,扩展方法不会向该类添加行为。这是一个完全独立的方法,仅在语法上 似乎 属于该类,即使它不属于该类。这与在运行时实际改变类的公共 API 非常不同,就像 OP 的示例在 Ruby 中所做的那样,或者有时在 JavaScript 中完成。
    猜你喜欢
    • 1970-01-01
    • 2019-04-09
    • 2018-12-13
    • 1970-01-01
    • 2022-01-20
    • 2014-02-03
    • 2011-02-05
    • 1970-01-01
    • 2010-11-25
    相关资源
    最近更新 更多