2013-07-18 12 views
12

Java 7 hat nun dieses „Diamant-Syntax“, wo ich Dinge wie ArrayList<int> = new ArrayList<>();Diamant-Syntax in C#

tun kann, frage ich mich, wenn C# eine ähnliche Syntax hat, die ich nutzen können.
Zum Beispiel habe ich diesen Teil einer Klasse:

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

Wer weiß, wenn dies möglich ist, und wenn ja, wie ich gehen könnte es über die Verwendung?

+3

Verwenden Sie übrigens keine Arrays von Arrays. Erstellen Sie stattdessen ein geeignetes Datenmodell. –

Antwort

8

Nein, es gibt nichts Vergleichbares zur Diamantsyntax in C#. Die nächstgelegene Sie kommen könnte, wäre, so etwas haben:

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

Dann:

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

Das ganz normale generische Typinferenz verwendet für Methoden T zu bekommen. Beachten Sie, dass der Wert des Parameters vollständig ignoriert wird - es ist nur der Kompilierungszeittyp wichtig.

Persönlich denke ich, das ist ziemlich hässlich, und ich würde nur den Konstruktor direkt verwenden. Wenn Sie die Art der ProtoTypes der Compiler ändert den Unterschied erkennen, und es dauert nicht lang, es zu reparieren ...

EDIT: Zwei Alternativen zu prüfen:

  • Ein ähnliches Verfahren, aber mit einem out Parameter:

    public static class Lists 
    { 
        public static void NewList<T>(out List<T> list) 
        { 
         list = new List<T>(); 
        } 
    } 
    
    ... 
    
    Lists.NewList(out ProtoTypes); 
    
  • Dasselbe elbe~~POS=HEADCOMP Verfahren, aber als eine Verlängerung Methode, mit dem Namen New:

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

Ich ziehe die erste Annäherung an eine dieser :)

+6

Während des Entwurfs von C# 3 haben wir "mumble typing" der Form "List mylist = new List ()" betrachtet, weil dadurch bestimmte Szenarien mit anonymen Typen einfacher zu schreiben sind. Offensichtlich wurde es nie umgesetzt. –

+4

@EricLippert: Offensichtlich ist die Lösung, den unären '+' Operator in 'List ' zu überladen, um einfach eine neue 'List ' zurückzugeben. Dann könnte der Konstruktor-Body einfach lauten: 'ProtoTypes = + ProtoTypes;' Schließlich hat der unäre Plus-Operator keine anderen wichtigen Verwendungen für nicht literal darstellbare Typen, oder? ;) –

+0

@JonSkeet OMG du bist ein Genie. –

3

Als Jon Skeet sagte und Eric Lippert gesichert, Konstrukteure für generische Klassen in C# ihre Typen aus ihrer Parameter oder die Art der nicht ableiten kann Variable, der die Konstruktion zugeordnet ist. Das Einstiegsmuster, wenn diese Art von Verhalten nützlich ist, ist normalerweise eine statische generische Fabrikmethode, die ihren eigenen generischen Typ von ihren Parametern ableiten kann. Tuple.Create() ist ein Beispiel; geben Sie ihm eine Liste von Parametern bis zu 8, und es wird ein stark typisiertes generisches Tuple mit diesen Parametern als Datenfelder erstellt. Dies funktioniert jedoch nicht gut für Ihren Fall.

Wenn die Variable lokal ist, können Sie auch umgekehrt vorgehen. verwenden variable Typinferenz über das var Stichwort:

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

Dies ist, wie das C# Team auf der Eingabe zu reduzieren entschieden, als Variablen Instanziieren. Lokale Variablen werden viel häufiger als Instanzvariablen erstellt und geändert. Dieser Ansatz lässt C# -Code ein wenig wie JavaScript aussehen.

Wie Jon gezeigt hat, ist es möglich, das Chaos zu verstecken, aber Sie werden mehr Chaos in diesem Prozess verursachen. Hier ist eine andere Möglichkeit mit .NET 3.5/4.0s Ausdruckeigenschaften:

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

Die Implikation ist hier offensichtlich; es ist mehr Ärger als es wert ist.

Um zu erklären, warum ich scheinbar gerade dieses herumliegen hatte; Obiges ist eine Adaption einer Methode, die ich verwende, um ArgumentNullExceptions basierend auf übergebenen Parametern zu werfen, die erfordert, dass die Werte in Expressions eingekapselt werden, um die Namen der tatsächlichen Parameter von der aufrufenden Methode beizubehalten. In dieser Situation ist die Komplexität hinter den Kulissen reduziert, da ich im Haupthelfer nur eine Null-Überprüfung benötige und die zusätzliche Komplexität spart mir viel mehr, als ich ausgeben kann, indem ich meine Null-Checks in jedem einzeilen lasse Methode und Konstruktor der Codebasis.

Ich empfehle ReSharper als eine langfristige Lösung zur Verringerung dieser Typisierung. Wenn der Typ eines Zuweisungsziels bekannt ist (wie z. B. für Felder und Eigenschaften) und Sie = new eingeben, wird ReSharper einen Vorschlag für den Typ des Konstruktors anzeigen und ihn für Sie automatisch ausfüllen, wenn Sie möchten. Wenn Sie danach entweder den Typ oder den Konstruktor ändern, kennzeichnet R # die Zuweisung als inkonsistent und Sie können R # anweisen, den Wert zu ändern, der mit dem anderen übereinstimmen soll.

+1

Es gibt so viele Sachen, die in IDEA/Android Studio integriert sind und nur in Erweiterungen für Visual Studio existieren ... – nasch

2

Wenn Sie nur dort Code Ausführlichkeit reduzieren wollen, ist eine entgegengesetzte shortand Syntax: die var Operator

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

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

Mindestens Sie List nur einmal schreiben