2009-04-17 12 views
12

Ich habe eine Klasse, die in einer Client-Anwendung und in einer Server-Anwendung verwendet wird. In der Serveranwendung füge ich der Klasse einige Funktionen durch Erweiterungsmethoden hinzu. Funktioniert super. Jetzt möchte ich ein bisschen mehr:Virtuelle Erweiterungsmethoden?

Meine Klasse (B) erbt von einer anderen Klasse (A).

Ich möchte eine virtuelle Funktion an A (sagen wir Execute()), und implementieren Sie dann diese Funktion in B. Aber nur auf dem Server. Die Execute() -Methode müsste Dinge tun, die nur auf dem Server möglich sind, wobei Typen verwendet werden, die nur der Server kennt.

Es gibt viele Typen, die genau wie B von A erben, und ich möchte Execute() für jede von ihnen implementieren.

Ich hatte gehofft, ich könnte eine virtuelle Erweiterungsmethode zu A hinzufügen, aber diese Idee scheint nicht zu fliegen. Ich suche den elegantesten Weg, um dieses Problem zu lösen, mit oder ohne Erweiterungsmethoden.

Antwort

4

Nein, es gibt keine Dinge wie virtuelle Erweiterungsmethoden. Sie könnten Überladung verwenden, aber Polymorphie wird nicht unterstützt. Es klingt wie Sie auf so etwas wie Dependency Injection (etc.) suchen möchten vielleicht andere Code (Abhängigkeiten) hinzugefügt haben in verschiedenen Umgebungen - und es in regelmäßigen virtuellen Methoden verwenden:

class B { 
    public B(ISomeUtility util) { 
     // store util 
    } 
    public override void Execute() { 
     if(util != null) util.Foo(); 
    } 
} 

Dann einen DI-Framework verwenden zu schaffen eine serverspezifische ISomeUtility Implementierung zu B zur Laufzeit. Sie können mit einem zentralen static Register (IOC, aber keine DI) das gleiche tun:

override void Execute() { 
     ISomeUtility util = Registry.Get<ISomeUtility>(); 
     if(util != null) util.Foo(); 
    } 

(wo Sie Registry etc zu schreiben brauchen würde, und auf dem Server registrieren die ISomeUtility Implementierung)

+0

Dank Marc. Ich werde so etwas umsetzen. Es ist etwas komplizierter für mich, wenn ich diese Klassen serialisiere und sie über den Draht vom Server zum Client und zurück sende. Daher ist die traditionelle DI vielleicht etwas knifflig, aber ich denke, ich kann ein serverseitiges Äquivalent der Klasse B implementieren (wahrscheinlich von B erben), und wenn der Client dem Server eine Instanz von B schickt, muss ich sie durch eine ersetzen neue Instanz von ServerB. – Lucas

+0

Der Registrierungsansatz funktioniert mit Serialisierung in Ordnung. Ich benutze diesen Ansatz, um die gemeinsame Nutzung von Baugruppen mit WCF-Objekten zu tun ... –

0

Virtual impliziert die Vererbung in einer OOP-Weise, und Erweiterungsmethoden sind "nur" statische Methoden, die den Compiler durch ein wenig syntaktischen Zucker erlauben, vorzutäuschen, eine Instanz des Typs seines ersten Parameters aufzurufen. Nein, virtuelle Erweiterungsmethoden kommen nicht in Frage.

Überprüfen Sie die Antwort von Marc Gravell für eine mögliche Lösung für Ihr Problem.

0

Sie können ein Serviceregister implementieren. Beispiel (Serverseite):

static IDictionary<Type, IService> serviceRegister; 

public void ServerMethod(IBusinessType object) 
{ 
    serviceRegister[obect.GetType()].Execute(object); 
} 

Was Sie brauchen, sind eher Dienste in Ihrem Server, der Server-Seite Funktionalität zu implementieren, statt Erweiterungsmethoden. Ich würde nicht zu viel Logik in Erweiterungsmethoden bringen.

0

Lassen Sie mich überprüfen: Sie haben eine Klassenhierarchie von A geerbt, vermutlich nach Ihrer Geschäftsdomäne strukturiert. Dann möchten Sie Verhaltensweisen abhängig davon hinzufügen, wo die Klassen ausgeführt werden. Bis jetzt haben Sie Erweiterungsmethoden verwendet, aber jetzt können Sie nicht feststellen, dass sie mit Ihrer Klassenhierarchie variieren. Welche Arten von Verhaltensweisen verbinden Sie auf dem Server?

Wenn es um Transaktionsverwaltung und Sicherheit geht, sollten Richtlinien, die durch Dependency-Injection à la Marc implementiert werden, gut funktionieren. Sie könnten auch die Implementierung der Strategy pattern über Delegaten und Lambdas für eine eingeschränktere Version von DI in Betracht ziehen. Was jedoch nicht klar ist, ist, wie Client-Code derzeit Ihre Klassen und ihre Erweiterungsmethoden auf dem Server verwendet. Wie abhängig sind andere Klassen davon, wie Sie die serverseitige Funktionalität hinzufügen?Sind es serverseitige Klassen, die derzeit die Erweiterungsmethoden erwarten?

In jedem Fall klingt es so, als ob Sie eine sorgfältige Testbarkeits- und Teststrategie benötigen, da Sie die Variation entlang zweier simultaner Dimensionen einführen (Vererbungshierarchie, Ausführungsumgebung). Sie verwenden Komponententests, ich vertraue? Überprüfen Sie, ob die von Ihnen gewählte Lösung (z. B. DI über Konfiguration) gut mit Testen und Mocking interagiert.

2

Ich würde so etwas wie das Folgende vorschlagen. Dieser Code könnte verbessert werden, indem Unterstützung für die Erkennung von Zwischenklassenhierarchietypen hinzugefügt wird, die keine Dispatch-Zuordnung haben und die nächste Dispatch-Methode basierend auf der Laufzeithierarchie aufrufen. Es könnte auch verbessert werden, indem mithilfe von Reflection die Überladung von ExecuteInteral() erkannt und automatisch zur Dispatch-Map hinzugefügt wird.

using System; 
using System.Collections.Generic; 

namespace LanguageTests2 
{ 
    public class A { } 

    public class B : A {} 

    public class C : B {} 

    public static class VirtualExtensionMethods 
    { 
     private static readonly IDictionary<Type,Action<A>> _dispatchMap 
      = new Dictionary<Type, Action<A>>(); 

     static VirtualExtensionMethods() 
     { 
      _dispatchMap[typeof(A)] = x => ExecuteInternal((A)x); 
      _dispatchMap[typeof(B)] = x => ExecuteInternal((B)x); 
      _dispatchMap[typeof(C)] = x => ExecuteInternal((C)x); 
     } 

     public static void Execute(this A instance) 
     { 
      _dispatchMap[instance.GetType()](instance); 
     } 

     private static void ExecuteInternal(A instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 

     private static void ExecuteInternal(B instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 

     private static void ExecuteInternal(C instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 
    } 

    public class VirtualExtensionsTest 
    { 
     public static void Main() 
     { 
      var instanceA = new A(); 
      var instanceB = new B(); 
      var instanceC = new C(); 

      instanceA.Execute(); 
      instanceB.Execute(); 
      instanceC.Execute(); 
     } 
    } 
} 
3

Sie können die neue dynamische Typ-Funktionalität verwenden, um zu vermeiden eine Registrierung von Arten zu Methoden bauen:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using visitor.Extension; 

namespace visitor 
{ 
    namespace Extension 
    { 
     static class Extension 
     { 
      public static void RunVisitor(this IThing thing, IThingOperation thingOperation) 
      { 
       thingOperation.Visit((dynamic)thing); 
      } 

      public static ITransformedThing GetTransformedThing(this IThing thing, int arg) 
      { 
       var x = new GetTransformedThing {Arg = arg}; 
       thing.RunVisitor(x); 
       return x.Result; 
      } 
     } 
    } 

    interface IThingOperation 
    { 
     void Visit(IThing iThing); 
     void Visit(AThing aThing); 
     void Visit(BThing bThing); 
     void Visit(CThing cThing); 
     void Visit(DThing dThing); 
    } 

    interface ITransformedThing { } 

    class ATransformedThing : ITransformedThing { public ATransformedThing(AThing aThing, int arg) { } } 
    class BTransformedThing : ITransformedThing { public BTransformedThing(BThing bThing, int arg) { } } 
    class CTransformedThing : ITransformedThing { public CTransformedThing(CThing cThing, int arg) { } } 
    class DTransformedThing : ITransformedThing { public DTransformedThing(DThing dThing, int arg) { } } 

    class GetTransformedThing : IThingOperation 
    { 
     public int Arg { get; set; } 

     public ITransformedThing Result { get; private set; } 

     public void Visit(IThing iThing) { Result = null; } 
     public void Visit(AThing aThing) { Result = new ATransformedThing(aThing, Arg); } 
     public void Visit(BThing bThing) { Result = new BTransformedThing(bThing, Arg); } 
     public void Visit(CThing cThing) { Result = new CTransformedThing(cThing, Arg); } 
     public void Visit(DThing dThing) { Result = new DTransformedThing(dThing, Arg); } 
    } 

    interface IThing {} 
    class Thing : IThing {} 
    class AThing : Thing {} 
    class BThing : Thing {} 
    class CThing : Thing {} 
    class DThing : Thing {} 
    class EThing : Thing { } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var things = new List<IThing> { new AThing(), new BThing(), new CThing(), new DThing(), new EThing() }; 
      var transformedThings = things.Select(thing => thing.GetTransformedThing(4)).Where(transformedThing => transformedThing != null).ToList(); 
      foreach (var transformedThing in transformedThings) 
      { 
       Console.WriteLine(transformedThing.GetType().ToString()); 
      } 
     } 
    } 
} 
+1

Sie konw, ich mag dieses Beispiel, aber Sie könnten Ihren Punkt viel viel sipler gesetzt haben. –