C#中的钻石语法

时间:2013-07-18 18:39:30

标签: c# types initialization

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
    }
}

有没有人知道这是否可能,如果有的话,我可以如何使用它?

3 个答案:

答案 0 :(得分:12)

不,没有什么比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();
    

我更喜欢第一种方法:)

答案 1 :(得分:5)

正如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,并且增加的复杂性比我花费更多,通过允许我在每个行中进行单行检查。代码库的方法和构造函数。

我建议将ReSharper作为减少此类型的长期解决方案。当已知赋值目标的类型(例如字段和属性),并键入= new时,ReSharper将弹出构造函数类型的建议,并自动填充它,如果你要。如果之后更改了类型或构造函数,R#会将赋值标记为不一致,并且您可以告诉R#更改要与另一个匹配的任何一个。

答案 2 :(得分:2)

如果您只想减少代码详细程度,则会出现相反的简短语法:var运算符

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

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

至少你只写一次List