Creating instance of type without default constructor in C# using reflection
Asked Answered
T

4

108

Take the following class as an example:

class Sometype
{
    int someValue;

    public Sometype(int someValue)
    {
        this.someValue = someValue;
    }
}

I then want to create an instance of this type using reflection:

Type t = typeof(Sometype);
object o = Activator.CreateInstance(t);

Normally this will work, however because SomeType has not defined a parameterless constructor, the call to Activator.CreateInstance will throw an exception of type MissingMethodException with the message "No parameterless constructor defined for this object." Is there an alternative way to still create an instance of this type? It'd be kinda sucky to add parameterless constructors to all my classes.

Trample answered 24/12, 2008 at 1:34 Comment(2)
FormatterServices.GetUninitializedObject not allow to create uninitialized string. You may get exception: System.ArgumentException: Uninitialized Strings cannot be created. Please keep this in mind.Extortioner
Thanks for the heads up, but I'm already handling strings and basic types separately.Trample
M
155

I originally posted this answer here, but here is a reprint since this isn't the exact same question but has the same answer:

FormatterServices.GetUninitializedObject() will create an instance without calling a constructor. I found this class by using Reflector and digging through some of the core .Net serialization classes.

I tested it using the sample code below and it looks like it works great:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;

namespace NoConstructorThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass)); //does not call ctor
            myClass.One = 1;
            Console.WriteLine(myClass.One); //write "1"
            Console.ReadKey();
        }
    }

    public class MyClass
    {
        public MyClass()
        {
            Console.WriteLine("MyClass ctor called.");
        }

        public int One
        {
            get;
            set;
        }
    }
}

With .NET 8 right around the corner and the obsoletion of the FormatterService

You can use RuntimeHelpers.GetUninitializedObject, which is the same method that FormatterService.GetUninitializedObject was calling under the hood in the first place.

Mercurate answered 24/12, 2008 at 1:44 Comment(8)
Awesome, looks like that's exactly what I need. I assume uninitialized means all its memory will be set to zeros? (Similar to how structs are instantiated)Trample
Whatever the default value is for each type will be the default. So objects will be null, ints 0, etc. I do think that any class-level initialization occurs, but no constructor is run.Mercurate
@JSBangs, That sucks you are dinging a perfectly legitimate answer. Your comment and the other answer doesn't actually address the question asked. If you feel like you have a better answer then provide one. But the answer I provided highlight how to use a documented class in the same way other serialization classes use this code.Mercurate
@JSBangs FormatterServices (msdn.microsoft.com/en-us/library/…) is not undocumented.Cosmo
using GetUninitializedObject seems to depend on your specific purpose: Because the new instance of the object is initialized to zero and no constructors are run, the object might not represent a state that is regarded as valid by that object. The current method should only be used for deserialization when the user intends to immediately populate all fields. It does not create an uninitialized string, since creating an empty instance of an immutable type serves no purpose.Livelihood
@Cal - Yup, this is a very powerful function and should be used with caution. I have only ever used this function in one place in my code in the last 3 years since I posted this.Mercurate
How can I go about calling all the default (and base for inherited) constructors at a later time?Liqueur
Wow. I've been struggling a bit finding a solution like this. I'm surprised now. Feels like a 'hack'Poling
H
73

Use this overload of the CreateInstance method:

public static Object CreateInstance(
    Type type,
    params Object[] args
)

Creates an instance of the specified type using the constructor that best matches the specified parameters.

See: http://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx

Hulen answered 24/12, 2008 at 1:44 Comment(2)
This solution oversimplifies the problem. What if I don't know my type and I'm saying "just create an object of the Type in this Type variable"?Elastic
@Elastic if neither the type nor the parameters it expects are known then what can you do with that class?Libelee
G
25

When I benchmarked performance of (T)FormatterServices.GetUninitializedObject(typeof(T)) it was slower. At the same time compiled expressions would give you great speed improvements though they work only for types with default constructor. I took a hybrid approach:

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

This means the create expression is effectively cached and incurs penalty only the first time the type is loaded. Will handle value types too in an efficient manner.

Call it:

MyType me = New<MyType>.Instance();

Note that (T)FormatterServices.GetUninitializedObject(t) will fail for string. Hence special handling for string is in place to return empty string.

Gunfire answered 23/4, 2013 at 6:29 Comment(3)
It is strange how a look at one line of someone's code can save a day. Thank you, sir! Performance reasons took me to your post and the trick is done :) FormatterServices and Activator classes are underperforming compared to compiled expressions, what a pity one finds Activators all around the place.Emeryemesis
@Gunfire Regarding your special handling for string, I know it would fail for string without this special handling, but I just want to know: will it work for all other types?Intelsat
@Sнаđошƒаӽ unfortunately no. The given example is barebones and .NET has many different types of types. For e.g. consider, if you pass a delegate type how will you give you it an instance? Or else if constructor throws what can you do about it? Many different ways to handle it. I had since answering this updated to handle lot more scenarios in my library. It isnt published anywhere for now.Gunfire
C
4

Good answers but unusable on the dot net compact framework. Here is a solution that will work on CF.Net...

class Test
{
    int _myInt;

    public Test(int myInt)
    {
        _myInt = myInt;
    }

    public override string ToString()
    {
        return "My int = " + _myInt.ToString();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var ctor = typeof(Test).GetConstructor(new Type[] { typeof(int) });
        var obj = ctor.Invoke(new object[] { 10 });
        Console.WriteLine(obj);
    }
}
Cosmo answered 22/1, 2009 at 11:48 Comment(4)
This is the way I'd call a non-default constructor. I'm not sure I'd ever want to create an object without calling any constructor at all.Hexone
You may want to create an object without calling constructors if you are writing custom serializers.Cosmo
Yup, that's the exact use case scenario this question was for :)Trample
@Trample Perhaps you could add this information to the question? Most people would be against creating objects without calling their ctors and would take the time to argue with you about that, but your use case actually justifies it, so I think it is very relevant to the question itself.Coxalgia

© 2022 - 2024 — McMap. All rights reserved.