我试图在添加到列表时通过其构造函数创建一个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.
}
...
}
如果你只是想用构造函数参数初始化一个成员字段或属性,在c# >= 3中你可以很容易地做到:
public static string GetAllItems<T>(...) where T : InterfaceOrBaseClass, new()
{
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(new T{ BaseMemberItem = listItem }); // No error, BaseMemberItem owns to InterfaceOrBaseClass.
}
...
}
这和Garry Shutler说的一样,但我想补充一点。
当然,您可以使用属性技巧来做更多的事情,而不仅仅是设置字段值。
属性“set()”可以触发设置其相关字段所需的任何处理,以及对对象本身的任何其他需求,包括在使用对象之前检查是否要进行完整的初始化,模拟完整的构造(是的,这是一个丑陋的解决方法,但它克服了M$ new()的限制)。
我不能确定这是一个计划好的漏洞还是一个意外的副作用,但它确实有效。
有趣的是,微软的人给语言添加了新功能,却似乎没有做一个完整的副作用分析。
整个通用的东西就是一个很好的证据…
如果你只是想用构造函数参数初始化一个成员字段或属性,在c# >= 3中你可以很容易地做到:
public static string GetAllItems<T>(...) where T : InterfaceOrBaseClass, new()
{
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(new T{ BaseMemberItem = listItem }); // No error, BaseMemberItem owns to InterfaceOrBaseClass.
}
...
}
这和Garry Shutler说的一样,但我想补充一点。
当然,您可以使用属性技巧来做更多的事情,而不仅仅是设置字段值。
属性“set()”可以触发设置其相关字段所需的任何处理,以及对对象本身的任何其他需求,包括在使用对象之前检查是否要进行完整的初始化,模拟完整的构造(是的,这是一个丑陋的解决方法,但它克服了M$ new()的限制)。
我不能确定这是一个计划好的漏洞还是一个意外的副作用,但它确实有效。
有趣的是,微软的人给语言添加了新功能,却似乎没有做一个完整的副作用分析。
整个通用的东西就是一个很好的证据…
为了在函数中创建泛型类型的实例,必须使用"new"标志约束它。
public static string GetAllItems<T>(...) where T : new()
但是,只有当你想调用没有参数的构造函数时,这才会起作用。这里不是这样。相反,您必须提供另一个参数,允许基于参数创建对象。最简单的是函数。
public static string GetAllItems<T>(..., Func<ListItem,T> del) {
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(del(listItem));
}
...
}
然后你可以这样称呼它
GetAllItems<Foo>(..., l => new Foo(l));
如果你可以访问将要使用的类,你可以使用我使用的这种方法。
创建一个具有替代创建者的接口:
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);
}
}
对象初始化器
如果你的带形参的构造函数除了设置属性之外没有做任何事情,你可以在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可能有一些性能开销,如果执行速度是最优先考虑的,并且另一个选项对您来说是可维护的,那么您可能希望避免这些开销。