正如 Jon Skeet 所说和 Eric Lippert 所支持的那样,C# 中泛型类的构造函数不能从它们的参数或分配构造的变量的类型推断它们的类型。当这种行为有用时,首选模式通常是静态泛型工厂方法,它可以从其参数的泛型类型中推断出自己的泛型类型。Tuple.Create()
是一个例子;给它最多 8 个参数的列表,它将创建一个强类型的泛型元组,其中这些参数作为数据字段。但是,这不适合您的情况。
当变量是本地变量时,请考虑以相反的方式进行;通过var
关键字使用变量类型推断:
var Prototypes = new List<double[][]>();
这就是 C# 团队决定在实例化变量时减少输入的方式。局部变量的创建和更改比实例变量更频繁,这种方法使 C# 代码看起来更像 JavaScript。
正如 Jon 所展示的,隐藏混乱是可能的,但在这个过程中你会制造更多的混乱。这是使用 .NET 3.5/4.0 的表达式功能的另一种可能性:
public static string GetName(this Expression<Func<object>> expr)
{
if (expr.Body.NodeType == ExpressionType.MemberAccess)
return ((MemberExpression) expr.Body).Member.Name;
//most value type lambdas will need this because creating the Expression
//from the lambda adds a conversion step.
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");
//it'd be nice to put these under some umbrella of "DataMembers",
//abstracting the GetValue/SetValue methods
if(myMember.MemberType == MemberTypes.Field)
((FieldInfo)myMember).SetValue(me, new T());
else
((PropertyInfo)myMember).SetValue(me, new T());
}
}
//usage
class MyClass
{
public List<double[][]> list1;
public List<double[][]> list2;
public MyOtherObject object1;
public MyClass()
{
this.Initialize(()=>list1, ()=>list2);
this.Initialize(()=>object1); //each call can only have parameters of one type
}
}
这里的含义很明显;这比它的价值更麻烦。
解释为什么我似乎只是把这个放在身边;以上是我用来根据传递的参数抛出 ArgumentNullExceptions 的方法的改编,该方法需要将值封装在 Expressions 中,以便保留调用方法的实际参数的名称。在这种情况下,减少了幕后的复杂性,因为我在主帮助程序中需要的只是检查空值,而且增加的复杂性为我节省了比我花费的更多的钱,因为我允许我在每个代码库的方法和构造函数。
我推荐 ReSharper 作为减少这种类型的长期解决方案。当分配目标的类型已知(例如字段和属性)并且您键入= new
时,ReSharper 将弹出有关构造函数类型的建议,并根据需要为您自动填充。如果您之后更改类型或构造函数,R# 会将赋值标记为不一致,您可以告诉 R# 更改您想要匹配的任何一个。