创buildgenericstypes的实例?
如果BaseFruit
有一个构造函数接受一个int weight
,我可以用这样的一个通用方法实例化一块水果吗?
public void AddFruit<T>()where T: BaseFruit{ BaseFruit fruit = new T(weight); /*new Apple(150);*/ fruit.Enlist(fruitManager); }
在评论后面添加一个示例。 看来我只能这样做,如果我给BaseFruit
一个无参数的构造函数,然后通过成员variables填写所有内容。 在我真正的代码(而不是水果)这是不切实际的。
-Update-
所以看来它不能以任何方式通过约束来解决。 答案有三个候选scheme:
- 工厂模式
- reflection
- 活化剂
我倾向于认为反思是最不干净的一个,但是我不能在另外两个之间做出决定。
另外一个更简单的例子:
return (T)Activator.CreateInstance(typeof(T), new object[] { weight });
请注意,在T上使用new()约束只是为了让编译器在编译时检查一个公共无参数的构造函数,实际用于创buildtypes的代码是Activator类。
你需要确保自己关于具体的构造函数存在,这种需求可能是一个代码异味(或者你应该尽量避免在C#上的当前版本)。
你不能使用任何参数化的构造函数。 如果你有一个“ where T : new()
”约束,你可以使用无参数的构造函数。
这是一个痛苦,但这是生活:(
这是我想用“静态接口”来解决的事情之一。 然后你可以限制T包含静态方法,运算符和构造函数,然后调用它们。
是; 改变你的位置:
where T:BaseFruit, new()
但是,这只适用于无参数的构造函数。 你必须有其他方法来设置你的财产(设置财产本身或类似的东西)。
正如Jon指出的那样,这是限制非参数构造函数的生命。 然而,不同的解决scheme是使用工厂模式。 这很容易约束
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); }
另一个select是使用function性方法。 通过工厂方法。
public void AddFruit<T>(Func<int,T> factoryDel) where T : BaseFruit { BaseFruit fruit = factoryDel(weight); /* new Apple(150); */ fruit.Enlist(fruitManager); }
最简单的解决schemeActivator.CreateInstance<T>()
你可以用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); }
编辑:添加构造函数== null检查。
编辑:使用caching更快的变体:
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) }); }
最近我遇到了一个非常类似的问题。 只是想与大家分享我们的解决scheme。 我想从一个json对象创build一个Car<CarA>
的实例,使用它有一个枚举:
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) });
高性能仍然可以通过以下方式来实现:
// 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; }
和
// ///<summary>Base class for read-only objects</summary> public partial interface IBaseRO { void Initialize( IDTO dto ); void Initialize( object value ); }
然后相关的类必须从这个接口派生并相应地初始化。 请注意,在我的情况下,这个代码是周围类的一部分,已经有<T>作为generics参数。 在我的情况下,R也是一个只读类。 IMO,Initialize()函数的公开可用性对不变性没有负面影响。 这个类的用户可以放入另一个对象,但这不会修改底层的集合。