SpaceMonkey
SpaceMonkey

Reputation: 4365

C# override/add methods/fields to an object

I have a library that creates and setups objects for me, then I can use those objects to do stuff.

Let's say I'm given object "a" of class "A"

So I want to override a method in that specific object, I don't want to change the code of its class because that would require changing the library.

In Ruby I can do that using Singleton Classes, like:

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

Now how can I do the same in C#?

Update

I ended up not using the answer I submitted because it's too ugly and it requires changing all private fields in the base class to either protect or public to make them exist in the derived class hence I can copy values from base to derived.

The way I ended up using is to pass a Type derived from the base class to the library and change the library so that it creates instances using:

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

Upvotes: 3

Views: 3445

Answers (3)

SpaceMonkey
SpaceMonkey

Reputation: 4365

There is an ugly way to do this actually.

I was thinking of using inheritance and downcasting, but downcasting is invalid. However, you can use reflection to achieve the same outcome.

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);
            }
        }

So I have overridden the methods within "a" and added new fields to it.

I realise this doesn't achieve the same as what you can do in Ruby, but at least I can override methods and add methods/fields that can be used inside the methods I'm overriding.

Upvotes: 0

Fede
Fede

Reputation: 44038

To "override a method" in runtime (notice the quotes) is possible in C# by using Delegates

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

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

    public Action OverridableMethod {get;set;}
}

usage:

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!"

Upvotes: 1

Reed Copsey
Reed Copsey

Reputation: 564413

C# doesn't (directly) support runtime extension of types, other than via the dynamic mechanisms.

The closest option would likely be to use ExpandoObject with dynamic:

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

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

// Invoke
o.ExtraMethod();

That being said, this is not a typical way to work with C#.

Upvotes: 4

Related Questions