简体   繁体   English

C#中的钻石语法

[英]Diamond Syntax in C#

Java 7 now has this "diamond syntax" where I can do things like ArrayList<int> = new ArrayList<>(); Java 7现在有了这种“菱形语法”,我可以在这里做类似ArrayList<int> = new ArrayList<>();

I'm wondering if C# has a similar syntax that I can take advantage of. 我想知道C#是否有类似的语法,我可以利用它。
For example, I have this part of a class: 例如,我有一个类的这一部分:

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

Does anyone know if this is possible, and if so, how I might go about using it? 有谁知道这是否可能,如果是的话,我怎么可能去使用它?

No, there's nothing quite like the diamond syntax in C#. 不,没有什么比C#中的钻石语法更像了。 The closest you could come would be to have something like this: 你最接近的将是这样的东西:

public static class Lists
{
    public static List<T> NewList<T>(List<T> ignored)
    {
        return new List<T>();
    }
}

Then: 然后:

public MyClass()
{
    ProtoTypes = Lists.NewList(ProtoTypes);
}

That just uses normal generic type inference for methods to get T . 这只是使用普通的泛型类型推断来获取T方法。 Note that the value of the parameter is completely ignored - it's only the compile-time type which is important. 请注意,参数的完全被忽略 - 它只是编译时类型,这很重要。

Personally I think this is pretty ugly, and I'd just use the constructor directly. 我个人认为这很难看,我只是直接使用构造函数。 If you change the type of ProtoTypes the compiler will spot the difference, and it won't take long at all to fix it up... 如果您更改ProtoTypes的类型,编译器将发现差异,并且不需要很长时间来修复它...

EDIT: Two alternatives to consider: 编辑:两种可供选择的方案:

  • A similar method, but with an out parameter: 一个类似的方法,但有一个out参数:

     public static class Lists { public static void NewList<T>(out List<T> list) { list = new List<T>(); } } ... Lists.NewList(out ProtoTypes); 
  • The same method, but as an extension method, with the name New : 使用相同的方法,但作为扩展方法,名称为New

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

I prefer the first approach to either of these :) 我更喜欢这两种方法中的第一种方法:)

As Jon Skeet said and Eric Lippert backed up, constructors for generic classes in C# cannot infer their types from their parameters or the type of the variable to which the construction is assigned. 正如Jon Skeet所说,Eric Lippert支持,C#中泛型类的构造函数不能从它们的参数或构造赋值的变量类型推断出它们的类型。 The go-to pattern when this type of behavior is useful is usually a static generic factory method, which can infer its own generic type from those of its parameters. 这种行为有用时的首选模式通常是静态通用工厂方法,它可以从其参数中推断出自己的泛型类型。 Tuple.Create() is an example; Tuple.Create()就是一个例子; give it any list of parameters up to 8, and it will create a strongly-typed generic Tuple with those parameters as the data fields. 给它任何参数列表最多8个,它将创建一个强类型的通用元组,并将这些参数作为数据字段。 This doesn't work out well for your case, however. 但是,这对你的情况来说效果不好。

When the variable will be local, consider doing it the other way around; 当变量是本地变量时,请考虑反过来做; use variable type inference, via the var keyword: 通过var关键字使用变量类型推断:

var Prototypes = new List<double[][]>();

This is how the C# team decided to cut down on typing when instantiating variables. 这就是C#团队在实例化变量时决定减少打字的方式。 Locals are created - and change - much more often than instance variables, and this approach makes C# code look a little more like JavaScript. 本地创建 - 并且更改 - 比实例变量更常见,这种方法使C#代码看起来更像JavaScript。

As Jon showed, it's possible to hide the mess, but you'll create more of a mess in the process. 正如Jon所展示的那样,隐藏这些混乱是可能的,但是你会在这个过程中制造更多混乱。 Here's another possibility using .NET 3.5/4.0's Expression features: 这是使用.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
    }
}

The implication is obvious here; 这意味着很明显; it's more trouble than it's worth. 它比它的价值更麻烦。

To explain why I seemingly just had this laying around; 解释为什么我看起来只是这样做; the above is an adaptation of a method I use to throw ArgumentNullExceptions based on passed parameters, which requires the values to be encapsulated within Expressions in order to retain the names of the actual parameters from the calling method. 以上是我用来根据传递的参数抛出ArgumentNullExceptions的方法的改编,它要求将值封装在表达式中,以便保留调用方法中实际参数的名称。 In that situation, the complexity behind the scenes is reduced since all I need in the main helper is a check for null, and the added complexity saves me a lot more than I spend, by allowing me to one-line my null checks in every method and constructor of the codebase. 在那种情况下,幕后的复杂性降低了,因为我在主帮助器中需要的只是检查null,并且增加的复杂性比我花费更多的东西,允许我在每一行中进行单行检查。代码库的方法和构造函数。

I recommend ReSharper as a long-term solution to reducing this typing. 我建议将ReSharper作为减少此类型的长期解决方案。 When the type of an assignment target is known (as it is for instance fields and properties), and you type = new , ReSharper will pop up a suggestion for the type of the constructor, and auto-fill it for you if you want. 当已知赋值目标的类型(例如字段和属性),并键入= new ,ReSharper将弹出构造函数类型的建议,并根据需要自动填充它。 If you change either the type or constructor afterward, R# will flag the assignment as inconsistent, and you can tell R# to change whichever one you want to match the other. 如果之后更改了类型或构造函数,R#会将赋值标记为不一致,并且您可以告诉R#更改要与另一个匹配的任何一个。

If you just want to reduce code verbosity there is an opposite shortand syntax: the var operator 如果您只想减少代码冗长,则会出现相反的短语和语法: var运算符

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

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

At least you write List only once 至少你只写一次List

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM