2008-10-10 37 views
367

¿Es posible tener un tipo anónimo implementar una interfaz. Tengo un código que me gustaría trabajar, pero no sé cómo hacerlo.¿Puede una clase anónima de C# implementar una interfaz?

He recibido un par de respuestas que dicen que no, o crean una clase que implementa la interfaz crea nuevas instancias de eso. Esto no es realmente ideal, pero me pregunto si existe un mecanismo para crear una clase dinámica delgada sobre una interfaz que lo haga simple.

public interface DummyInterface 
{ 
    string A { get; } 
    string B { get; } 
} 

public class DummySource 
{ 
    public string A { get; set; } 
    public string C { get; set; } 
    public string D { get; set; } 
} 

public class Test 
{ 
    public void WillThisWork() 
    { 
     var source = new DummySource[0]; 
     var values = from value in source 
        select new 
        { 
         A = value.A, 
         B = value.C + "_" + value.D 
        }; 

     DoSomethingWithDummyInterface(values); 

    } 

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values) 
    { 
     foreach (var value in values) 
     { 
      Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B); 
     } 
    } 
} 

He encontrado un artículo Dynamic interface wrapping que describe un enfoque. ¿Es esta la mejor manera de hacer esto?

+2

poco fuera de tema, pero se puede hacer esto en Java? Estoy seguro de que lo he visto en alguna parte ... –

+0

C4H5As: Estoy de acuerdo, ya he visto esto antes. Tal vez VB.Net puede hacerlo? O tal vez solo estoy pensando en Java. –

+0

El enlace parece desactualizado, esta quizás sea una alternativa adecuada http://www.liensberger.it/web/blog/?p=298. –

Respuesta

290

No, los tipos anónimos no pueden implementar una interfaz. Desde C# programming guide:

Los tipos anónimos son tipos de clases que constan de una o más propiedades públicas de solo lectura. No se permiten otros tipos de miembros de clase, como métodos o eventos. Un tipo anónimo no se puede convertir a ninguna interfaz o tipo excepto objeto.

+23

+1 - El uso de tipos anónimos normalmente debe limitarse a expresiones lambda y LINQ ... tan pronto como los * datos * se expongan a los llamantes que necesitan "hacer algo" con el objeto, es una muy buena idea implementar una clase concreta. – Mark

+6

@Mark: me gusta usar tipos anónimos como DTO también. Con el mapeo automático funcionando bastante bien en algunos escenarios. – boj

+4

sería bueno tener estas cosas de todos modos. Si está hablando de legibilidad de código, las expresiones lambda generalmente no son el camino a seguir. Si hablamos de RAD, estoy totalmente en la implementación de una interfaz anónima similar a Java. Por cierto, en algunos casos esa característica es más poderosa que las delegadas –

11

No; un tipo anónimo no puede hacer nada excepto tener algunas propiedades. Necesitarás crear tu propio tipo. No leí el artículo vinculado en profundidad, pero parece que usa Reflection. Emitir para crear nuevos tipos sobre la marcha; pero si limita la discusión a las cosas dentro de C# en sí, no puede hacer lo que quiera.

+0

Y es importante tener en cuenta: las propiedades pueden incluir funciones o vacíos (Acción) también: seleccione nuevo {... MyFunction = new Func (s => value.A == s)} funciona aunque no puede hacer referencia a nuevas propiedades en sus funciones (no podemos usar "A" en lugar de "valor.A"). – cfeduke

+2

Bueno, ¿no es solo una propiedad que resulta ser un delegado? En realidad no es un método. –

+1

He usado Reflection.Emit para crear tipos en tiempo de ejecución, pero creo que ahora preferiría una solución de AOP para evitar los costos de tiempo de ejecución. –

11

La mejor solución es simplemente no utilizar clases anónimas.

public class Test 
{ 
    class DummyInterfaceImplementor : IDummyInterface 
    { 
     public string A { get; set; } 
     public string B { get; set; } 
    } 

    public void WillThisWork() 
    { 
     var source = new DummySource[0]; 
     var values = from value in source 
        select new DummyInterfaceImplementor() 
        { 
         A = value.A, 
         B = value.C + "_" + value.D 
        }; 

     DoSomethingWithDummyInterface(values.Cast<IDummyInterface>()); 

    } 

    public void DoSomethingWithDummyInterface(IEnumerable<IDummyInterface> values) 
    { 
     foreach (var value in values) 
     { 
      Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B); 
     } 
    } 
} 

Tenga en cuenta que debe enviar el resultado de la consulta al tipo de interfaz. Puede haber una forma mejor de hacerlo, pero no pude encontrarlo.

+2

Puede usar 'values.OfType ()' en lugar de cast. Solo devuelve los objetos de tu colección que realmente se pueden convertir a ese tipo. Todo depende de lo que quieras. –

38

Lanzar tipos anónimos a las interfaces ha sido algo que he querido por un tiempo, pero lamentablemente la implementación actual te obliga a tener una implementación de esa interfaz.

La mejor solución al respecto es tener algún tipo de proxy dinámico que crea la implementación para usted. El uso de la excelente LinFu project puede reemplazar

select new 
{ 
    A = value.A, 
    B = value.C + "_" + value.D 
}; 

con

select new DynamicObject(new 
{ 
    A = value.A, 
    B = value.C + "_" + value.D 
}).CreateDuck<DummyInterface>(); 
+15

[Impromptu-Interface Project] (http://code.google.com/p/impromptu-interface/) hará esto en .NET 4.0 utilizando el DLR y es más liviano que Linfu. – jbtule

+0

¿'DynamicObject' es un tipo de LinFu? 'System.Dynamic.DynamicObject' solo tiene un constructor protegido (al menos en .NET 4.5). – jdmcnair

+0

Sí. Me refería a la implementación de LinFu de 'DynamicObject' anterior a la versión DLR –

76

Si bien esto puede ser una cuestión de dos años, y si bien las respuestas en el hilo son lo suficientemente todo es verdad, no puede resistir el impulso de le digo que de hecho es posible tener una clase anónima para implementar una interfaz, aunque se necesita un poco de trampa creativa para llegar allí.

Ya en 2008 escribía un proveedor de LINQ personalizado para mi empleador, y en un momento tuve que poder decirle a "mi" clase anónima de otras anónimas, lo que significaba tener que implementar una interfaz que pudiera use para tipearlos. La forma en que lo resolvimos fue mediante el uso de aspectos (utilizamos PostSharp), para agregar la implementación de la interfaz directamente en el IL. Entonces, de hecho, permitiendo que las clases anónimas implementen interfaces es posible, solo necesita doblar las reglas ligeramente para llegar allí.

+41

Este es exactamente el tipo de comportamiento que evito activamente. Compila en otra máquina y todo se vuelve popular. – Gusdor

+19

Me gusta doblar la parte de reglas;) – Beatles1692

+7

@Gusdor, en este caso, teníamos un control total sobre la compilación, y siempre se ejecutó en una máquina dedicada. Además, dado que estábamos usando PostSharp, y lo que estábamos haciendo era totalmente legal dentro de ese marco, nada podía funcionar realmente siempre que nos asegurásemos de que PostSharp estuviera instalado en el servidor de compilación que estábamos utilizando. –

6

La respuesta a la pregunta específicamente formulada es no.¿Pero has estado mirando estructuras burlonas? Yo uso MOQ pero hay millones de ellos por ahí y te permiten implementar/stub (parcial o totalmente) interfaces en línea. P.ej.

public void ThisWillWork() 
{ 
    var source = new DummySource[0]; 
    var mock = new Mock<DummyInterface>(); 

    mock.SetupProperty(m => m.A, source.Select(s => s.A)); 
    mock.SetupProperty(m => m.B, source.Select(s => s.C + "_" + s.D)); 

    DoSomethingWithDummyInterface(mock.Object); 
} 
10

tipos anónimos pueden implementar interfaces a través de un proxy dinámico.

Escribí un método de extensión en GitHub y una publicación de blog http://wblo.gs/feE para admitir este escenario.

El método puede ser utilizado de esta manera:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var developer = new { Name = "Jason Bowers" }; 

     PrintDeveloperName(developer.DuckCast<IDeveloper>()); 

     Console.ReadKey(); 
    } 

    private static void PrintDeveloperName(IDeveloper developer) 
    { 
     Console.WriteLine(developer.Name); 
    } 
} 

public interface IDeveloper 
{ 
    string Name { get; } 
} 
Cuestiones relacionadas