2012-10-24 18 views
6

Ich möchte eine generische Liste erstellen <> deren Typ zur Laufzeit deklariert wird.C# dynamische generische Liste

Ich kann Folgendes tun, aber da es dynamisch ist, vermute ich, es gibt eine Geschwindigkeitsstrafe. Ich schreibe einen Wrapper in eine exotische Datenbank, also ist Geschwindigkeit entscheidend.

List<dynamic> gdb = new List<dynamic>() 

Ich las this post in dynamischen generische Typen, aber es funktioniert nicht bekommen kann. Insbesondere erscheint das Objekt nicht als Liste und hat daher keine add-Methode.

Type ac; 

    switch (trail[dataPos].Type) 
    { 
     case GlobalsSubscriptTypes.Int32: 
      ac = typeof(System.Int32); 

      break; 
     case GlobalsSubscriptTypes.Int64: 
      ac = typeof(System.Int64); 

      break; 

     default: 
      ac = typeof(System.String); 

      break; 
    } 

    var genericListType = typeof(List<>); 
    var specificListType = genericListType.MakeGenericType(ac); 
    var gdb = Activator.CreateInstance(specificListType); 

Wie erhalte ich GDB als eine der folgenden erscheinen:

List<System.Int32> 
List<System.Int64> 
List<System.String> 
+1

Es Vielleicht ist es einfacher, in diesem Fall einfach eine 'List ' zu verwenden. Wenn der Typ zum Zeitpunkt der Kompilierung nicht bekannt ist, hilft Ihnen die Überprüfung der Kompilierzeit, die Generika liefern, nicht. – Servy

+0

Keine schlechte Idee, aber ich möchte die Liste getippt werden, da es Teil einer Abfrage sein wird. – IamIC

+0

Wie wäre es mit Liste ? –

Antwort

5

Sobald Sie Activator.CreateInstance verwendet haben, können Sie das Ergebnis in den entsprechenden Typ gegossen. Sie könnten IList verwenden, zB:

var gdb = (IList)Activator.CreateInstance(specificListType); 
gdb.Add(1); 

Beachten Sie, dass die oben einen ArgumentException wirft, wenn der Typ Sie hinzufügen nicht den generischen Typ entspricht.

+0

Dies ist die Antwort, nach der ich gesucht habe. Ich war am falschen Ort. – IamIC

+0

Wenn Sie nur die nicht-generische Schnittstelle verwenden möchten, können Sie auch einfach eine 'List ' verwenden und sich die Notwendigkeit ersparen, Reflektion zu verwenden, um die Liste zu erstellen. – Servy

+1

@Servy, das erfordert, dass alle int und long boxed und unboxed sind, was einen erheblichen Performance-Hit darstellt. – IamIC

1

In Ihrem Fall gdb wird immer ein System.Object sein, wie CreateInstance kehrt Objekte jeglicher Art.

Sie haben hier ein paar Optionen - Sie können es als IList (nicht generisch) umwandeln, wie Sie wissen, dass List<T> diese Schnittstelle implementiert und IList.Add verwenden.

Alternativ können Sie nur erklären, es dynamic, und verwenden Sie dynamische Bindung:

dynamic gdb = Activator.CreateInstance(specificListType); 

Auf diese Weise können Sie Ihren Code schreiben, als ob es eine List<T> des entsprechenden Typs ist, und die Anrufe werden zur Laufzeit gebunden werden über dynamische Bindung.

3

Oh, gdbist des richtigen Typs. Da Sie den Typ bei Laufzeit ermitteln, weiß der Compiler es nicht. Daher wird es statisch als object eingegeben und zeigt keine Add Methode. Sie haben ein paar Optionen, die zu beheben:

  1. Guss es auf den richtigen Typ:

    switch (trail[dataPos].Type) 
    { 
        case GlobalsSubscriptTypes.Int32: 
         ((List<int>) gdb).Add(...); 
         break; 
        ... 
        default: 
         ((List<String>) gdb).Add(...); 
         break; 
    } 
    
  2. Cast auf einen gemeinsamen Supertyp:

    ((System.Collections.IList) gdb).Add(...); 
    
  3. Verwendung dynamischer Aufruf:

    dynamic gdb = Activator.CreateInstance(specificListType); 
    gdb.Add(...); 
    
1

List<dynamic> kompiliert den genau gleichen Typ wie List<object> keine Geschwindigkeitsstrafen über eine typisierte List<int>, zum Beispiel jenseits Boxen Wertearten. Allerdings, wenn Sie nach IList werfen werden Sie immer noch die Boxstrafe FYI haben.

Sie könnten in Schwierigkeiten rufen ruft Activator (eine Reflektionsmethode) als es ist wahrscheinlich dramatisch langsamer, dass ein Konstruktor direkt aufrufen.

Fällt irgendetwas davon aus? Sie werden es nicht wissen, es sei denn, Sie führen ein Profil aus, da es immer von Ihrer tatsächlichen Verwendung abhängt.

Meine beste Gast, was Sie wirklich tun möchte, ist:

IList gdb; 

switch (trail[dataPos].Type) 
{ 
    case GlobalsSubscriptTypes.Int32: 
     gdb = new List<int>(); 
     break; 
    case GlobalsSubscriptTypes.Int64: 
     gdb = new List<long>(); 
     break; 
    default: 
     gdb = new List<string>(); 
     break; 
} 

Auch wenn Sie wirklich Operationen ohne Boxen eine allgemeine Hilfsmethode vornehmen müssen alles tun, um Ihre Arbeit zu tun:

switch (trail[dataPos].Type) 
{ 
    case GlobalsSubscriptTypes.Int32: 
     return Helper<int>(trail[dataPos]); 
    case GlobalsSubscriptTypes.Int64: 
     return Helper<long>(trail[dataPos]); 
    default: 
     return Helper<string>(trail[dataPos]); 
} 
+0

Danke für Ihre klare Erklärung. Ich mag die Helper-Idee (obwohl ich nicht viel über seinen Code nachgedacht habe). Wie sich herausstellt, habe ich aufgrund der Polymorphie keine andere Wahl, als dynamic in der Klasse zu verwenden, die den Wert aus der Datenbank bezieht. Also gehe ich davon aus, dass ich einfach mit der Liste gehen sollte. – IamIC

+1

@IanC Ich würde mit allem gehen, was den einfachsten Code produziert, und mich später um die Geschwindigkeit sorgen. – jbtule