将参数传递给模板化类型的 C# 泛型 new()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/840261/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Passing arguments to C# generic new() of templated type
提问by LB.
I'm trying to create a new object of type T via its constructor when adding to the list.
添加到列表时,我试图通过其构造函数创建一个 T 类型的新对象。
I'm getting a compile error: The error message is:
我收到编译错误:错误消息是:
'T': cannot provide arguments when creating an instance of a variable
'T':创建变量实例时不能提供参数
But my classes do have a constructor argument! How can I make this work?
但是我的类确实有一个构造函数参数!我怎样才能使这项工作?
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.
}
...
}
采纳答案by JaredPar
In order to create an instance of a generic type in a function you must constrain it with the "new" flag.
为了在函数中创建泛型类型的实例,您必须使用“new”标志对其进行约束。
public static string GetAllItems<T>(...) where T : new()
However that will only work when you want to call the constructor which has no parameters. Not the case here. Instead you'll have to provide another parameter which allows for the creation of object based on parameters. The easiest is a function.
但是,这仅在您想调用没有参数的构造函数时才有效。不是这里的情况。相反,您必须提供另一个允许基于参数创建对象的参数。最简单的是函数。
public static string GetAllItems<T>(..., Func<ListItem,T> del) {
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(del(listItem));
}
...
}
You can then call it like so
然后你可以这样称呼它
GetAllItems<Foo>(..., l => new Foo(l));
回答by Richard
You need to add where T: new() to let the compiler know that T is guaranteed to provide a default constructor.
您需要添加 where T: new() 以让编译器知道 T 保证提供默认构造函数。
public static string GetAllItems<T>(...) where T: new()
回答by klkitchens
I believe you have to constraint T with a where statement to only allow objects with a new constructor.
我相信您必须使用 where 语句约束 T 以仅允许具有新构造函数的对象。
RIght now it accepts anything including objects without it.
现在它接受任何东西,包括没有它的对象。
回答by Garry Shutler
This will not work in your situation. You can only specify the constraint that it has an empty constructor:
这在您的情况下不起作用。您只能指定它具有空构造函数的约束:
public static string GetAllItems<T>(...) where T: new()
What you could do is use property injection by defining this interface:
你可以做的是通过定义这个接口来使用属性注入:
public interface ITakesAListItem
{
ListItem Item { set; }
}
Then you could alter your method to be this:
然后你可以改变你的方法是这样的:
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 });
}
...
}
The other alternative is the Func
method described by JaredPar.
另一种选择是Func
JaredPar 描述的方法。
回答by fljx
If you simply want to initialise a member field or property with the constructor parameter, in C# >= 3 you can do it very easier:
如果您只想使用构造函数参数初始化成员字段或属性,在 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.
}
...
}
This is the same thing Garry Shutler said, but I'd like to put an aditional note.
这与 Garry Shutler 所说的相同,但我想补充一点。
Of course you can use a property trick to do more stuff than just setting a field value. A property "set()" can trigger any processing needed to setup its related fields and any other need for the object itself, including a check to see if a full initialization is to take place before the object is used, simulating a full contruction (yes, it is an ugly workaround, but it overcomes M$'s new() limitation).
当然,您可以使用属性技巧来做更多的事情,而不仅仅是设置字段值。属性“set()”可以触发设置其相关字段所需的任何处理以及对象本身的任何其他需要,包括检查是否在使用对象之前进行完全初始化,模拟完全构造(是的,这是一个丑陋的解决方法,但它克服了 M$ 的 new() 限制)。
I can't be assure if it is a planned hole or an accidental side effect, but it works.
我不能确定这是计划中的漏洞还是意外的副作用,但它确实有效。
It is very funny how MS people adds new features to the language and seems to not do a full side effects analysis. The entire generic thing is a good evidence of this...
MS 人们如何为语言添加新功能并且似乎没有进行完整的副作用分析,这非常有趣。整个通用的东西很好地证明了这一点......
回答by James Jones
Since nobody bothered to post the 'Reflection' answer (which I personally think is the best answer), here goes:
由于没有人费心发布“反思”答案(我个人认为这是最好的答案),这里是:
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);
}
...
}
Edit: This answer is deprecated due to .NET 3.5's Activator.CreateInstance, however it is still useful in older .NET versions.
编辑:由于 .NET 3.5 的 Activator.CreateInstance 已弃用此答案,但它在较旧的 .NET 版本中仍然有用。
回答by silasdavis
This is kind of mucky, and when I say kind of mucky I may mean revolting, but supposing you can furnish your parameterised type with an empty constructor, then:
这有点麻烦,当我说有点麻烦时,我的意思可能是令人反感,但假设您可以使用空构造函数提供参数化类型,那么:
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);
}
Will effectively allow you to construct an object from a parameterised type with an argument. In this case I am assuming the constructor I want has a single argument of type object
. We create a dummy instance of T using the constraint permitted empty constructor and then use reflection to get one of its other constructors.
将有效地允许您从带有参数的参数化类型构造对象。在这种情况下,我假设我想要的构造函数有一个类型为 的参数object
。我们使用约束允许的空构造函数创建 T 的虚拟实例,然后使用反射来获取其其他构造函数之一。
回答by user287107
in .Net 3.5 and after you could use the activator class:
在 .Net 3.5 和之后,您可以使用激活器类:
(T)Activator.CreateInstance(typeof(T), args)
回答by Tim Lehner
Object initializer
对象初始值设定项
If your constructor with the parameter isn't doing anything besides setting a property, you can do this in C# 3 or better using an object initializerrather than calling a constructor (which is impossible, as has been mentioned):
如果带有参数的构造函数除了设置属性之外没有做任何事情,您可以在 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
}
...
}
Using this, you can always put any constructor logic in the default (empty) constructor, too.
使用它,您也可以始终将任何构造函数逻辑放在默认(空)构造函数中。
Activator.CreateInstance()
Activator.CreateInstance()
Alternatively, you could call Activator.CreateInstance()like so:
或者,您可以像这样调用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
}
...
}
Note that Activator.CreateInstance can have some performance overheadthat you may want to avoid if execution speed is a top priority and another option is maintainable to you.
请注意 Activator.CreateInstance 可能会产生一些性能开销,如果执行速度是重中之重并且您可以维护另一个选项,则您可能希望避免这些开销。
回答by chris31389
I found that I was getting an error "cannot provide arguments when creating an instance of type parameter T" so I needed to do this:
我发现我收到一个错误“在创建类型参数 T 的实例时无法提供参数”,所以我需要这样做:
var x = Activator.CreateInstance(typeof(T), args) as T;