创建泛型类型的实例?

Bor*_*ens 212 .net c# generics

如果BaseFruit有一个接受一个的构造函数,int weight我可以用这样的泛型方法实例化一块水果吗?

public void AddFruit<T>()where T: BaseFruit{
    BaseFruit fruit = new T(weight); /*new Apple(150);*/
    fruit.Enlist(fruitManager);
}
Run Code Online (Sandbox Code Playgroud)

注释后面添加了一个示例.如果我给出BaseFruit一个无参数构造函数然后通过成员变量填充所有内容,我似乎只能这样做.在我的真实代码中(不是关于水果),这是不切实际的.

-Update-
所以它似乎无法以任何方式通过约束来解决.从答案中有三种候选解决方案:

  • 工厂模式
  • 反射
  • 活化剂

我倾向于认为反射是最不干净的,但我不能在其他两个之间做出决定.

mea*_*ode 306

另外一个更简单的例子:

return (T)Activator.CreateInstance(typeof(T), new object[] { weight });
Run Code Online (Sandbox Code Playgroud)

请注意,在T上使用new()约束只是为了让编译器在编译时检查公共无参数构造函数,用于创建类型的实际代码是Activator类.

您需要确保自己关于现有的特定构造函数,并且这种要求可能是代码异味(或者更确切地说,您应该在c#的当前版本中尝试避免这种情况).

  • 不需要使用`new object [] {weight}`.`CreateInstance`是用params声明的,`public static object CreateInstance(Type type,params object [] args)`,所以你可以做`return(T)Activator.CreateInstance(typeof(T),weight);`.如果有多个参数,请将它们作为单独的参数传递.只有当你已经有一个构造的可枚举参数时,你才需要将它转换为`object []`并将其传递给`CreateInstance`. (17认同)
  • 这个很好.还有一个CreateInstance <T>()过程,但对于某些rason的参数没有重载. (3认同)
  • 这将有我读过的性能问题.请使用已编译的lambda.https://vagifabilov.wordpress.com/2010/04/02/dont-use-activator-createinstance-or-constructorinfo-invoke-use-compiled-lambda-expressions/ (2认同)

Jon*_*eet 90

您不能使用任何参数化构造函数.如果您有" where T : new()"约束,则可以使用无参数构造函数.

这是一种痛苦,但这就是生活:(

这是我想用"静态接口"解决的问题之一.然后,您可以将T约束为包含静态方法,运算符和构造函数,然后调用它们.

  • 至少你可以做这样的限制--Java总是令我失望. (2认同)

Ada*_*son 55

是; 改变你的位置:

where T:BaseFruit, new()
Run Code Online (Sandbox Code Playgroud)

但是,这仅适用于无参数构造函数.您必须有其他一些设置属性的方法(设置属性本身或类似的东西).


小智 24

最简单的解决方案 Activator.CreateInstance<T>()


Jar*_*Par 17

正如Jon指出的那样,这是限制非参数构造函数的生命.然而,不同的解决方案是使用工厂模式.这很容易受到限制

interface IFruitFactory<T> where T : BaseFruit {
  T Create(int weight);
}

public void AddFruit<T>( IFruitFactory<T> factory ) where T: BaseFruit {    
  BaseFruit fruit = factory.Create(weight); /*new Apple(150);*/    
  fruit.Enlist(fruitManager);
}
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用功能方法.通过工厂方法.

public void AddFruit<T>(Func<int,T> factoryDel) where T : BaseFruit { 
  BaseFruit fruit = factoryDel(weight); /* new Apple(150); */
  fruit.Enlist(fruitManager);
}
Run Code Online (Sandbox Code Playgroud)

  • 好的建议 - 虽然如果你不小心你可能最终在Java DOM API的地狱,工厂丰富:( (2认同)

mmm*_*mmm 11

你可以使用反射:

public void AddFruit<T>()where T: BaseFruit
{
  ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) });
  if (constructor == null)
  {
    throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor");
  }
  BaseFruit fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit;
  fruit.Enlist(fruitManager);
}
Run Code Online (Sandbox Code Playgroud)

编辑:添加构造函数== null检查.

编辑:使用缓存的更快的变体:

public void AddFruit<T>()where T: BaseFruit
{
  var constructor = FruitCompany<T>.constructor;
  if (constructor == null)
  {
    throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor");
  }
  var fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit;
  fruit.Enlist(fruitManager);
}
private static class FruitCompany<T>
{
  public static readonly ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) });
}
Run Code Online (Sandbox Code Playgroud)


Rob*_*len 6

作为 user1471935 建议的补充:

要使用带有一个或多个参数的构造函数实例化泛型类,您现在可以使用 Activator 类。

T instance = Activator.CreateInstance(typeof(T), new object[] {...}) 
Run Code Online (Sandbox Code Playgroud)

对象列表是您要提供的参数。根据微软的说法

CreateInstance [...] 使用与指定参数最匹配的构造函数创建指定类型的实例。

还有一个通用版本的 CreateInstance ( CreateInstance<T>()) 但它也不允许您提供构造函数参数。