it-swarm.com.de

Ein instanziiertes System.Type als Typparameter für eine generische Klasse übergeben

Der Titel ist irgendwie dunkel. Ich möchte wissen, ob dies möglich ist:

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

Offensichtlich wird MyGenericClass wie folgt beschrieben:

public class MyGenericClass<T>

Der Compiler beschwert sich gerade, dass "Der Typ oder Namespace" myType "nicht gefunden werden konnte." Dazu muss es eine Möglichkeit geben.

160
Robert C. Barth

Ohne Reflexion geht das nicht. Sie können es jedoch mit Reflektion tun . Hier ist ein vollständiges Beispiel:

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        Type typeArgument = Type.GetType(typeName);

        Type genericClass = typeof(Generic<>);
        // MakeGenericType is badly named
        Type constructedClass = genericClass.MakeGenericType(typeArgument);

        object created = Activator.CreateInstance(constructedClass);
    }
}

Hinweis: Wenn Ihre generische Klasse mehrere Typen akzeptiert, müssen Sie die Kommas einfügen, wenn Sie die Typnamen weglassen. Beispiel:

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);
207
Jon Skeet

Leider gibt es keinen. Generische Argumente müssen zur Kompilierzeit als 1) gültiger Typ oder 2) generischer Parameter auflösbar sein. Es gibt keine Möglichkeit, generische Instanzen auf der Grundlage von Laufzeitwerten zu erstellen, ohne die Reflektion verwenden zu müssen.

14
JaredPar

Meine Anforderungen waren etwas anders, werden aber hoffentlich jemandem helfen. Ich musste den Typ aus einer Config lesen und den generischen Typ dynamisch instanziieren.

namespace GenericTest
{
    public class Item
    {
    }
}

namespace GenericTest
{
    public class GenericClass<T>
    {
    }
}

Schließlich nennen Sie es so. Definiere den Typ mit einem Backtick .

var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
var a = Activator.CreateInstance(t);
2
Master P

Einige zusätzliche Anweisungen zum Ausführen von Scherecode. Angenommen, Sie haben eine ähnliche Klasse

public class Encoder() {
public void Markdown(IEnumerable<FooContent> contents) { do magic }
public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
}

Angenommen, Sie haben zur Laufzeit ein FooContent  

Wenn Sie zur Kompilierzeit binden konnten, würden Sie dies wünschen 

var fooContents = new List<FooContent>(fooContent)
new Encoder().Markdown(fooContents)

Sie können dies jedoch nicht zur Laufzeit tun. Zur Laufzeit tun Sie dies wie folgt:

var listType = typeof(List<>).MakeGenericType(myType);
var dynamicList = Activator.CreateInstance(listType);
((IList)dynamicList).Add(fooContent);

Dynamisches Aufrufen von Markdown(IEnumerable<FooContent> contents)

new Encoder().Markdown( (dynamic) dynamicList)

Beachten Sie die Verwendung von dynamic im Methodenaufruf. Zur Laufzeit wird dynamicListList<FooContent> sein (zusätzlich auch IEnumerable<FooContent>), da selbst die Verwendung von dynamic immer noch in einer stark typisierten Sprache verwurzelt ist, wählt der Laufzeitbinder die entsprechende Markdown-Methode aus. Wenn keine exakten Typübereinstimmungen vorhanden sind, wird nach einer Objektparametermethode gesucht. Wenn keine der Bedingungen einer Laufzeitbinderausnahme übereinstimmt, wird eine Warnung ausgegeben, dass keine Methode übereinstimmt.

Der offensichtliche Nachteil dieses Ansatzes ist ein enormer Verlust an Typensicherheit zur Kompilierzeit. Trotzdem wird der Code in diese Richtung in einem sehr dynamischen Sinn ausgeführt, der zur Laufzeit immer noch so geschrieben ist, wie Sie es erwarten.

2
Chris Marisic

Wenn Sie wissen, welche Typen übergeben werden, können Sie dies ohne Nachdenken tun. Eine Wechselanweisung würde funktionieren. Natürlich würde dies nur in einer begrenzten Anzahl von Fällen funktionieren, aber es wäre viel schneller als Nachdenken.

public class Type1 { }

public class Type2 { }

public class Generic<T> { }

public class Program
{
    public static void Main()
    {
        var typeName = nameof(Type1);

        switch (typeName)
        {
            case nameof(Type1):
                var type1 = new Generic<Type1>();
                // do something
                break;
            case nameof(Type2):
                var type2 = new Generic<Type2>();
                // do something
                break;
        }
    }
}
0
Todd Skelton

In diesem Snippet möchte ich zeigen, wie eine dynamisch erstellte Liste erstellt und verwendet wird. Zum Beispiel füge ich hier der dynamischen Liste hinzu.

void AddValue<T>(object targetList, T valueToAdd)
{
    var addMethod = targetList.GetType().GetMethod("Add");
    addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
}

var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
var list = Activator.CreateInstance(listType);

AddValue(list, 5);

Ebenso können Sie jede andere Methode in der Liste aufrufen.

0
EGN