2010-07-09 18 views
23

Wenn ich eine generische Klasse:Sammlung von generischen Typen

public class MyClass<T> 
{ 
    public T Value; 
} 

ich mehrere Elemente wie ...

new MyClass<string> 
new MyClass<int> 

... und fügen Sie sie zu einer Sammelstelle instanziiert werden soll. Wie definiere ich die Sammlung, so dass sie eine Liste von generischen Typen enthalten kann? Ich möchte dann irgendwann die Auflistung durchlaufen und die Value-Eigenschaft verwenden. Möglich?

+0

Wie haben Sie Ihre Klasse deklarieren? Wie haben Sie die Backing-Sammlung definiert? – Oded

+0

ist dies nur eine "hypothetische" oder etwas, das Sie als Teil einer Lösung verwenden möchten? Ich frage, weil ein bisschen mehr Kontext geben wir könnte eine bessere Antwort vorschlagen .. :) – Rob

+0

Ziemlich die gleichen Fragen wurde später (Ich beschloss, um zu schließen, aber es könnte nicht machen, da es so alt ist): http: //stackoverflow.com/q/3777057/590790 –

Antwort

23

Lassen Sie Ihre generische Klasse von einer nicht-generischen Basis erben oder implementieren Sie eine nicht generische Schnittstelle. Dann können Sie eine Sammlung dieses Typs erstellen und in den Code umwandeln, den Sie für den Zugriff auf den Inhalt der Sammlung verwenden.

Hier ist ein Beispiel.

public abstract class MyClass 
{ 
    public abstract Type Type { get; } 
} 

public class MyClass<T> : MyClass 
{ 
    public override Type Type 
    { 
     get { return typeof(T); } 
    } 

    public T Value { get; set; } 
} 

// VERY basic illustration of how you might construct a collection 
// of MyClass<T> objects. 
public class MyClassCollection 
{ 
    private Dictionary<Type, MyClass> _dictionary; 

    public MyClassCollection() 
    { 
     _dictionary = new Dictionary<Type, MyClass>(); 
    } 

    public void Put<T>(MyClass<T> item) 
    { 
     _dictionary[typeof(T)] = item; 
    } 

    public MyClass<T> Get<T>() 
    { 
     return _dictionary[typeof(T)] as MyClass<T>; 
    } 
} 
+1

Die Rückgabe des Typs ist eine interessante Variante. –

+1

Ihre Lösung ist definitiv elegant. Ein internes Dictionary von Typen zu halten, ermöglicht einfachere Zugriffsmethoden. –

+0

Zusätzlich können Sie jetzt das 'dynamic' Schlüsselwort verwenden: http://msdn.microsoft.com/en-us/library/dd264736.aspx – Tommy

1

Ich glaube, dass Ihre Sammlung alle vom selben MyClass-Typ sein müsste (wie in T müsste derselbe sein), weil der Compiler nicht weiß, welche Typen Sie zu welchen Elementen in der Sammlung hinzugefügt haben.

Mit anderen Worten, wenn Sie 2 Elemente zu einer Liste hinzuzufügen, sind:

list.Add(new MyClass<string>()); 
list.Add(new MyClass<int>()); 

dann versuchen, einen verweisen:

var myItem = list[1]; 

Der Compiler weiß nicht, was generic zugewiesen wurde Die MyClass-Liste am Element 1, weil die Elemente zur Laufzeit hinzugefügt werden, aber die Generika zur Kompilierzeit bestimmt werden.

Ich bin mir ziemlich sicher, dass was Sie tun möchten, nicht getan werden kann.


Wenn Sie die Anzahl der Elemente im Voraus wissen, vielleicht könnten Sie ein Tuple benutzen?

7

Die einzige Art, wie ich mir vorstellen kann, aus der Spitze von meinem Kopf ist wie folgt (in einer Konsolenanwendung zum Testen eingewickelt):

class Program 
{ 
    static void Main(string[] args) 
    { 
     var x = new MyClass<string>() { Value = "34" }; 
     var y = new MyClass<int>() { Value = 3 }; 

     var list = new List<IMyClass>(); 
     list.Add(x); 
     list.Add(y); 

     foreach (var item in list) 
     { 
      Console.WriteLine(item.GetValue); 
     } 
    } 

    private interface IMyClass 
    { 
     object GetValue { get; } 
    } 

    private class MyClass<T> : IMyClass 
    { 
     public T Value; 

     public object GetValue 
     { 
      get 
      { 
       return Value; 
      } 
     } 
    } 
} 

IE MyClass eine leere Schnittstelle implementieren und dann erstellen Sie Ihre Auflistungen als eine, die Instanzen von Klassen enthält, die diese Schnittstelle implementieren.

Update: ich eine „GetValue“ Methode zur Schnittstelle hinzugefügt habe, dass Sie den „Wert“ der MyClass Instanz als Objekt zugreifen kann. Das ist ungefähr so ​​gut, wie es wird, afaik, wenn Sie eine Sammlung haben möchten, die gemischte Typen enthält.

+0

guten Ruf - das ist eine äquivalente Antwort zu mir mit einer Basisklasse :) –

+0

Aber wie greifen Sie auf die Wert-Eigenschaft? –

+0

Das Problem ist, dass er eine Value-Eigenschaft möchte, die am Ende den richtigen Typ hat, der nicht bestimmt werden kann. – CodingWithSpike

3

Sie möchten eine Basisklasse für MyClass definieren, dann sind Ihre Sammlungen eine Liste der Basisklasse. Ex:

void Main() 
{ 
var a = new MyClass<string>(); 
var b = new MyClass<int>(); 
var c = new List<MyBase>(); 
c.Add(a); 
c.Add(b); 

} 

public class MyBase { } 
// Define other methods and classes here 
public class MyClass<T> : MyBase { 
public T Value { get; set;} 
} 
+1

Es könnte in Ihrem Beispiel wert sein, MyBase als eine abstrakte Klasse zu markieren, um klarer zu machen, was die Absicht ist, .. :) – Rob

+0

Interessant ... Wie iteriere ich durch die Liste und überprüfe den Inhalt der Value-Eigenschaft? – Jeremy

+0

+1 von mir, so ziemlich die gleiche Idee wie meine Antwort :) – Rob

3

Sie wollen eine Sammlung von MyClass haben, für die der Wert des Typparameter T in jedem Fall unterschiedlich ist. Dies ist in .NET nicht möglich. Es fehlt das Äquivalent der Java-Wildcard (?). Was Sie stattdessen tun müssen, ist eine nicht-generische Basisklasse oder Schnittstelle zu erstellen, die MyClass implementieren kann. Zum Beispiel:

public interface IMyClass { 
    object Value { get; set; } 
} 
public class MyClass<T> : IMyClass { 
    public T Value { get; set; } 
    object IMyClass.Value { 
    get { return Value; } 
    set { Value = (T)value; } 
    } 
} 
List<IMyClass> m = new List<IMyClass> { new MyClass<string>(), new MyClass<int> }; 
+0

@ Commongenius, hast du verifiziert, dass es nicht kompiliert wird? Es kompiliert für mich gerade gut in VS2k8 – Rob

+0

Zu der Zeit, als ich meinen Kommentar gepostet hatte, wurde der ursprüngliche Beitrag geändert. Ich habe meinen Kommentar entsprechend geändert. –

3

Ich habe Schnittstellen auf den meisten meiner generischen Typen mit „Untypisierte“ Mitgliedern:

private interface IMyClass 
{ 
    object UntypedValue { get; } 
} 

private class MyClass<T> : IMyClass 
{ 
    T Value { get; set; } 

    object UntypedValue { get { return Value; } } 
} 

Sie auch dies durch die Verwendung von expliziter Schnittstellenimplementierung tun könnten, aber meiner Meinung nach, Es ist viel einfacher, einen separaten Namen zu verwenden.(Es gibt einige CA Hinweise auf explizite Schnittstellenimplementierung als auch)

+0

+1 für das Anbieten von typisierten und untypisierten Werten. –

1

IList

Es gibt keine Art und Weise ist eine generische Sammlung zu definieren, die jeden Geschmack Ihrer generischen Klasse akzeptieren kann ... also IList<MyClass>. Generische Klassen sind nur eine Abkürzung für den Entwickler, um beim Schreiben einer Menge repetitivem Code zu sparen, aber bei der Kompilierung wird jeder Geschmack der generischen Klasse in einen konkreten übersetzt. Wenn Sie also MyClass<string>, MyClass<int>, MyClass<bool> haben, generiert der Compiler 3 separate und unterschiedliche Klassen. Die einzige Möglichkeit zu tun, was Sie wollen, ist eine Schnittstelle für Ihr generisches.

public interface IMyGeneric { 
    Type MyType { get; set;}  
} 

class MyGeneric<T> : IMyGeneric { 

    public MyGeneric() { 
     MyType = typeof(T); 
    } 

    public Type MyType { 
     get; set; 
    } 
} 

und dann kann man sagen,

IList<IMyGeneric> list = new List<IMyGeneric>(); 
list.add(new MyGeneric<string>()); 
list.add(new MyGeneric<int>()); 
2

ich glaube, das Problem ist, dass generische Klassen sind wirklich nicht die gleiche Art überhaupt. Sie sind einfach Vorlagen, die beim Kompilieren ganze neue Typen erstellen (wenn ich das richtig verstehe). Daher sind MyClass<int> und MyClass<string>vollständig verschiedene Typen, je nach Laufzeit. Sie könnten auch MyIntClass und MyStringClass sein, die Sie offensichtlich nicht in der gleichen Liste haben können, ohne sie zuerst einzupacken. Sie erben (notwendigerweise) nicht dieselbe Basisklasse, implementieren dieselben Schnittstellen oder irgendetwas anderes. Sie sind so verschieden wie alle anderen zwei Typen, und Sie müssen sie als solche behandeln (obwohl Sie denken, dass Sie es besser wissen).

Natürlich können Sie sie implementieren eine Schnittstelle, erben ein Basisobjekt oder eine der anderen Optionen bereits gegeben. Werfen Sie einen Blick auf commongenius' answer für eine gute Möglichkeit, dies zu tun.

1

Seit .Net 3 gab es eine CompositeCollection Klasse, die in mehreren Einzelstücke oder auch Sammlungen können enthalten sein. Es wird von WPF-Entwicklern verwendet, um unterschiedliche Elemente in Xaml zu speichern und anzuzeigen. Aber das bedeutet nicht, dass es nicht in Nicht-WPF-Situationen verwendet werden kann. Hier

ist ein Beispiel, wo ich speichern unterschiedliche Dinge von Strings in Dezimalzahlen und extrahieren und alle Elemente aufzuzählen über, dann Elemente eines bestimmten Typs:

CompositeCollection cc = new CompositeCollection(); 

cc.Add(1); 
cc.Add("Item One"); 
cc.Add(1.0M); 
cc.Add(1.0); 
cc.Add("Done"); 

Console.WriteLine ("Every Item"); 

foreach (var element in cc) 
    Console.WriteLine ("\t" + element.ToString()); 

Console.WriteLine (Environment.NewLine + "Just Strings"); 

foreach (var str in cc.OfType<string>()) 
    Console.WriteLine ("\t" + str); 

/* Outputs: 

Every Item 
    1 
    Item One 
    1.0 
    1 
    Done 

Just Strings 
    Item One 
    Done 

*/ 
Verwandte Themen