C#中的菱形语法

本文关键字:语法 | 更新日期: 2023-09-27 18:24:00

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#中的菱形语法

不,没有什么能比得上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。请注意,参数的被完全忽略——这只是编译时类型才是重要的。

就我个人而言,我觉得这很难看,我会直接使用构造函数。如果您更改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#中泛型类的构造函数无法从它们的参数或构造所分配的变量的类型推断出它们的类型。当这种类型的行为有用时,go to模式通常是一个静态的泛型工厂方法,可以从其参数中推断出自己的泛型类型。CCD_ 6是一个例子;给它一个最多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;
    //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#更改要匹配的类型或构造函数。

如果你只想减少代码的冗长,有一个相反的shortand语法:var运算符

旧:List<int> intList = new List<int>();

新增:var intList = new List<int>();

至少你只写一次List