使用反射在C#中创建没有默认构造函数的类型实例


97

以下面的类为例:

class Sometype
{
    int someValue;

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

然后,我想使用反射创建此类型的实例:

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

通常这将起作用,但是由于SomeType尚未定义无参数构造函数,因此对的调用Activator.CreateInstance将引发类型异常,MissingMethodException并显示消息“未为此对象定义无参数构造函数。 ”是否还有其他方法可以创建此类型的实例?将无参数的构造函数添加到我的所有类中有点麻烦。


2
FormatterServices.GetUninitializedObject不允许创建未初始化的字符串。您可能会得到例外:System.ArgumentException: Uninitialized Strings cannot be created.请记住这一点。
Bartosz Pierzchlewicz

感谢您的注意,但是我已经在分别处理字符串和基本类型。
艾斯蒂娜(Aistina)2010年

Answers:


142

我最初在此处发布了此答案,但是这里是重印版,因为这不是完全相同的问题,但是答案相同:

FormatterServices.GetUninitializedObject()将创建实例而不调用构造函数。我通过使用Reflector并深入研究了一些核心.Net序列化类来找到此类。

我使用下面的示例代码对其进行了测试,看起来效果很好:

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

太棒了,看起来正是我所需要的。我假设未初始化意味着所有内存都将设置为零?(类似于如何实例化结构)
Aistina

每种类型的默认值都是默认值。因此对象将为null,int 0等。我确实认为会发生任何类级别的初始化,但不会运行任何构造函数。
杰森·杰克逊

14
@JSBangs,这很糟糕,您正在敲定一个完全合法的答案。您的评论和其他答案实际上并未解决所提出的问题。如果您觉得自己有更好的答案,请提供答案。但是我提供的答案强调了如何像其他序列化类使用此代码一样使用文档化的类。
杰森·杰克逊

21
@JSBangs FormatterServices(msdn.microsoft.com/en-us/library/…)没有记录在案。
Autodidact 2010年


72

使用CreateInstance方法的以下重载:

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

使用与指定参数最匹配的构造函数创建指定类型的实例。

请参阅:http//msdn.microsoft.com/en-us/library/wcxyzt4d.aspx


1
此解决方案简化了问题。如果我不知道我的类型,而是说“只需在此Type变量中创建Type的对象”怎么办?
kamii

23

当我性能进行基准测试时(T)FormatterServices.GetUninitializedObject(typeof(T))它的速度较慢。同时,尽管编译表达式仅适用于具有默认构造函数的类型,但它们可以大大提高速度。我采取了一种混合方法:

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

这意味着create表达式将被有效地缓存,并且仅在首次加载类型时才产生代价。也会以有效的方式处理值类型。

称它为:

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

请注意,这(T)FormatterServices.GetUninitializedObject(t)对于字符串将失败。因此,对字符串进行了特殊处理以返回空字符串。


1
奇怪的是,只看一行人的代码就能节省一天的时间。谢谢你,先生!出于性能原因,我来到了您的岗位,并且成功完成了窍门:) FormatterServices和Activator类与编译后的表达式相比表现不佳,可惜人们到处都发现Activators。
jmodrak

@nawfal关于您对字符串的特殊处理,我知道如果没有这种特殊处理,字符串处理将失败,但是我只想知道:它将对所有其他类型有效吗?
Sнаđошƒаӽ

不幸的是,@Sнаđошƒаӽ。给定的示例是准系统,.NET具有许多不同类型的类型。例如,考虑一下,如果您传递委托类型,您将如何为它提供实例?否则,如果构造函数抛出该怎么办?有很多不同的处理方式。从那以后,我就回答了这个更新,以处理图书馆中的更多场景。它暂未在任何地方发布。
nawfal

4

好的答案,但在点网紧凑框架上无法使用。这是可以在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);
    }
}

1
这就是我称为非默认构造函数的方式。我不确定我是否要创建一个对象而根本不调用任何构造函数。
罗里·麦克劳德

2
如果要编写自定义序列化程序,则可能想创建一个不调用构造函数的对象。
Autodidact

1
是的,这是此问题用于的确切用例场景:)
Aistina 2010年

1
@Aistina也许您可以将此信息添加到问题中?大多数人都反对在不调用对象的情况下创建对象,并会花一些时间与您争论,但是您的用例确实证明了这一点,因此,我认为它与问题本身息息相关。
julealgon
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.