8

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.

Community
  • 1
  • 1
zastrowm
  • 8,017
  • 3
  • 43
  • 63
  • As a note, you mention `should never really need to do this`, but basically that's what a `Reset` method does. – Mephy Nov 26 '14 at 21:48
  • @Mephy my original use case was an object pool, and I was wondering if it was possible to code the classes normally but still use an object pool (to perhaps cut down on new allocations). Though probably not the best approach it was an interesting thought puzzle. – zastrowm Nov 26 '14 at 21:58
  • 1
    About the first of your links: it wouldn't be a valid answer to that question, I think, but depending on what you need it for, [`RuntimeHelpers.RunClassConstructor`](http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.runclassconstructor%28v=vs.110%29.aspx) may be a better alternative. –  Nov 26 '14 at 21:59
  • 1
    Quote: "CLS Rule 22: An object constructor shall not be called except as part of the creation of an object, and an object shall not be initialized twice." Violating this rule has pretty unguessable consequences. – Hans Passant Nov 26 '14 at 22:01
  • @HansPassant It means the code is not CLS-compliant, but nothing more than that. CLS compliance is a goal for some, and not a goal for others. Both are legitimate. –  Nov 26 '14 at 22:07
  • 1
    For a reference for the comment that @HansPassant made, see [Standard ECMA-335](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf) page 46 (page 72 in the pdf) in case anyone else is interested. – zastrowm Nov 26 '14 at 22:09
  • 1
    @HansPassant No, really, that's exactly what it means. CLS-compliant means adhering to the CLS rules. Note: the CLS rules are stricter than the CLI rules, many programs only adhere to the CLI rules, and those programs are guaranteed to work. –  Nov 26 '14 at 22:11

2 Answers2

8

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.

  • I had imagined that this might work, but had ruled it out because the object returned from `Invoke` did not equal the original `(ret != a)`. I didn't consider that in this case, `Invoke` wouldn't have to return anything, and thus why should `ret` equal the instance! – zastrowm Nov 26 '14 at 21:56
  • 1
    If you want to reset fields you can do that using reflection too in a manual way. If any of this is perf-critical you can emit a method that does all of this using expression trees. – usr Nov 26 '14 at 22:00
  • 1
    @MackieChan The return type of instance constructors is `void` if you look at the IL. – Jeppe Stig Nielsen Nov 26 '14 at 22:03
5
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).

Brad Christie
  • 100,477
  • 16
  • 156
  • 200