【问题标题】:Create a new instance of T without the new constraint创建没有新约束的 T 的新实例
【发布时间】:2013-06-26 06:43:47
【问题描述】:

如果要创建泛型的新实例,则需要定义 new constraint,如下所示:

public T SomeMethod<T>() where T : new()
{
    return new T();
}

是否可以使用反射创建一个没有新约束的 T 实例,像这样(包含伪代码):

public T SomeMethod<T>()
{
    if (T has a default constructor)
    {
        return a new instance of T;
    }
    else
    {
        return Factory<T>.CreateNew();
    }
}

【问题讨论】:

  • 你的a new instance of T = `new T()'
  • 注意——你不能通过if检查来满足约束——即使你检查它一个构造函数,你也不能调用一个要求 T : new() 约束的方法,因为编译器不会相信你

标签: c# .net generics reflection instantiation


【解决方案1】:

为此使用Activator.CreateInstance()。有关如何使用此方法的更多信息,请参阅http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx。基本上,你要做的是:

var obj = (T)Activator.CreateInstance(typeof(T));

你可以通过GetConstructors()方法验证它是否有默认构造函数:

var constructors = typeof(T).GetConstructors();

如果你找到一个零参数的构造函数,你可以使用Activator.CreateInstance 方法。否则,您使用Factory&lt;T&gt;.CreateNew() 方法。

编辑:

直接判断是否存在不带参数的构造函数,可以使用如下检查:

if (typeof(T).GetConstructor(Type.EmptyTypes) != null)
{
    // ...

【讨论】:

  • 就在GetConstructors() 上——typeof(T).GetConstructor(Type.EmptyTypes) 可能更具体——返回null 或非null
【解决方案2】:

带有where T : new() 约束的通用方法通过调用Activator.CreateInstance&lt;T&gt;() 来实现new T() 调用。该方法的一个有趣之处在于它不包含约束,因此如果您愿意将检查推迟到运行时,只需使用:

public T SomeMethod<T>() {
    return Activator.CreateInstance<T>();
}

这将要么完全按照return new T() 所做的,或者将引发有意义的异常。因为它同时处理成功和失败的情况,所以做任何额外的检查并没有真正的好处,除非你想做一些模糊的事情,比如根本不使用构造函数(可以这样做)。

【讨论】:

  • 您说:根本不使用构造函数(可以这样做)。您能否说明如何做到这一点,我的意思是在不使用构造函数的情况下创建实例?我希望我的方法的用户在执行record.SomeColumn 时不会出现空异常,其中record 是数据库表的列的集合,SomeColumn 是一列,并且不存在记录。请注意,我也没有(也不能拥有)T : new()
  • @Sнаđошƒаӽ System.Runtime.Serialization.FormatterServices.GetUninitializedObject - 请注意,以这种方式创建的对象可能具有完全不确定的行为;典型的用例是在创建 POCO/DTO 记录时用于序列化或 ORM 库
  • 非常感谢。请再提供一个帮助。可以这样创建一个对象,然后使用反射手动初始化它吗?正如我已经提到的,当我在 DB 中没有记录时(我正在使用 Dapper,顺便说一句),我希望返回的对象是非空的,但它的所有属性都有各自的默认值。另外,您能否告诉我在哪里可以了解完全非确定性的行为
  • @Sнаđошƒаӽ 非确定性行为我的意思很简单:构造函数通常会做的任何事情(比如配置私有状态):不会发生。至于“可以吗” - 这是大量的上下文并且取决于正在创建的类型。对于 POCO,它通常会起作用。如果您尝试使用 SqlConnection 或类似的东西:预计它会失败
  • 我的是 POCO,所以我很安全 ;-) 非常感谢。
【解决方案3】:

Marc Gravell 回答的最后一句话(强调我的)

...除非您想做一些晦涩的事情,例如不使用构造函数 完全没有(可以做到

并且来自他的this comment,有一种替代方法可以实现相同的目标。我不太清楚哪个是better performance wise,但老实说,我不在乎。如果您想知道,请点击该链接。那里作者展示了一个混合的方式。

使用System.Runtime.Serialization.FormatterServices.GetUninitializedObject()(旧的 MSDN 文档link)。

public static T SomeMethod<T>()
{
    if (typeof(T) == typeof(string))
    {
        return default(T);
    }

    return (T)FormatterServices.GetUninitializedObject(typeof(T));
}

请注意对string 的特别注意。没有那个GetUninitializedObject() 将会失败。这是来自 MSDN 关于在字符串上使用这种方法的内容:

它不会创建一个未初始化的字符串,因为创建一个空的 不可变类型的实例毫无用处。

它没有说明失败的原因,而是更多地说明了应该失败的原因。紧随其后的是另一个 Note,如下所示:

您不能使用 GetUninitializedObject 方法创建实例 派生自 ContextBoundObject 类的类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-24
    • 2011-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多