Алмазный синтаксис в С#

Java 7 теперь имеет этот "синтаксис бриллианта", где я могу делать такие вещи, как ArrayList<int> = new ArrayList<>();

Мне интересно, есть ли у С# аналогичный синтаксис, который я могу использовать.
Например, у меня есть эта часть класса:

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

Кто-нибудь знает, возможно ли это, и если да, то как я могу его использовать?

Ответ 1

Нет, нет ничего похожего на синтаксис бриллианта в С#. Самое близкое, что вы могли бы прийти, было бы иметь что-то вроде этого:

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, то компилятор определит разницу, и это не займет много времени, чтобы исправить это...

EDIT: две альтернативы для рассмотрения:

  • Аналогичный метод, но с параметром 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();
    

Я предпочитаю первый подход к любому из этих:)

Ответ 2

Как сказал Джон Скит и Эрик Липперт, конструкторы для общих классов в С# не могут вывести свои типы из своих параметров или типа переменной, которой назначена конструкция. Обратный шаблон, когда этот тип поведения полезен, обычно представляет собой статический общий метод factory, который может вывести свой собственный общий тип из параметров его параметров. Tuple.Create() - пример; дайте ему список параметров до 8, и он создаст строго типизированный общий Tuple с этими параметрами в качестве полей данных. Однако это не подходит для вашего дела.

Когда переменная будет локальной, подумайте об этом наоборот; используйте вывод типа переменной с помощью ключевого слова var:

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

Именно так команда С# решила сократить ввод при создании переменных. Локали создаются - и изменяются - гораздо чаще, чем переменные экземпляра, и этот подход делает код С# немного похожим на JavaScript.

Как показал Джон, можно скрыть беспорядок, но вы создадите больше беспорядка в этом процессе. Вот еще одна возможность с использованием возможностей .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 на основе переданных параметров, что требует, чтобы значения были инкапсулированы в выражениях, чтобы сохранить имена фактических параметров от вызывающего метода. В этой ситуации сложность за кулисами уменьшается, так как все, что мне нужно в главном помощнике, - это проверка на нуль, а добавленная сложность позволяет мне намного больше, чем я тратить, позволяя мне выполнять однострочные мои проверки на нуль в каждом метод и конструктор кодовой базы.

Я рекомендую ReSharper в качестве долгосрочного решения для сокращения этой типизации. Когда тип цели назначения известен (как, например, поля и свойства), и вы вводите = new, ReSharper выведет предложение о типе конструктора и автоматически заполнит его для вас, если вы хотите, Если после этого вы измените тип или конструктор, R # будет отмечать назначение как несогласованное, и вы можете указать, что R # изменит то, которое вы хотите сопоставить другому.

Ответ 3

Если вы просто хотите уменьшить многословность кода, существует противоположный короткий синтаксис: оператор var

Старый: List<int> intList = new List<int>();

Новое: var intList = new List<int>();

По крайней мере, вы пишете List только один раз