Laden...

Type-Instanz als Generic-<T> verwenden

Erstellt von florian.reischer vor 14 Jahren Letzter Beitrag vor 14 Jahren 1.497 Views
F
florian.reischer Themenstarter:in
56 Beiträge seit 2009
vor 14 Jahren
Type-Instanz als Generic-<T> verwenden

Hallo zusammen,
ich habe ein Problem: Über eine ComboBox wähle ich aus einer Liste von Type-Objekten eine gewünschte Instanz aus. Dann möchte ich eine Dao-Klasse instanzieren, die mir Objekte des gewählten Typs aus der Datenbank holen kann.

Type selectedType = cmbTypes.SelectedItem as Type;
Dao<T> dao = new Dao<T>();

dao.GetAll();
...

Mein Problem ist jetzt, wie ich von der Type-Instanz auf eine Form komme, die ich im Dao als <T> einsetzen kann... 😦 Ist das überhaupt möglich? Das sind ja im Grunde Typennamen und Typeninstanzen die da aufeinander prallen...

Danke im Voraus

2.760 Beiträge seit 2006
vor 14 Jahren

Wahrscheinlich suchst du sowas wie Activator.CreateInstance(Type t)

5.742 Beiträge seit 2007
vor 14 Jahren

Hallo florian.reischer,

oder suchst du?


Type t = typeof(Dao<>).MakeGenericType(type);

1.564 Beiträge seit 2007
vor 14 Jahren

Hallo florian.reischer

Wenn du wirklich mit dynamisch mit generischen Typen arbeiten musst, geht das erst ab .NET 3.5 wirklich. Du musst mit Type.GetType() eine typisierte Version des generischen Basistypen erzeugen. Über die ConstructorInfo kannst du dann eine Instanz erzeugen und diese in einen Basis-Typen casten.

Setzt jedoch voraus, dass deine Typen <T> alle vom gleichen Basistypen abgeleitet sind.

Grüße
Florian Reischl

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

5.742 Beiträge seit 2007
vor 14 Jahren

Wenn du wirklich mit dynamisch mit generischen Typen arbeiten musst, geht das erst ab .NET 3.5 wirklich. Du musst mit Type.GetType() eine typisierte Version des generischen Basistypen erzeugen.

Das verstehe ich jetzt nicht ganz - wieso soll das erst ab .NET 3.5 gehen bzw. was ist bei dir "dynamisch"?

1.564 Beiträge seit 2007
vor 14 Jahren

Hi winSharp93

Sorry, mein Fehler... Ich meinte .NET 4.0 😁 - spielte auf Kovarianz an. Siehe mein Post hier

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

946 Beiträge seit 2008
vor 14 Jahren

Also ich habe das gestestet - und es geht nicht.

Etwas wie

List<object> list = new List<string>(new[] { "Hello", "World" });

ist nicht möglich. Und zwar aus gutem Grund; bei der List<object> könntest du ohne weiteres einen int hinzufügen. Und so einen Fehler zur Laufzeit zu kriegen ist nicht schön.
Implizit verwandeln kann man nur ein IEnumerable<_out_ T&gt;; List<T> bleibt List<T> und IList<T> entsprechend auch.

Trotzdem geht es: Mit dem Schlüsselwort dynamic.

Ein bisschen Code:

static void TestGenericActivator(Type type, params object[] addItems)
{
    // Schlüsselwort dynamic: Methoden werden intern über MethodInfo ausgelesen und aufgerufen -> geht bei passenden addItems
    Console.WriteLine();
    Console.WriteLine("Dynamic List");

    dynamic dynamicList = Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
    foreach (var item in (dynamic[])addItems)
        dynamicList.Add(item);
    foreach (var item in dynamicList)
        Console.WriteLine(item);

    // Cast: eine List<T> (wobei T nicht direkt object) kann man nicht zu einer List<object> casten; normale InvalidCastException
    Console.WriteLine();
    Console.WriteLine("Casted List");

    List<object> castedList = (List<object>)(Activator.CreateInstance(typeof(List<>).MakeGenericType(type)));
    // Hier (Laufzeit-)Exception: System.InvalidCastException was unhandled
    // "Unable to cast object of type 'System.Collections.Generic.List`1[System.String]' to type 'System.Collections.Generic.List`1[System.Object]'."
    
    foreach (var item in addItems)
        castedList.Add(addItems);
    foreach (var item in castedList)
        Console.WriteLine(item);
}

Aufrufe:

TestGenericActivator(typeof(object), 'U', 2);           // Problemlos
TestGenericActivator(typeof(string), "Hello", "World"); // Exception bei Cast
TestGenericActivator(typeof(float), 3, 1, 4, 1);        // Exception bei Cast (alles ints)
TestGenericActivator(typeof(int), "Exception!!!");      // Exception bei beiden Varianten:
// Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled
// "The best overloaded method match for 'System.Collections.Generic.List<int>.Add(int)'
// has some invalid arguments"

Also: Mit Kovarianz geht auch nicht alles. Nur das, was idiotensicher ist (zu sein scheint).

mfg
SeeQuark

F
florian.reischer Themenstarter:in
56 Beiträge seit 2009
vor 14 Jahren

Danke für die schnellen Antworten, also was ich brauche ist sozusagen die statische Variante des Typs selectedType...

Normalerweise würde man so etwas tun um das Dao zu erzeugen:

Dao<Person> personDao = new Dao<Person>();

Person steht dabei allerdings als sozusagen "Keyword" für einen Datentyp. Ich habe nur das Typobjekt selber, quasi so:

Type personType = typeof(Person); und das kommt aus einer ComboBox. Eine Instanz eines Objekts vom Typ Person nutzt mir in diesem Fall auch nichts, da ich als Generic Type ja keine Instanz angeben kann. Ich nenne das was ich brauche einfach mal einen dynamischen Generic-Typ...

946 Beiträge seit 2008
vor 14 Jahren

Implizit steht das schon im Post über mir; es geht nicht.

Schlüsselwort dynamic: Methoden werden intern über MethodInfo ausgelesen und aufgerufen

Etwas anderes fällt mir nicht ein, und gibt es IMHO auch nicht. Zu Reflection: [Artikel] Reflection und Metaprogrammierung.

mfg
SeeQuark

F
florian.reischer Themenstarter:in
56 Beiträge seit 2009
vor 14 Jahren

Das bedeutet dann wohl das die Variante nur funktioniert, in dem man ein riesiges if-else Würstchen auf den selectedItem der Combobox legt und sich dementsprechend die Daos herbeiholt... 😦 Dann werde ich mir wohl eine andere Methode überlegen 😉

1.564 Beiträge seit 2007
vor 14 Jahren

Trick...

Deinem generischen Dao<> Typen von einem nicht generischen Typen ableiten. Den kannst du verwenden 😉

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

5.941 Beiträge seit 2005
vor 14 Jahren

Hallo Florian

Das ist aber auch nur gefühlt besser als object zu benutzen 😉.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

1.564 Beiträge seit 2007
vor 14 Jahren

Njain 😉

Ist einmal umständlich zu implementieren, aber in der Anwendung eigentlich okay. Beispiel einer generischen Liste für abgeleitete Typen welche von einem bestimmten Basistypen abgeleitet sind:

* Abstrakte Basis-Klasse welche IList<MyBasisType> explizit implementiert. Protected Methoden AddInternal(MyBasisType), RemoveInternal(MyBasisType), ... . Die expliziten Methoden von IList<> rufen als Proxy-Methoden die XyzInternal Methoden auf.
* Abgeleitete generische Klasse "MyList<T> where T : MyBasisType". Public Methoden Add/Remove/... welche ebenfalls als Proxy-Methoden die XyzInternal Methoden aufrufen.

Wie gesagt, einmal mehr Code. Hilft jedoch ungemein, wenn man viel bei großen Basis-Klassen.

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.