Frage an generics, c#, inheritance – Aufrufen einer generischen Methode mit dem richtigen abgeleiteten Typ

9

Ich habe folgendes Szenario:

Ich habe drei Klassen, nennen wir sieA, B undC. Allen gemeinsam ist, dass sie von derselben Schnittstelle erben,ISomeInterface und dass es sich um Klassen handelt, die mit Entity Framework Entitäten zugeordnet werden.

Ich habe eine Methode, die eine Liste von Objekten erhalten hat, die diese Schnittstelle implementiert, aber die Objekte selbst sind Instanzen vonA, B oderC.

Die Methoden-Shell sieht so aus

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    ProcessEntity(entity);
  }
}

Jetzt liegt das Problem bei derProcessEntity Methode. Dies ist eine generische Methode, die die Tabelle der übereinstimmenden Elemente aus der Datenbank nach Typ oder Entität abrufen muss. Sie sieht also folgendermaßen aus:

public void ProcessEntity<T>(T entity)
{
  using( var repository = new DbRepository())
  {
    var set = repository.Set<T>();
    ...
  }
}

Das Problem ist, dass die Leitungvar set = repository.Set<T>(); scheitert daranT istISomeInterface in diesem Fall und nicht der tatsächliche Typ (A, B oderC), es gibt also eine Ausnahme, die sich nicht auf den angegebenen Typ beziehen kann, was verständlich ist.

Meine Frage lautet also: Wie kann ich ProcessEntity mit dem tatsächlichen Typ des Objekts in der Liste aufrufen und nicht mit dem von ihnen implementierten Schnittstellentyp?

Deine Antwort

3   die antwort
2

Sie können Reflection verwenden, um die generische Methodendefinition abzurufen und sie dann aufzurufen. Beispiel:

var method = typeof(ClassContainingProcessEntity)
    .GetMethod(ProcessEntity)
    .MakeGenericMethod(entity.GetType);
method.Invoke(this, entity);

Sie können die Methode nach Typ zwischenspeichern und zur Laufzeit mithilfe einer Delegate-Factory kompilieren, wenn die Leistung kritisch ist.

Alternativ können Sie auch dieBesuchermuster

Ja, beide sollten funktionieren, und ich dachte darüber nach, den Reflexionsweg zu gehen, wenn ich hier keine bessere Antwort bekomme. Scheint, als ob dynamische Korrekturen dies viel sauberer. Danke für deinen Beitrag :) Øyvind Bråthen
14

Ihr könnt euch bewerbendynamic Schlüsselwort, wenn eine Entität an ProcessEntity übergeben wird. In diesem Fall wird der tatsächliche Entitätstyp zur Laufzeit ermittelt.

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    dynamic obj = entity;
    ProcessEntity(obj);
  }
}
Das hat den Trick gemacht. Ich ändereProcessEntity(obj); zuProcessEntity(obj as dynamic);, und das hat prima geklappt. Eine Verwendung für Dynamik, von der ich nichts wusste. Danke vielmals :) Øyvind Bråthen
@ ØyvindKnobloch-Bråthen yep, ich mag diese Typisierung zur Laufzeit sehr Sergey Berezovskiy
2

Nun, Sie können einen Besucher-ähnlichen Trick ausführen und die folgende Problemumgehung verwenden:

Definieren Sie eine MethodeProcess(EntityProcessor ep) imISomeInterfaceImplementiere es inA genauso wieep.ProcessEntity<A>(this) (und genauso inB undC)AnstattProcessEntity(entity) in deiner Schleife ruf einfach anentity.Process(this).

(Die Methodennamen sind vielleicht nicht die saubersten, aber Sie sollten auf die Idee kommen)

@ Øyvind: die Verwendung vondynamic macht den Code kompakter, zu einem Preis, der die Typensicherheit einschränkt. Wie immer in der Softwareentwicklung ist es ein Kompromiss. Für die neuen Klassen werden Sie nicht vergessen, das erforderliche Element zu implementieren, da es von der Schnittstelle benötigt wird. Vlad
Ja, und genau das vermeide ich durch den Einsatz von Dynamic. Der Kompromiss in diesem Fall ist also ein gewisser Verlust an Typensicherheit (und ich bin in diesem Fall damit einverstanden, da es sich nur um diese eine Methode handelt), um die Entitätsklassen sauberer zu halten, indem ihnen keine Schnittstellenmethode auferlegt wird, die von implementiert werden muss alle von ihnen. Øyvind Bråthen
@ Øyvind: Nun, meine Lösung erfordert mehr Arbeit, prüft jedoch die Existenz der erforderlichen Methode zur Kompilierungszeit. Es könnte auch etwas schneller sein. Vlad
Das würde funktionieren, aber Lazyberezovskys Antwort ist viel sauberer für mich, da das bedeutet, dass ich nicht mehr Logik in A, B und C stecken muss (und es gibt genau 20 dieser Typen, nicht 3;)) Øyvind Bråthen

Verwandte Fragen