zastrowm
zastrowm

Reputation: 8243

Is it possible to directly invoke a constructor on an already created instance?

I know that you can directly invoke the static constructor of a type and I know that you can create an instance of an object without calling the constructor, but is there a way to run the constructor of a type (.ctor) on an already existing instance?

I'm looking for something like:

public static void Reinitialize<T>(T instance)
{
  var initializer = typeof(T).GetHiddenConstructorThatDoesntNew(typeof(int), typeof(string));
  // call the constructor (int, string) on instance
  initializer.Invoke(instance, 7, "Bill");
}

I am aware that I should never really need to do this, I am more wondering if it is possible to re-invoke the constructor/initializer on an already created object.

Upvotes: 9

Views: 1746

Answers (2)

Brad Christie
Brad Christie

Reputation: 101614

var ctor = typeof(T).GetConstructor(new[]{ typeof(int), typeof(string) });
ctor.Invoke(instance, new Object[]{ 7, "Bill" });

You're looking for .GetConstructor.


More details

Given the following object:

public class Foo
{
    public Int32 a;
    public String b;
    public DateTime c;
    public Double d = 5318008;

    public Foo(Int32 a, String b)
    {
        this.a = a;
        this.b = b;
    }
}

Calling the ctor in the standard fashion results in the following:

var foo = new Foo(42, "Hello, world!") { new DateTime(2014, 11, 26) };
// foo {
//   a=42,
//   b="Hello, world!",
//   c=11/27/2014 12:00:00 AM
//   d = 5318008
// }

Now let' change d:

foo.d = 319009;
// foo {
//   a=42,
//   b="Hello, world!",
//   c=11/27/2014 12:00:00 AM
//   d=319009
// }

Calling the ctor again:

typeof(Foo)
  .GetConstructor(new[]{ typeof(Int32), typeof(String) }).
  .Invoke(foo, new Object[]{ 84, "World, Hello!" });
// foo {
//   a=84,
//   b="World, hello!",
//   c=11/27/2014 12:00:00 AM // only unchanged property
//   d=5318008
// }

Note that c remains unchanged. This is because a & b are defined in the ctor, and, although not obvious, so is d (properties assigned at object level are actually assigned when ctor is called).

Upvotes: 5

user743382
user743382

Reputation:

A ConstructorInfo object overloads MethodBase's Invoke method, but doesn't hide the inherited methods. You can simply make sure to pass the correct instance. Example:

using System;
using System.Reflection;

class A
{
    public int i;
    public A()
    {
        Console.WriteLine("A()");
    }
    private A(int j)
    {
        Console.WriteLine("A(" + j + "): i = " + i);
    }
}

static class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        a.i = 3;
        var constructor = a.GetType().GetConstructor(BindingFlags.Instance | BindingFlags.Public |BindingFlags.NonPublic, null, new Type[] { typeof(int) }, null);
        constructor.Invoke(a, new object[] { 3 });
    }
}

However, as this answer also shows, this doesn't reset any of the object's fields, and a constructor may be written with the assumption that all fields are left at their default values. If you've got such a constructor, you will definitely need to make sure that you either don't mess with any fields, or re-set them if you have. If you've got an uninitialised object for which no constructor has been called yet, that should be fine.

Upvotes: 9

Related Questions