解决C#泛型类参数无法带参数实例化的问题
概要
本文提供了一个基于C#表达式目录树的方法来解决泛型参数不能调用带参数的构造方法来实例化的问题。
C#泛型的限制
我们看如下的代码,User类需要一个泛型类,泛型类是要以CreditCard为基类。
class User<T> where T :CreditCard, new()
{private readonly T _value;public User(){_value = new T();}}
以上代码可以正常运行,但是我们稍加修改,我们需要调用信用卡类的另一个构造方法,传入用户id,代码如下:
class User<T> where T :CreditCard, new()
{private readonly T _value;public User(){_value = new T(userId);}}
上面的代码是不能通过编译的,因为C#无法在创建变量类型的实例时候,传入参数。这个是C#的限制。
解决方法
我们通过构造表达式目录树,动态构建一个委托,将参数传入。目标委托的是(int userId) => T(userId),我们执行该委托方法,就可以传入参数,在创建变量类型的实例时候,传入参数。
我们定义一个方法createTWithParam(int userId)的方法,用表达式目录树实现该方法:
public T createTWithParam(int userId) {ParameterExpression parameter = Expression.Parameter(typeof(int), "userId");ConstructorInfo ctor = typeof(T).GetConstructor(bindingAttr: BindingFlags.Instance | BindingFlags.Public,binder: null,callConvention: CallingConventions.HasThis,types: new[] { typeof(int), typeof(string) },modifiers: new ParameterModifier[] { });var expressionNew = Expression.New(ctor, new Expression[] { parameter });LambdaExpression newLambda = Expression.Lambda<Func<int, T>>(expressionNew, new ParameterExpression[] { parameter });var newLambdaDelegate = (Func<int,T>)newLambda.Compile();var creditCard = newLambdaDelegate.Invoke(number);return creditCard;}
- 构造Lambda表达式左侧的(int userId),变量名是userId;
- 构造 表达式new T(userId);
- 编译表达式,生成委托;
- 传入参数,调用该委托,生成T类型的实例。
附录
internal class CreditCard
{public CreditCard(int userId){Console.WriteLine(userId);}public CreditCard(){}
}