2013-01-23 12 views
6

Ich brauche hier eine kleine Musterrichtung. Neu in C#.Gemeinsame Schnittstelle für zwei Drittanbieter-Klassen, die ich nicht kontrolliere. Externer Polymorphismus?

Ich arbeite mit einem Devipaket von Drittanbietern, das einen Webdienst umschließt. Es gibt zwei spezifische Klassen, mit denen ich mich befasse, die zwar relativ ähnlich sind, sich aber in zwei verschiedenen Namespaces im DevKit befinden, und es gibt keine gemeinsame Basisklasse. Ich möchte jedoch beide gegen eine gemeinsame Schnittstelle programmieren. Ich habe willkürlich eine Implementierung zusammengeschustert, die den Wrapper im Wesentlichen umhüllt, aber ich bin ziemlich sicher, dass es nicht die effizienteste Methode aufgrund des unaufhörlichen Castings ist.

Ich habe durch Artikel über Adapter, Schnittstellen, Erweiterungsmethoden usw. gegraben. Aber ich habe wenig Zeit, also wenn ich einen Schub in eine Richtung bekommen könnte, würde das sehr geschätzt werden.

using ThirdParty.TypeA.Employee; 
using ThirdParty.TypeB.Employee; 

public class Employee 
{ 
    private object genericEmployee; 

    private EmployeeType empType; 

    public enum EmployeeType 
    { 
      TypeA = 0; 
      TypeB = 1; 
    } 

    public Employee(Object employee, EmployeeType type) 
    { 
     genericEmployee = employee; 
     empType = type; 
    } 

    public String Name 
    { 
     if (empType == EmployeeType.TypeA) 
      return (ThirdParty.TypeA.Employee)genericEmployee.Name; 
     else 
      return (ThirdParty.TypeB.Employee)genericEmployee.Name; 
    } 

    public String Age 
    { 
     if (empType == EmployeeType.TypeA) 
      return (ThirdParty.TypeA.Employee)genericEmployee.Age; 
     else 
      return (ThirdParty.TypeB.Employee)genericEmployee.Age; 
    } 
} 

Rev 2:

class EmployeeTypeAAdapter : TypeA, IEmployeeAdapter 
{ 
    TypeA _employee; 
    public EmployeeTypeAAdapter(TypeA employee) 
    { 
     _employee = employee 
    } 

    public String Name 
    { 
     get { return _employee.Name; } 
     set { _employee.Name = value; } 
    } 

     public String Balance 
     { 
     get 
     { 
      if (_employee.Balance != null) 
      { 
       decimal c = _employee.Balance.Amount; 
       return String.Format("{0:C}", c); 
      } 
      else 
      { 
       return ""; 
      } 
      } 
     } 

     //... 
} 

class EmployeeTypeBAdapter : TypeB, IEmployeeAdapter 
{ 
    TypeB _employee; 
    public EmployeeTypeAAdapter(TypeB employee) 
    { 
     _employee = employee 
    } 

    public String Name 
    { 
     get { return _employee.Name; } 
     set { _employee.Name = value; } 
    } 

     public String Balance 
     { 
     get 
     { 
      if (_employee.Balance != null) 
      { 
       decimal c = _employee.Balance.Amount; 
       return String.Format("{0:C}", c); 
      } 
      else 
      { 
       return ""; 
      } 
      } 
     } 

    //.... 
} 
+0

Sind TypA und TypB öffentlich und bekannt, ich meine, müssen Sie Enum wirklich einführen? Du könntest einen generischen Weg gehen. – abatishchev

+0

Was sind die Typen von TypeA und TypeB, wie werden die Klassen deklariert? Nur gleiche Menge von Eigenschaften und keine Basisklasse? Sie können sie also nur durch "Objekt" verallgemeinern? – abatishchev

+0

Es gibt keine Basisklasse in der Third-Party-Bibliothek. Die Enumeration war nur eine Convenience-Eigenschaft, die ich erstellt habe, um zu vermeiden, dass TypeOf oder IS verwendet werden muss, um den Objekttyp (TypeA oder TypeB) bei jedem Zugriff auf eine Eigenschaft zu bestimmen. – RyanMac

Antwort

8

versuchen, diesen Ansatz:

public interface IEmployeeAdapter 
{ 
    string Age { get; set; } 
    string Name { get; set; } 
} 

class EmployeeTypeAAdapter : TypeA, IEmployeeAdapter 
{ 
    public EmployeeTypeAAdapter(TypeA employee) { } 
} 

class EmployeeTypeBAdapter : TypeB, IEmployeeAdapter 
{ 
    public EmployeeTypeBAdapter(TypeB employee) { } 
} 

public static class EmployeeAdapterFactory 
{ 
    public static IEmployeeAdapter CreateAdapter(object employee, EmployeeType type) 
    { 
     switch (type) 
     { 
      case EmployeeType.TypeA: return new EmployeeTypeAAdapter((TypeA)employee); 
      case EmployeeType.TypeB: return new EmployeeTypeBAdapter((TypeB)employee); 
     } 
    } 

    // or without enum 

    public static IEmployeeAdapter CreateAdapter(object employee) 
    { 
     if (employee is TypeA) return new EmployeeTypeAAdapter((TypeA)employee); 
     if (employee is TypeB) return new EmployeeTypeABdapter((TypeB)employee); 
    } 

    // or better introduce sort of type map 
} 

Ein weiterer Eigenname ist EmployeeProxy, wie Sie bevorzugen.

+0

Würde ich dann die Eigenschaften in den oben genannten Adaptern zurückgeben? Beachten Sie, dass die beiden Objekte (TypeA und TypeB) zu 99% identisch sind (möglicherweise gibt es eine oder zwei von 30 Eigenschaften/Methoden, die einen anderen Namen haben) und nur echte Unterschiede im Typ (einer ist vom Typ ThirdPartyAdaptor.TypeA.Employee und der andere ist ThirdPartyAdaptor.TypeB.Employee) Dies würde scheinbar fast doppelte Klassen erstellen. Siehe Rev 2 oben. – RyanMac

+0

@RyanMac: Was sind die Schwierigkeiten? Entschuldigung, folge dir nicht. Sie legen alle geteilten Eigenschaften in die Schnittstelle, es wird automatisch implementiert. Wenn sich ein Eigenschaftsname im zugrunde liegenden Typ und in der Schnittstelle unterscheidet, können Sie "helfen", ihn ordnungsgemäß zu implementieren/anzupassen. – abatishchev

+0

@RyanMac: Sie können auch eine abstrakte Basisklasse einführen, die gemeinsame Eigenschaften und andere Hilfsmethoden enthält. – abatishchev

2

Was Sie versuchen, ist bekannt als Duck typing. Sie können dies mithilfe von Adapterklassen und einer gemeinsam genutzten Schnittstelle durchführen, aber das manuelle Erstellen dieser Adapter erfordert viel repetitiven Klebecode. Eine Möglichkeit, den Klebercode zu schreiben, besteht darin, den Adaptertyp dynamisch zu konstruieren. Sie können dies selbst über IL Emit tun (eine lohnende Übung, wenn Sie noch nie eine Chance hatten, damit zu spielen, obwohl es einige Grenzfälle zu beachten gibt.) Wenn Sie nur daran interessiert sind, es zum Laufen zu bringen, Sie können jedoch auch this project als Startpunkt auswählen. Der C# -Dynamic-Typ könnte ebenfalls verwendet werden (und endet mit der gleichen Code-Generierung im Hintergrund), aber Sie erhalten keine Referenz, die Sie an nicht-dynamischen Code weitergeben können, als wäre es eine Schnittstelle Art.