19

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

有谁知道这是否可行,如果可以,我该如何使用它?

4

3 回答 3

14

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

我更喜欢第一种方法来处理这两种方法:)

于 2013-07-18T18:43:02.977 回答
7

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

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

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

这就是 C# 团队决定在实例化变量时减少输入的方式。局部变量的创建和更改比实例变量更频繁,这种方法使 C# 代码看起来更像 JavaScript。

正如 Jon 所展示的,隐藏混乱是可能的,但在这个过程中你会制造更多的混乱。这是使用 .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 的方法的改编,该方法需要将值封装在 Expressions 中,以便保留调用方法的实际参数的名称。在这种情况下,减少了幕后的复杂性,因为我在主帮助程序中需要的只是检查空值,而且增加的复杂性为我节省了比我花费的更多的钱,因为我允许我在每个代码库的方法和构造函数。

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

于 2013-07-18T19:44:45.927 回答
3

如果您只想减少代码冗长,则可以使用相反的 shortand 语法:var运算符

老的:List<int> intList = new List<int>();

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

至少你List只写一次

于 2016-05-31T12:48:13.860 回答