正如Jon Skeet所说,以及Eric Lippert所证实的那样,在C#中的泛型类构造函数不能从参数或分配给构造函数的变量类型中推断出其类型。当这种类型的行为非常有用时,常用的模式通常是使用静态的泛型工厂方法,该方法可以从其参数的类型中推断出自己的泛型类型。Tuple.Create()是一个例子; 给它任何最多8个参数的列表,它将创建一个具有这些参数作为数据字段的强类型泛型Tuple。然而,这对于您的情况不太适用。
当变量将是局部变量时,考虑采用另一种方式; 通过
var
关键字使用变量类型推断:
var Prototypes = new List<double[][]>()
这是C#团队在实例化变量时决定减少输入的方式。局部变量比实例变量更频繁地创建和更改,这种方法使得C#代码看起来有点像JavaScript。
正如Jon所展示的那样,隐藏混乱是可能的,但在此过程中会创建更多混乱。以下是另一种可能性,使用.NET 3.5/4.0的Expression功能:
public static string GetName(this Expression<Func<object>> expr)
{
if (expr.Body.NodeType == ExpressionType.MemberAccess)
return ((MemberExpression) expr.Body).Member.Name;
if (expr.Body.NodeType == ExpressionType.Convert
&& ((UnaryExpression)expr.Body).Operand.NodeType
== ExpressionType.MemberAccess)
return ((MemberExpression)((UnaryExpression)expr.Body).Operand)
.Member.Name;
throw new ArgumentException(
"Argument 'expr' must be of the form ()=>variableName.");
}
public static void InitializeNew(this object me, params Expression<Func<T>>[] exprs)
where T:new()
{
var myType = me.GetType();
foreach(var expr in exprs)
{
var memberName = expr.GetName()
var myMember = myType.GetMember(memberName,
BindingFlags.Instance|BindingFlags.Public
|BindingFlags.NonPublic|BindingFlags.FlattenHierarchy,
MemberTypes.Field|MemberTypes.Property);
if(myMember == null)
throw new InvalidOperationException(
"Only property or field members are valid as expression parameters");
if(myMember.MemberType == MemberTypes.Field)
((FieldInfo)myMember).SetValue(me, new T());
else
((PropertyInfo)myMember).SetValue(me, new T());
}
}
class MyClass
{
public List<double[][]> list1;
public List<double[][]> list2;
public MyOtherObject object1;
public MyClass()
{
this.Initialize(()=>list1, ()=>list2);
this.Initialize(()=>object1);
}
}
这里的含义很明显,它会带来更多的麻烦,并且价值不大。
为了解释我为什么似乎只是把这个东西放在那里,上面的内容是我用来根据传递的参数抛出ArgumentNullExceptions的方法的改编版本,它需要将值封装在表达式中以保留调用方法的实际参数的名称。在那种情况下,后台的复杂性得到了减少,因为我在主助手中所需的仅是空检查,而增加的复杂性可以为我节省更多时间,通过允许我在代码库的每个方法和构造函数中单行执行空检查。
我建议使用ReSharper作为长期减少打字量的解决方案。当赋值对象的类型已知(例如字段和属性),并且您键入
= new
时,ReSharper会弹出构造函数类型的建议,并在您想要的情况下自动填充它。如果之后更改了类型或构造函数,则R#将标记该赋值不一致,并且您可以告诉R#更改任何一个以匹配另一个。