2009-12-17 11 views
16

Tengo algunas clases que no implementan una determinada interfaz pero cumplen estructuralmente con esa interfaz.Creación automática de un contenedor para implementar una interfaz

interface IFoo { 
    void method(); 
} 

class Bar { // does not implement IFoo 
    public void method() {...} 
} 

Ahora, podría escribir una envoltura alrededor de aquellas clases que simplemente delegan a la clase envuelta

class BarWrapper : IFoo { 
    Bar bar = new Bar(); 
    public void method() 
    { 
     bar.method(); 
    } 
} 

Pero eso es un montón de trabajo tedioso. ¿Pueden esas clases contenedoras generarse automáticamente? Algo así como:

IFoo foo = CreateWrapper<IFoo>(new Bar());

Estoy seguro de que podría hacer esto con Reflection.Emit pero nunca he utilizado y que no se ve muy fácil a primera vista.

¿Hay una manera más fácil o quizás hay una biblioteca que ya implementa esto?

+0

Si al final tener que escribir usted mismo, esto podría ayudar como un ejemplo relacionado: http://stackoverflow.com/questions/847809/how-can-i-write-a-generic-container-class-that-implements-a-given-interface-in -c/847975 # 847975 –

Respuesta

2

Es posible que desee ver Castle Project DynamicProxy. Eso es lo que Rhino.Mocks usa para su tipo de proxying.

No lo he usado, por lo que no sé cuánto esfuerzo requerirá de su parte, pero sospecho que es un buen punto de partida.

+0

AFAIK, la versión actual de Dynamic Proxy solo funciona en interfaces y tipos concretos, y solo pueden interceptar métodos virtuales. No hace mapeo. Sin embargo, puede abordar esto en el futuro: http://using.castleproject.org/display/CASTLE/Dynamic+Proxy+3+design+meeting (baje hasta "Tipo de envoltura") –

+0

@Mark: la pregunta solo menciona trabajar con interfaces, por lo que no parece una importante restricción relevante para mí. –

+0

@Jon Skeet: Sí, pero quiere envolver/proxy una clase concreta que no implementa una interfaz ni tiene métodos virtuales, por lo que creo que es muy relevante. –

0

Si está dispuesto a usar .NET 4, una solución podría ser definir la clase contenedora como DynamicObject y convertir las llamadas a los métodos dinámicos en llamadas a la clase envuelta mediante el uso de la reflexión (no estoy seguro si eso realmente sería menos trabajo, de todos modos, también tomar en cuenta las posibles preocupaciones de rendimiento asociadas al uso de la reflexión).

0

Aunque no las he usado yo mismo, creo que las plantillas T4 en Visual Studio se pueden usar para la generación de código de tipos dinámicos link text.

0

Aquí hay un enfoque ligeramente diferente al usar genéricos. Esto necesitaría un poco más de pensamiento de trabajo. Debe implementar un contenedor para cada interfaz y llamar a todos los métodos con reflexión.

class FooWrapper<T> : IFoo 
{ 
    private T obj; 

    public FooWrapper(T obj) 
    { 
     this.obj = obj; 
    } 

    public void method() 
    { 
     // call method with reflection 
     // obj.method(); 
    } 
} 

IFoo foo = new FooWrapper(new Bar()); 
+0

+1: haría lo mismo. Además de agregar algo de caché de reflexión para minimizar la penalización de rendimiento agregada al encontrar los métodos por reflexión –

+1

Buena idea, pero si está utilizando Reflection, entonces también puede utilizar algunas soluciones de tipado de pato existentes. Al final, por motivos de rendimiento, terminarías Emitiendo código IL y reinventando la rueda. – Groo

8

Lo que estás tratando de lograr, se conoce como pato de tipeo. Hay algunas bibliotecas dedicadas que te permitirán hacer eso, aunque no he usado ninguna de ellas.

Con poco esfuerzo (y algo de reflexión) puede usar Castle Dynamic Proxy para hacer eso, utilizando el enfoque descrito here.

Si por alguna razón el enfoque basado en interceptor no fuera aceptable para usted, Dynamic Proxy no lo admite de inmediato (pero), pero si utiliza la versión 2.2 beta, sería bastante fácil proporcionar eso en una manera fuertemente tipada (sin usar interceptores), al proporcionar su propio generador de tipo de proxy (observe cómo se implementan las mezclas).

+1

+1 Gracias por unirse :) –

3

Se puede crear una nueva clase

class SubBar : IFoo, Bar { 
} 

y cree una instancia que (Suponiendo Bar verdaderamente tiene el ducktype, es decir,, los métodos exactos IFoo).

5

Si quiere un soporte de tipado simple y ligero, también puede visitar: Duck Typing project. Funciona con .Net 2.0 y más reciente.

Ejemplo de uso (tomado de David Meyer's site):

public interface ICanAdd 
{ 
    int Add(int x, int y); 
} 

// Note that MyAdder does NOT implement ICanAdd, 
// but it does define an Add method like the one in ICanAdd: 
public class MyAdder 
{ 
    public int Add(int x, int y) 
    { 
     return x + y; 
    } 
} 

public class Program 
{ 
    void Main() 
    { 
     MyAdder myAdder = new MyAdder(); 

     // Even though ICanAdd is not implemented by MyAdder, 
     // we can duck cast it because it implements all the members: 
     ICanAdd adder = DuckTyping.Cast<ICanAdd>(myAdder); 

     // Now we can call adder as you would any ICanAdd object. 
     // Transparently, this call is being forwarded to myAdder. 
     int sum = adder.Add(2, 2); 
    } 
} 

El uso de métodos de extensión, que podría simplificar en algo como esto (simlar a Bart De Smet's sintaxis)

MyAdder myAdder = new MyAdder(); // this doesn't implement the interface 
ICanAdd adder = myAdder.AsIf<ICanAdd>(); // but it quacks like a duck 
int sum = adder.Add(2, 2); 
Cuestiones relacionadas