156 Stimmen

Wie erzeugt man dynamisch ein generisches C#-Objekt mit Reflection?

In C# habe ich das folgende Objekt:

public class Item
{ }

public class Task<T>
{ }

public class TaskA<T> : Task<T>
{ }

public class TaskB<T> : Task<T>
{ }

Ich möchte dynamisch TaskA oder TaskB mithilfe von C#-Reflection erstellen ( Aktivator.CreateInstance ). Da ich den Typ jedoch nicht im Voraus kenne, muss ich TaskA dynamisch auf der Grundlage einer Zeichenfolge wie "namespace.TaskA" oder "namespace.TaskAB" erstellen.

284voto

JP Alioto Punkte 44283

Sehen Sie sich das an Artikel und dies einfaches Beispiel . Schnelle Übersetzung desselben in Ihre Klassen ...

var d1 = typeof(Task<>);
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

Zu Ihrer Bearbeitung: Für diesen Fall können Sie Folgendes tun ...

var d1 = Type.GetType("GenericTest.TaskA`1"); // GenericTest was my namespace, add yours
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

Wie ich auf backtick1 als Namen für die generische Klasse gekommen bin, erfahren Sie unter dieser Artikel .

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

Type type = typeof(IReadOnlyDictionary<,>);

0 Stimmen

Ist der Backtick erforderlich, d.h. wenn er weggelassen wird, nimmt der Compiler an, dass er 1 ist?

8 Stimmen

Danke für die Verlinkung zu meinem Blog-Artikel "Using Reflection to Instantiate a Generic Class in C# .Net"( omegacoder.com/?p=38 ) als "Einfaches Beispiel". :-) Ich freue mich, dass der Artikel neues Leben erfährt.

0 Stimmen

Wie vergleiche ich generische Typen mit is Stichwort?

9voto

Jérôme Laban Punkte 5106

Sie wären nämlich nicht in der Lage, die letzte Zeile zu schreiben.

Aber wahrscheinlich wollen Sie das Objekt nicht einfach nur um des Erstellens willen erstellen. Wahrscheinlich wollen Sie eine Methode für Ihre neu erstellte Instanz aufrufen.

Sie benötigen dann so etwas wie eine Schnittstelle :

public interface ITask 
{
    void Process(object o);
}

public class Task<T> : ITask
{ 
   void ITask.Process(object o) 
   {
      if(o is T) // Just to be sure, and maybe throw an exception
        Process(o as T);
   }

   public void Process(T o) { }
}

und rufen Sie es mit auf:

Type d1 = Type.GetType("TaskA"); //or "TaskB"
Type[] typeArgs = { typeof(Item) };
Type makeme = d1.MakeGenericType(typeArgs);
ITask task = Activator.CreateInstance(makeme) as ITask;

// This can be Item, or any type derived from Item
task.Process(new Item());

In jedem Fall werden Sie nicht statisch auf einen Typ gecastet, den Sie vorher nicht kennen (in diesem Fall "makeme"). ITask ermöglicht es Ihnen, zu Ihrem Zieltyp zu gelangen.

Wenn Sie das nicht wollen, müssen Sie wahrscheinlich etwas genauer formulieren, was Sie damit erreichen wollen.

0 Stimmen

Ich habe so etwas wie Process() in Task<T>. Und in meinem Fall kümmere ich mich eigentlich nicht mehr um die letzte Zeile, wie Sie festgestellt haben, dass ich einfach task.Process() aufrufe, daher ist es irrelevant, ob die letzte Zeile codiert werden kann.

3voto

Ben M Punkte 21694

Es scheint mir, dass die letzte Zeile Ihres Beispielcodes einfach lauten sollte:

Task<Item> itsMe = o as Task<Item>;

Oder übersehe ich etwas?

7 Stimmen

Sie verpassen nichts. Ich bin es, der nicht klar denken kann. Ich hätte gestern Abend nicht so viel Alkohol trinken sollen!

1voto

clemahieu Punkte 1439

Stellen Sie sicher, dass Sie dies aus einem guten Grund tun, eine einfache Funktion wie die folgende würde statische Typisierung ermöglichen und erlaubt Ihrer IDE Dinge wie "Find References" und Refactor -> Rename zu tun.

public Task <T> factory (String name)
{
  Task <T> result;

  if (name.CompareTo ("A") == 0)
  {
    result = new TaskA ();
  }
  else if (name.CompareTo ("B") == 0)
  {
    result = new TaskB ();
  }

  return result;
}

1 Stimmen

Warum verwenden Sie .CompareTo? Warum nicht == oder .Equals (wenn Sie mehr Kontrolle wollen). Vielleicht wäre ein Schalter sogar besser.

0 Stimmen

Ich habe nie überprüft, ob C# einen String-Vergleich auf == anstelle eines Referenzvergleichs durchführt. CompareTo und Equals sollten die gleiche Laufzeiteffizienz haben, wenn sie korrekt implementiert sind. Ein Switch-Block hätte nicht die gleichen Geschwindigkeitsvorteile wie ein Integer-Block; er würde zu einem if-else-Block kompiliert.

1voto

A Aiston Punkte 707

Ich weiß, dass diese Frage geklärt ist, aber für alle anderen, die es lesen: Wenn Sie alle beteiligten Typen als Zeichenketten haben, können Sie dies als Einzeiler tun:

IYourInterface o = (Activator.CreateInstance(Type.GetType("Namespace.TaskA`1[OtherNamespace.TypeParam]") as IYourInterface);

Immer, wenn ich so etwas gemacht habe, hatte ich eine Schnittstelle, die der nachfolgende Code nutzen sollte, also habe ich die erstellte Instanz auf eine Schnittstelle gecastet.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X