C#中的钻石语法

ins*_*get 18 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
    }
}
Run Code Online (Sandbox Code Playgroud)

有谁知道这是否可能,如果是的话,我怎么可能去使用它?

Jon*_*eet 13

不,没有什么比C#中的钻石语法更像了.你最接近的将是这样的东西:

public static class Lists
{
    public static List<T> NewList<T>(List<T> ignored)
    {
        return new List<T>();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

public MyClass()
{
    ProtoTypes = Lists.NewList(ProtoTypes);
}
Run Code Online (Sandbox Code Playgroud)

这只是使用普通的泛型类型推断来获取方法T.请注意,参数的完全被忽略 - 它只是编译时类型,这很重要.

我个人认为这很难看,我只是直接使用构造函数.如果您更改ProtoTypes编译器的类型将发现差异,并且它将花费很长时间来修复它...

编辑:两种可供选择的方案:

  • 一个类似的方法,但有一个out参数:

    public static class Lists
    {
        public static void NewList<T>(out List<T> list)
        {
            list = new List<T>();
        }
    }
    
    ...
    
    Lists.NewList(out ProtoTypes);
    
    Run Code Online (Sandbox Code Playgroud)
  • 同样的方法,但作为扩展方法,名称为New:

    public static class Lists
    {
        public static List<T> New<T>(this List<T> list)
        {
            return new List<T>();
        }
    }
    
    ...
    
    ProtoTypes = ProtoTypes.New();
    
    Run Code Online (Sandbox Code Playgroud)

我更喜欢这两种方法中的第一种方法:)

  • 在C#3的设计过程中,我们考虑了"List <?> mylist = new List <int>()"形式的"mumble typing",因为它使得某些涉及匿名类型的场景更容易编写.显然它从来没有得到实施. (8认同)
  • @EricLippert:显然解决方案是重载`List <T>`中的一元`+`运算符,只返回一个新的`List <T>`.然后构造函数体可能只是:`ProtoTypes = + ProtoTypes;`毕竟,一元加运算符对非文字表示类型没有任何其他重要用途,对吧?;) (7认同)
  • @HighCore:我原本想到的是扩展方法,实际上 - 我已经添加了这个和`out`参数变体.遗憾的是我们不能单独添加运算符,否则我们*可以*使用运算符.然后跑开了尖叫. (2认同)

Kei*_*thS 6

正如Jon Skeet所说,Eric Lippert支持,C#中泛型类的构造函数不能从它们的参数或构造赋值的变量类型推断出它们的类型.这种行为有用时的首选模式通常是静态通用工厂方法,它可以从其参数中推断出自己的泛型类型.Tuple.Create()就是一个例子; 给它任何参数列表最多8个,它将创建一个强类型的通用元组,并将这些参数作为数据字段.但是,这对你的情况来说效果不好.

当变量是本地变量时,请考虑以相反的方式进行变换; 使用变量类型推断,通过var关键字:

var Prototypes = new List<double[][]>();
Run Code Online (Sandbox Code Playgroud)

这就是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
    }
}
Run Code Online (Sandbox Code Playgroud)

这意味着很明显; 它比它的价值更麻烦.

解释为什么我看起来只是这样做; 以上是我用来根据传递的参数抛出ArgumentNullExceptions的方法的改编,它要求将值封装在表达式中,以便保留调用方法中实际参数的名称.在那种情况下,幕后的复杂性降低了,因为我在主帮助器中需要的只是检查null,并且增加的复杂性比我花费更多的东西,允许我在每一行中进行单行检查.代码库的方法和构造函数.

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