2017-03-02 3 views
0

Ich habe zwei Schnittstellen:Implementierung abstrakten/Interface-Methoden enthalten, abstrakten/Interface-Typen Parameter

public interface IController 
{ 
    void doSomething(IEntity thing); 
} 

public interface IEntity { ... } 

Ein Beispiel Implementierung wäre:

public class ControllerImplA : IController 
{ 
    public void doSomething(IEntity entity) 
    { 
     EntityImplA entityForA = (EntityImplA)entity; 
     ... 
    } 
} 

public class EntityImplA : IEntity { ... } 

Die entity Parameter von ControllerImplA.doSomething() immer EntityImplA sein werden. Gleichermaßen wird der entity Parameter von ControllerImplB.doSomething() immer EntityImplB sein und so weiter für andere Implementierungen.

Gibt es eine Möglichkeit zu vermeiden, die Downcasting in meinem aktuellen Code zu verwenden? Mit anderen Worten möchte ich so etwas tun:

ohne die Schnittstellen zu ändern? Was ist, wenn abstrakte Elternklassen anstelle von Schnittstellen verwendet werden?

+3

"Gibt es eine Möglichkeit, die Downcasting zu vermeiden" Verwenden Sie Generika. "ohne die Schnittstellen zu ändern" Uhhh ... – BoltClock

+1

Gibt es einen Grund, dass Sie Ihre Schnittstelle zu einer abstrakten Klasse ändern können, aber die Schnittstelle selbst nicht ändern können? – HimBromBeere

+2

Wenn Sie Schnittstellen durch abstrakte Klasse ersetzen können, denke ich, dass Sie sie auch ändern können, um Generika zu verwenden. – Evk

Antwort

3

Sie möchten Generika verwenden. die Schnittstellen zu modifizieren ist der einzige Weg, um diese sauber zu machen:

public interface IController<T> where T : IEntity 
{ 
    void doSomething(T thing); 
} 

Dann:

public class ControllerImplA : IController<EntityImplA> 
{ 
    public void doSomething(EntityImplA entity) 
    { 
     ... 
    } 
} 

Allerdings, wenn Sie wirklich nicht die Schnittstelle ändern, zum Beispiel Es wird von einer Third-Party-Bibliothek zur Verfügung gestellt, dann ist die Umgehung von @dasblinkenlight ungefähr so ​​sauber, wie Sie bekommen werden.

+1

"Ohne die Schnittstellen zu ändern" ...? – HimBromBeere

+0

Hmm ... guter Punkt. Habe das erste Mal nicht gesehen - war es bearbeitet, oder habe ich es gerade vermisst? Wie auch immer, dies ist der einzige Weg, der Sinn ergibt. – Baldrick

+0

Akzeptieren. Entschuldigung, wenn es verwirrend klingt, aber wenn ich sage, dass ich nicht mit der Schnittstelle herumspielen will, meinte ich, dass ich nicht wirklich den Parametertyp der Methode modifizieren möchte, um den spezifischen Implementierungstyp irgendwie zu kodieren, nur um das Downcasting zu entfernen . Es geht mir gut, dass ich in eine abstrakte Klasse statt in ein Interface übergehe ... Ich denke, man kann sagen, ich verstehe diese Sachen einfach nicht gut genug. Ich verbrachte eine Stunde damit, meine Frage in Worte zu fassen. – thegreatjedi

2

Wenn Sie keine Kontrolle über die Schnittstelle haben, implementieren sie in einer allgemeinen abstrakten Klasse mit einem Typ-Parameter den Entitätstyp Angabe wie folgt aus:

public interface IController { 
    void DoSomething(IEntity thing); 
} 

public interface IEntity { ... } 

abstract class AbstractController<TEntity> : IController where TEntity : IEntity { 
    public void DoSomething(IEntity e) { 
     // Forward the call to an abstract method with more specific type 
     DoSomethingImpl((TEntity)e); 
    } 
    // Subclasses need to implement this method instead of the interface method: 
    protected abstract void DoSomethingImpl(TEntity e); 
} 

Jetzt ist Ihre Implementierungen von AbstractController mit der spezifischen Unterklasse ableiten können , wie folgt:

public class ControllerImplA : AbstractController<EntityImplA> { 
    public void DoSomethingImpl(EntityImplA entity) { ... } 
} 

Die Besetzung ist immer noch da, aber jetzt ist es unter allen Implementierungen geteilt.

+2

Gute Problemumgehung. Ein bisschen wie ein Adapter von der nicht-generischen Schnittstelle zu einer generischen abstrakten Klasse. – Baldrick

0

Dies ist der beste Anwendungsfall für Generika. Fügen Sie dazu Ihrer Oberfläche eine generische Einschränkung hinzu, wie Baldrick bereits erwähnt hat.

Wenn - gleich aus welchem ​​seltsamen Grund - die Schnittstelle ändern, möchte nicht, dass, Sie sind auf einem wirklich hässliche Lösung stecken, die das Gießen von Ihrem API verbirgt:

public class ControllerImplA : IController 
{ 
    public void doSomething(IEntity entity) 
    { 
     this.doSomething((EntityImplA) entity); 
    } 

    public void doSomething(EntityImplA entity) 
    { 
     ... 
    } 
} 

beide diese Weise können Sie verwenden können Methoden, die von der Schnittstelle, die auf die andere umleitet, und die abgeleitete, die auf Ihrer Klasse definiert ist, aber nicht auf der Schnittstelle.

var myIEntity = ... 
var myEntity = new EntityImplA(); 
myControler.doSomething(myIEntity); // calls the interface-method 
myControler.doSomething(myEntity); // direclty calls the class-method 

Sie können auch explizit Ihre Schnittstelle implementieren, so dass, wenn das Verfahren von der Schnittstelle Zugriff auf Sie nur die generische Methode sehen, während auf der spezifischen Klasse, die Sie nur die mehr konkrete Umsetzung zu sehen.

Verwandte Themen