LB.*_*LB. 395 .net c# generics new-operator
我正在尝试在添加到列表时通过其构造函数创建类型为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.
}
...
}
Run Code Online (Sandbox Code Playgroud)
Jar*_*Par 400
要在函数中创建泛型类型的实例,必须使用"new"标志约束它.
public static string GetAllItems<T>(...) where T : new()
Run Code Online (Sandbox Code Playgroud)
但是,只有在想要调用没有参数的构造函数时才会有效.不是这里的情况.相反,您必须提供另一个参数,该参数允许基于参数创建对象.最简单的是一个功能.
public static string GetAllItems<T>(..., Func<ListItem,T> del) {
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(del(listItem));
}
...
}
Run Code Online (Sandbox Code Playgroud)
然后你就可以这样称呼它
GetAllItems<Foo>(..., l => new Foo(l));
Run Code Online (Sandbox Code Playgroud)
use*_*107 319
在.Net 3.5中,您可以使用激活器类:
(T)Activator.CreateInstance(typeof(T), args)
Run Code Online (Sandbox Code Playgroud)
Jam*_*nes 51
既然没有人费心去发表"反思"答案(我个人认为这是最好的答案),这里有:
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);
}
...
}
Run Code Online (Sandbox Code Playgroud)
编辑:由于.NET 3.5的Activator.CreateInstance,这个答案已被弃用,但它在旧的.NET版本中仍然有用.
Tim*_*ner 30
对象初始化器
如果带有参数的构造函数除了设置属性之外没有做任何事情,你可以在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
}
...
}
Run Code Online (Sandbox Code Playgroud)
使用它,您始终可以将任何构造函数逻辑放在默认(空)构造函数中.
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
}
...
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果执行速度是最高优先级而另一个选项可供您维护,则Activator.CreateInstance可能会有一些性能开销,您可能希望避免这些开销.
Gar*_*ler 18
这不适用于您的情况.您只能指定它具有空构造函数的约束:
public static string GetAllItems<T>(...) where T: new()
Run Code Online (Sandbox Code Playgroud)
你可以做的是通过定义这个接口来使用属性注入:
public interface ITakesAListItem
{
ListItem Item { set; }
}
Run Code Online (Sandbox Code Playgroud)
然后你可以改变你的方法:
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 });
}
...
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是Func
JaredPar描述的方法.
Jer*_*gen 17
很老的问题,但新答案;-)
ExpressionTree版本 :( 我认为紧固和最干净的解决方案)
就像Welly Tambunan所说,"我们也可以使用表达式树来构建对象"
这将为给定的类型/参数生成"构造函数"(函数).它返回一个委托并接受参数类型作为对象数组.
这里是:
// this delegate is just, so you don't have to pass an object array. _(params)_
public delegate object ConstructorDelegate(params object[] args);
public static ConstructorDelegate CreateConstructor(Type type, params Type[] parameters)
{
// Get the constructor info for these parameters
var constructorInfo = type.GetConstructor(parameters);
// define a object[] parameter
var paramExpr = Expression.Parameter(typeof(Object[]));
// To feed the constructor with the right parameters, we need to generate an array
// of parameters that will be read from the initialize object array argument.
var constructorParameters = parameters.Select((paramType, index) =>
// convert the object[index] to the right constructor parameter type.
Expression.Convert(
// read a value from the object[index]
Expression.ArrayAccess(
paramExpr,
Expression.Constant(index)),
paramType)).ToArray();
// just call the constructor.
var body = Expression.New(constructorInfo, constructorParameters);
var constructor = Expression.Lambda<ConstructorDelegate>(body, paramExpr);
return constructor.Compile();
}
Run Code Online (Sandbox Code Playgroud)
示例MyClass:
public class MyClass
{
public int TestInt { get; private set; }
public string TestString { get; private set; }
public MyClass(int testInt, string testString)
{
TestInt = testInt;
TestString = testString;
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
// you should cache this 'constructor'
var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string));
// Call the `myConstructor` fucntion to create a new instance.
var myObject = myConstructor(10, "test message");
Run Code Online (Sandbox Code Playgroud)
另一个例子:将类型作为数组传递
var type = typeof(MyClass);
var args = new Type[] { typeof(int), typeof(string) };
// you should cache this 'constructor'
var myConstructor = CreateConstructor(type, args);
// Call the `myConstructor` fucntion to create a new instance.
var myObject = myConstructor(10, "test message");
Run Code Online (Sandbox Code Playgroud)
DebugView的表达式
.Lambda #Lambda1<TestExpressionConstructor.MainWindow+ConstructorDelegate>(System.Object[] $var1) {
.New TestExpressionConstructor.MainWindow+MyClass(
(System.Int32)$var1[0],
(System.String)$var1[1])
}
Run Code Online (Sandbox Code Playgroud)
这相当于生成的代码:
public object myConstructor(object[] var1)
{
return new MyClass(
(System.Int32)var1[0],
(System.String)var1[1]);
}
Run Code Online (Sandbox Code Playgroud)
小的缺点
所有valuetypes参数在像对象数组一样传递时都会被装箱.
简单的性能测试:
private void TestActivator()
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1024 * 1024 * 10; i++)
{
var myObject = Activator.CreateInstance(typeof(MyClass), 10, "test message");
}
sw.Stop();
Trace.WriteLine("Activator: " + sw.Elapsed);
}
private void TestReflection()
{
var constructorInfo = typeof(MyClass).GetConstructor(new[] { typeof(int), typeof(string) });
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1024 * 1024 * 10; i++)
{
var myObject = constructorInfo.Invoke(new object[] { 10, "test message" });
}
sw.Stop();
Trace.WriteLine("Reflection: " + sw.Elapsed);
}
private void TestExpression()
{
var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string));
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1024 * 1024 * 10; i++)
{
var myObject = myConstructor(10, "test message");
}
sw.Stop();
Trace.WriteLine("Expression: " + sw.Elapsed);
}
TestActivator();
TestReflection();
TestExpression();
Run Code Online (Sandbox Code Playgroud)
结果:
Activator: 00:00:13.8210732
Reflection: 00:00:05.2986945
Expression: 00:00:00.6681696
Run Code Online (Sandbox Code Playgroud)
使用Expressions
是+/- 快8倍比调用ConstructorInfo
和+/- 快20倍比使用Activator
小智 7
你需要添加T:new(),让编译器知道T保证提供默认构造函数.
public static string GetAllItems<T>(...) where T: new()
Run Code Online (Sandbox Code Playgroud)
小智 7
如果您只想使用构造函数参数初始化成员字段或属性,则在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.
}
...
}
Run Code Online (Sandbox Code Playgroud)
这与加里·特勒勒所说的相同,但我想提出一个附加说明.
当然,除了设置字段值之外,您还可以使用属性技巧来执行更多操作.属性"set()"可以触发设置其相关字段所需的任何处理以及对象本身的任何其他需要,包括检查是否在使用对象之前进行完全初始化,模拟完整的构造(是的,这是一个丑陋的解决方法,但它克服了M $的新()限制).
我不能保证它是一个计划的洞还是偶然的副作用,但它确实有效.
MS人员如何为语言添加新功能并且似乎没有进行完整的副作用分析,这非常有趣.整个通用的东西都是这个的好证据......
我发现我收到错误"在创建类型参数T的实例时无法提供参数"所以我需要这样做:
var x = Activator.CreateInstance(typeof(T), args) as T;
Run Code Online (Sandbox Code Playgroud)
如果您可以访问要使用的类,则可以使用我使用的这种方法。
创建一个具有替代创建者的接口:
public interface ICreatable1Param
{
void PopulateInstance(object Param);
}
Run Code Online (Sandbox Code Playgroud)
使用空的创建者创建类并实现此方法:
public class MyClass : ICreatable1Param
{
public MyClass() { //do something or nothing }
public void PopulateInstance (object Param)
{
//populate the class here
}
}
Run Code Online (Sandbox Code Playgroud)
现在使用您的通用方法:
public void MyMethod<T>(...) where T : ICreatable1Param, new()
{
//do stuff
T newT = new T();
T.PopulateInstance(Param);
}
Run Code Online (Sandbox Code Playgroud)
如果您没有访问权限,请包装目标类:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
191947 次 |
最近记录: |