Java 7 现在有这种“钻石语法”,我可以在其中做类似的事情
ArrayList<int> = new ArrayList<>();
我想知道 C# 是否有类似的语法可供我利用。
例如,我有这部分课程:
class MyClass
{
public List<double[][]> Prototypes; // each prototype is a array of array of doubles
public MyClass()
{
Prototypes = new List<double[][]>; // I'd rather do List<>, in case I change the representation of a prototype later
}
}
有谁知道这是否可行,如果可以,我该如何使用它?
不,C# 中没有什么比钻石语法更好的了。最接近的你可能会得到这样的东西:
public static class Lists
{
public static List<T> NewList<T>(List<T> ignored)
{
return new List<T>();
}
}
然后:
public MyClass()
{
ProtoTypes = Lists.NewList(ProtoTypes);
}
这只是使用普通的泛型类型推断方法来获取
T
。请注意,参数的 value 完全被忽略 - 只有编译时类型才重要。
我个人认为这非常丑陋,我会直接使用构造函数。如果您更改
ProtoTypes
的类型,编译器会发现差异,并且根本不需要很长时间即可修复它...
编辑:要考虑的两种选择:
类似的方法,但带有
out
参数:
public static class Lists
{
public static void NewList<T>(out List<T> list)
{
list = new List<T>();
}
}
...
Lists.NewList(out ProtoTypes);
相同的方法,但作为扩展方法,名称为
New
:
public static class Lists
{
public static List<T> New<T>(this List<T> list)
{
return new List<T>();
}
}
...
ProtoTypes = ProtoTypes.New();
相比这两种方法,我更喜欢第一种方法:)
正如 Jon Skeet 和 Eric Lippert 所支持的那样,C# 中泛型类的构造函数无法从参数或构造所分配的变量类型推断出它们的类型。当这种类型的行为有用时,首选模式通常是静态泛型工厂方法,它可以从其参数的泛型类型推断出自己的泛型类型。
Tuple.Create()
是一个例子;给它任何最多 8 个参数的列表,它将创建一个强类型通用元组,并将这些参数作为数据字段。然而,这对你的情况来说效果不佳。
当变量是局部变量时,请考虑反其道而行之;通过
var
关键字使用变量类型推断:
var Prototypes = new List<double[][]>();
这就是 C# 团队决定在实例化变量时减少输入的方式。本地变量的创建和更改比实例变量更频繁,这种方法使 C# 代码看起来更像 JavaScript。
正如乔恩所展示的,隐藏混乱是可能的,但你会在这个过程中制造更多的混乱。这是使用 .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 的方法的改编,该方法要求将值封装在表达式中,以便保留调用方法中实际参数的名称。在这种情况下,幕后的复杂性会降低,因为我在主帮助器中需要的只是检查 null,并且增加的复杂性为我节省了比我花费的更多的钱,因为允许我在每个中进行一行 null 检查。代码库的方法和构造函数。
我推荐 ReSharper 作为减少这种打字的长期解决方案。当赋值目标的类型已知时(例如字段和属性),并且您输入
= new
,ReSharper 将弹出有关构造函数类型的建议,并根据需要自动填充。如果您随后更改类型或构造函数,R# 会将赋值标记为不一致,并且您可以告诉 R# 更改您想要匹配的任何一个。
如果您只是想减少代码冗长,则可以使用相反的简写语法:
var
运算符
旧:
List<int> intList = new List<int>();
新:
var intList = new List<int>();
至少你只写
List
一次
您可以使用new()。例如
List<int> numbers = new(); //will create new List<int>()