我试图在添加到列表时通过其构造函数创建一个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.
   } 
   ...
}

当前回答

我有时使用类似于使用属性注入的答案的方法,但使代码更简洁。 它没有一个带有一组属性的基类/接口,只包含一个(虚拟的)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;
    }
}

其他回答

我认为必须用where语句约束T,以只允许具有新构造函数的对象。

现在它接受任何东西,包括没有它的对象。

这有点麻烦,当我说有点麻烦时,我可能是指讨厌,但假设你可以为你的参数化类型提供一个空构造函数,那么:

public static T GetTInstance<T>() where T: new()
{
    var constructorTypeSignature = new Type[] {typeof (object)};
    var constructorParameters = new object[] {"Create a T"};
    return (T) new T().GetType().GetConstructor(constructorTypeSignature).Invoke(constructorParameters);
}

将有效地允许您从带有参数的参数化类型构造一个对象。在这种情况下,我假设我想要的构造函数有一个类型object的参数。我们使用约束允许的空构造函数创建T的虚拟实例,然后使用反射获取它的其他构造函数之一。

因为没有人愿意发表“反思”的答案(我个人认为这是最好的答案),下面是:

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       Type classType = typeof(T);
       ConstructorInfo classConstructor = classType.GetConstructor(new Type[] { listItem.GetType() });
       T classInstance = (T)classConstructor.Invoke(new object[] { listItem });

       tabListItems.Add(classInstance);
   } 
   ...
}

编辑:由于. net 3.5的Activator,这个答案已弃用。CreateInstance,但是它在旧的。net版本中仍然有用。

补充业绩资料

使用Jeroen van Langen的表达式方法(见上文)对数据库访问和数据模型类填充进行性能测试,并直接实例化数据模型类。

结论:该法表达速度快。

结果:

数据模型类的直接实例: 纪录:3558秒:1.2746019 实例的方法读取列表的类型参数: 记录:3558秒:0.4878858

表达式方法的代码示例:

var list = ReadList<DataModel>(SQLStatement, Connection);

方法ReadList: 注意:所有数据模型类都有一个参数类型为SQLDataReader的构造函数

public static List<pDataModel> ReadList<pDataModel>(string pSQLStatement, SqlConnection pConnection) where pDataModel : new()
    {
            // constructor of data model
            var lType = typeof(pDataModel);
            var lParameters = new Type[] { typeof(SqlDataReader) };
            var lDataModelConstructor = CreateConstructor(lType, lParameters);

            // read data
            List<pDataModel> lDataList = new List<pDataModel>();
            using (pConnection)
            {
                SqlCommand lCommand;
                lCommand = new SqlCommand(pSQLStatement, pConnection);
                pConnection.Open();
                SqlDataReader lReader = lCommand.ExecuteReader();


                if (lReader.HasRows)
                {
                    while (lReader.Read())
                    {
                        pDataModel lDataItem = (pDataModel)lDataModelConstructor(lReader);
                        lDataList.Add(lDataItem);
                    }
                }
                lReader.Close();
                pConnection.Close();
            }

            return lDataList;
    }

直接实例化的代码示例:

           List<DataModel> list= new List<DataModel>();
            using (connection)
            {
                SqlCommand command;
                command = new SqlCommand(SQLStatement, connection);
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {
                        list.Add(new DataModel(reader));
                    }
                }
                reader.Close();
                connection.Close();
            }

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

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方法。