C# 创建新的 T()

216
您可以看到我尝试使用以下代码(但失败了):
protected T GetObject()
{
    return new T();
}

任何帮助都将不胜感激。
编辑:
上下文如下。我正在尝试创建一个自定义控制器类,供所有控制器继承,并具有标准化方法。因此,在这种情况下,我需要创建控制器类型的新对象实例。因此,在编写时,它大致如下:
public class GenericController<T> : Controller
{
    ...

    protected T GetObject()
    {
        return (T)Activator.CreateInstance(ObjectType);
    }        

    public ActionResult Create()
    {
        var obj = GetObject()

        return View(obj);
    }

所以我决定在这里进行反思。 我同意,考虑到问题的初始陈述,标记为正确答案的最合适答案是使用new()约束的答案。我已经修复了这个问题。


29
不,我并不明白你试图做什么却失败了。我只看到一段代码,它可能是一个可工作程序的一部分,但缺乏上下文、错误信息和解释。 - Ben Voigt
19
啊,选错答案的时候我很讨厌! - David Heffernan
8个回答

514

请查看新的约束

该链接介绍了关于新的约束的信息。
public class MyClass<T> where T : new()
{
    protected T GetObject()
    {
        return new T();
    }
}

T 可能是一个没有默认构造函数的类:在这种情况下,new T() 将是无效的语句。 new() 约束表示 T 必须有一个默认构造函数,这使得 new T() 合法。

您可以将相同的约束应用于通用方法:

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

如果您需要传递参数:

protected T GetObject(params object[] args)
{
    return (T)Activator.CreateInstance(typeof(T), args);
}

2
谢谢,伙计 - 我很高兴今天学到了这个知识。考虑到我的方法的上下文,我选择了反射解决方案。干杯! - Hanshan
8
@nulliusinverba - 嗯...如果你在问题中展示你的方法的背景,那就太好了。 - Alex Aza
1
@nulliusinverba - 你在问题中没有表明你需要参数。 - Alex Aza
1
@Alex - 当我读到他的问题时,我以为他不想要参数:S 不过还是给你点赞 :) - Phill
可以使用类似于new(parameters)约束这样的东西吗? - Louis Rhys
显示剩余3条评论

121

31

另一种方法是使用反射:

protected T GetObject<T>(Type[] signature, object[] args)
{
    return (T)typeof(T).GetConstructor(signature).Invoke(args);
}

谢谢,伙计 - 我选择了这个解决方案,考虑到方法的上下文。 - Hanshan
23
顺便提一下,这也可以写成Activator.CreateInstance(typeof(T), signature, args); 更多细节请查看http://msdn.microsoft.com/en-us/library/4b0ww1we.aspx。 - Chris Baxter
@Calgary Coder:type[]签名有什么用处,你可以直接使用参数调用CreateInstance,而不必显式指定签名。在两种情况下,如果找不到匹配的构造函数,都会引发MissingMethodException异常。 - Boris B.
4
即使这个答案对你最有效,显然它并不是社区中最好的答案。寻找这个问题答案的人实际上是在寻找下面的答案。 - Trap
那么,这个上下文具体是什么呢?请将其添加到原来的问题中。 - James
@Boris - 只有在想要明确声明参数类型(即具有重载)时才需要签名。您是正确的,通常不需要签名,因为默认情况下它会找到“最佳匹配”。 - Chris Baxter

21

新的限制条件是不错的,但如果你需要T也是值类型,请使用以下代码:

protected T GetObject() {
    if (typeof(T).IsValueType || typeof(T) == typeof(string)) {
        return default(T);
    } else {
       return (T)Activator.CreateInstance(typeof(T));
    }
}

19

仅为完整性考虑,这里最好的解决方案通常是要求一个工厂函数参数:

T GetObject<T>(Func<T> factory)
{  return factory(); }

然后将其命名为类似这样的名称:

string s = GetObject(() => "result");

你可以使用它来要求或利用可用的参数,如果需要的话。

7

由于这是标记为C# 4的。使用开源框架ImpromptuIntereface,它将使用dlr调用构造函数时,如果您的构造函数有参数,则比Activator快得多,并且当没有参数时几乎不会慢。然而,其主要优点在于它将正确处理带有C# 4.0可选参数的构造函数,而Activator无法做到这一点。

protected T GetObject(params object[] args)
{
    return (T)Impromptu.InvokeConstructor(typeof(T), args);
}

6
为了得到这个,我尝试了以下代码:
  protected T GetObject<T>()
    {
        T obj = default(T);
        obj =Activator.CreateInstance<T>();
        return obj ;
    }

0

我尝试过

if (typeof(T).IsValueType || typeof(T) == typeof(string))
                        return default(T);
                    return Activator.CreateInstance<T>();

但是在运行时,它会在 return Activator.CreateInstance<T>(); 处抛出此错误。

System.MissingMethodException: 'No parameterless constructor defined for this object.'

被使用的T类型包括intList<int>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接