C# 创建其构造函数需要参数的泛型类型的实例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/731452/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:58:51  来源:igfitidea点击:

Create instance of generic type whose constructor requires a parameter?

c#.netgenerics

提问by Boris Callens

If BaseFruithas a constructor that accepts an int weight, can I instantiate a piece of fruit in a generic method like this?

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

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

An example is added behind comments. It seems I can only do this if I give BaseFruita parameterless constructor and then fill in everything through member variables. In my real code (not about fruit) this is rather impractical.

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

-Update-
So it seems it can't be solved by constraints in any way then. From the answers there are three candidate solutions:

-更新-
所以它似乎不能以任何方式通过约束来解决。从答案中可以看出三个候选解决方案:

  • Factory Pattern
  • Reflection
  • Activator
  • 工厂模式
  • 反射
  • 活化剂

I tend to think reflection is the least clean one, but I can't decide between the other two.

我倾向于认为反射是最不干净的一种,但我无法在其他两种之间做出决定。

采纳答案by meandmycode

Additionally a simpler example:

另外一个更简单的例子:

return (T)Activator.CreateInstance(typeof(T), new object[] { weight });

Note that using the new() constraint on T is only to make the compiler check for a public parameterless constructor at compile time, the actual code used to create the type is the Activator class.

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

You will need to ensure yourself regarding the specific constructor existing, and this kind of requirement may be a code smell (or rather something you should just try to avoid in the current version on c#).

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

回答by Adam Robinson

Yes; change your where to be:

是的; 改变你的位置:

where T:BaseFruit, new()

However, this only works with parameterlessconstructors. You'll have to have some other means of setting your property (setting the property itself or something similar).

但是,这只适用于无参数构造函数。你必须有一些其他的方式来设置你的属性(设置属性本身或类似的东西)。

回答by Jon Skeet

You can't use any parameterised constructor. You can use a parameterless constructor if you have a "where T : new()" constraint.

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

It's a pain, but such is life :(

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

This is one of the things I'd like to address with "static interfaces". You'd then be able to constrain T to include static methods, operators and constructors, and then call them.

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

回答by JaredPar

As Jon pointed out this is life for constraining a non-parameterless constructor. However a different solution is to use a factory pattern. This is easily constrainable

正如 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);
}

Yet another option is to use a functional approach. Pass in a factory method.

另一种选择是使用函数式方法。传入一个工厂方法。

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

回答by mmmmmmmm

You can do by using reflection:

您可以使用反射来完成:

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);
}

EDIT:Added constructor == null check.

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

EDIT:A faster variant using a cache:

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

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) });
}

回答by user1471935

Most simple solution Activator.CreateInstance<T>()

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

回答by farshid

Recently I came across a very similar problem. Just wanted to share our solution with you all. I wanted to I created an instance of a Car<CarA>from a json object using which had an enum:

最近我遇到了一个非常相似的问题。只是想与大家分享我们的解决方案。我想Car<CarA>从一个 json 对象创建一个 a 的实例,使用它有一个枚举:

Dictionary<MyEnum, Type> mapper = new Dictionary<MyEnum, Type>();

mapper.Add(1, typeof(CarA));
mapper.Add(2, typeof(BarB)); 

public class Car<T> where T : class
{       
    public T Detail { get; set; }
    public Car(T data)
    {
       Detail = data;
    }
}
public class CarA
{  
    public int PropA { get; set; }
    public CarA(){}
}
public class CarB
{
    public int PropB { get; set; }
    public CarB(){}
}

var jsonObj = {"Type":"1","PropA":"10"}
MyEnum t = GetTypeOfCar(jsonObj);
Type objectT = mapper[t]
Type genericType = typeof(Car<>);
Type carTypeWithGenerics = genericType.MakeGenericType(objectT);
Activator.CreateInstance(carTypeWithGenerics , new Object[] { JsonConvert.DeserializeObject(jsonObj, objectT) });

回答by cskwg

It is still possible, with high performance, by doing the following:

通过执行以下操作,仍然可以实现高性能:

    //
    public List<R> GetAllItems<R>() where R : IBaseRO, new() {
        var list = new List<R>();
        using ( var wl = new ReaderLock<T>( this ) ) {
            foreach ( var bo in this.items ) {
                T t = bo.Value.Data as T;
                R r = new R();
                r.Initialize( t );
                list.Add( r );
            }
        }
        return list;
    }

and

    //
///<summary>Base class for read-only objects</summary>
public partial interface IBaseRO  {
    void Initialize( IDTO dto );
    void Initialize( object value );
}

The relevant classes then have to derive from this interface and initialize accordingly. Please note, that in my case, this code is part of a surrounding class, which already has <T> as generic parameter. R, in my case, also is a read-only class. IMO, the public availability of Initialize() functions has no negative effect on the immutability. The user of this class could put another object in, but this would not modify the underlying collection.

然后相关的类必须从这个接口派生并相应地初始化。请注意,就我而言,此代码是周围类的一部分,该类已经将 <T> 作为泛型参数。R,就我而言,也是一个只读类。IMO,Initialize() 函数的公开可用性对不变性没有负面影响。此类的用户可以放入另一个对象,但这不会修改底层集合。

回答by Rob Vermeulen

As an addition to user1471935's suggestion:

作为 user1471935 建议的补充:

To instantiate a generic class by using a constructor with one or more parameters, you can now use the Activator class.

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

T instance = Activator.CreateInstance(typeof(T), new object[] {...}) 

The list of objects are the parameters you want to supply. According to Microsoft:

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

CreateInstance [...] creates an instance of the specified type using the constructor that best matches the specified parameters.

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

There's also a generic version of CreateInstance (CreateInstance<T>()) but that one also does not allow you to supply constructor parameters.

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

回答by Diocleziano Carletti

I created this method:

我创建了这个方法:

public static V ConvertParentObjToChildObj<T,V> (T obj) where V : new()
{
    Type typeT = typeof(T);
    PropertyInfo[] propertiesT = typeT.GetProperties();
    V newV = new V();
    foreach (var propT in propertiesT)
    {
        var nomePropT = propT.Name;
        var valuePropT = propT.GetValue(obj, null);

        Type typeV = typeof(V);
        PropertyInfo[] propertiesV = typeV.GetProperties();
        foreach (var propV in propertiesV)
        {
            var nomePropV = propV.Name;
            if(nomePropT == nomePropV)
            {
                propV.SetValue(newV, valuePropT);
                break;
            }
        }
    }
    return newV;
}

I use that in this way:

我以这种方式使用它:

public class A 
{
    public int PROP1 {get; set;}
}

public class B : A
{
    public int PROP2 {get; set;}
}

Code:

代码:

A instanceA = new A();
instanceA.PROP1 = 1;

B instanceB = new B();
instanceB = ConvertParentObjToChildObj<A,B>(instanceA);