我试图在添加到列表时通过其构造函数创建一个T类型的新对象。

我得到一个编译错误:错误消息是:

'T':创建变量实例时不能提供参数

但是我的类确实有构造函数参数!我该怎么做呢?

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T(listItem)); // error here.
   } 
   ...
}

当前回答

这在你的情况下行不通。你只能指定一个构造函数为空的约束:

public static string GetAllItems<T>(...) where T: new()

你可以通过定义这个接口来使用属性注入:

public interface ITakesAListItem
{
   ListItem Item { set; }
}

然后你可以改变你的方法如下:

public static string GetAllItems<T>(...) where T : ITakesAListItem, new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T() { Item = listItem });
   } 
   ...
}

另一种替代方法是JaredPar描述的Func方法。

其他回答

我有时使用类似于使用属性注入的答案的方法,但使代码更简洁。 它没有一个带有一组属性的基类/接口,只包含一个(虚拟的)Initialize()方法,充当“穷人的构造函数”。 然后,您可以让每个类像构造函数一样处理自己的初始化,这也为处理继承链增加了一种方便的方式。

If经常发现自己处于这样的情况:我希望链中的每个类初始化其唯一属性,然后调用其父类的initialize()方法,该方法反过来初始化父类的唯一属性,以此类推。当具有不同的类,但具有相似的层次结构时,这尤其有用,例如映射到DTO:s的业务对象。

使用普通Dictionary进行初始化的示例:

void Main()
{
    var values = new Dictionary<string, int> { { "BaseValue", 1 }, { "DerivedValue", 2 } };

    Console.WriteLine(CreateObject<Base>(values).ToString());

    Console.WriteLine(CreateObject<Derived>(values).ToString());
}

public T CreateObject<T>(IDictionary<string, int> values)
    where T : Base, new()
{
    var obj = new T();
    obj.Initialize(values);
    return obj;
}

public class Base
{
    public int BaseValue { get; set; }

    public virtual void Initialize(IDictionary<string, int> values)
    {
        BaseValue = values["BaseValue"];
    }

    public override string ToString()
    {
        return "BaseValue = " + BaseValue;
    }
}

public class Derived : Base
{
    public int DerivedValue { get; set; }

    public override void Initialize(IDictionary<string, int> values)
    {
        base.Initialize(values);
        DerivedValue = values["DerivedValue"];
    }

    public override string ToString()
    {       
        return base.ToString() + ", DerivedValue = " + DerivedValue;
    }
}

对象初始化器

如果你的带形参的构造函数除了设置属性之外没有做任何事情,你可以在c# 3中使用对象初始化器而不是调用构造函数来做这件事(这是不可能的,正如前面提到的):

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T() { YourPropertyName = listItem } ); // Now using object initializer
   } 
   ...
}

使用它,您也可以将任何构造函数逻辑放在默认(空)构造函数中。

Activator.CreateInstance ()

或者,你可以像这样调用Activator.CreateInstance():

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
        object[] args = new object[] { listItem };
        tabListItems.Add((T)Activator.CreateInstance(typeof(T), args)); // Now using Activator.CreateInstance
   } 
   ...
}

注意Activator。CreateInstance可能有一些性能开销,如果执行速度是最优先考虑的,并且另一个选项对您来说是可维护的,那么您可能希望避免这些开销。

使用c# 11中的静态抽象接口方法特性,可以使用工厂模式构建一个变通方案。例如:

public interface IFactory<TSelf> where TSelf : IFactory<TSelf> {
  static abstract TSelf New(int i, string s);
}

public struct Foo : IFactory<Foo> {

  public static Foo New(int i, string s) {
    return new Foo(i, s);
  }

  public readonly int I;
  public readonly string S;

  public Foo(int i, string s) {
    I = i;
    S = s;
  }
}

public static class Maker {
  public static T Make<T>(int i, string s) where T : IFactory<T> {
    return T.New(i, s);
  }
}

这种方法的一个限制是它只能用于您拥有的类,因为您需要在目标类上实现特定的工厂接口。

如果你可以访问将要使用的类,你可以使用我使用的这种方法。

创建一个具有替代创建者的接口:

public interface ICreatable1Param
{
    void PopulateInstance(object Param);
}

让你的类有一个空的创建器并实现这个方法:

public class MyClass : ICreatable1Param
{
    public MyClass() { //do something or nothing }
    public void PopulateInstance (object Param)
    {
        //populate the class here
    }
}

现在使用泛型方法:

public void MyMethod<T>(...) where T : ICreatable1Param, new()
{
    //do stuff
    T newT = new T();
    T.PopulateInstance(Param);
}

如果您没有访问权限,则包装目标类:

public class MyClass : ICreatable1Param
{
    public WrappedClass WrappedInstance {get; private set; }
    public MyClass() { //do something or nothing }
    public void PopulateInstance (object Param)
    {
        WrappedInstance = new WrappedClass(Param);
    }
}

在。net 3.5和之后,你可以使用activator类:

(T)Activator.CreateInstance(typeof(T), args)