¿Puede la clase anónima implementar la interfaz?


463

¿Es posible que un tipo anónimo implemente una interfaz?

Tengo un código que me gustaría trabajar, pero no sé cómo hacerlo.

He tenido un par de respuestas que dicen no o crean una clase que implementa la interfaz para construir nuevas instancias de eso. Esto no es realmente ideal, pero me pregunto si existe un mecanismo para crear una clase dinámica delgada en la parte superior de 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);
        }
    }
}

Encontré un artículo Ajuste dinámico de interfaz que describe un enfoque. ¿Es esta la mejor manera de hacer esto?


1
El enlace aparece desactualizado, esta puede ser una alternativa adecuada liensberger.it/web/blog/?p=298 .
Phil Cooper el

1
Sí, puede hacerlo con .NET 4 y superior (a través de DLR), utilizando el paquete nuget ImpromptuInterface .
BrainSlugs83

1
@PhilCooper Su enlace ha estado inactivo, probablemente desde al menos 2016, pero afortunadamente se archivó antes. web.archive.org/web/20111105150920/http://www.liensberger.it/…
Paul

Respuestas:


361

No, los tipos anónimos no pueden implementar una interfaz. De la guía de programación de C # :

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


55
Sería bueno tener estas cosas de todos modos. Si habla de legibilidad de código, las expresiones lambda generalmente no son el camino a seguir. Si estamos hablando de RAD, estoy totalmente interesado 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 los delegados
Arsen Zahray

18
@ArsenZahray: las expresiones lambda, cuando se usan bien, realmente aumentan la legibilidad del código. Son particularmente potentes cuando se usan en cadenas funcionales, que pueden reducir o eliminar la necesidad de variables locales.
Roy Tinker

2
Podría hacer el truco de esta manera "Clases de implementación anónimas: un patrón de diseño para C #" - twistedoakstudios.com/blog/…
Dmitry Pavlov

3
@DmitryPavlov, eso fue sorprendentemente valioso. Los transeúntes: aquí está la versión condensada.
kdbanman

1
puede emitir el tipo anónimo a un objeto anónimo con los mismos campos stackoverflow.com/questions/1409734/cast-to-anonymous-type
Zinov

90

Si bien esta podría ser una pregunta de dos años, y aunque las respuestas en el hilo son todas lo suficientemente verdaderas, no puedo resistir la tentación de decirte que, de hecho, es posible que una clase anónima implemente una interfaz, a pesar de que requiere Un poco de trampa creativa para llegar allí.

En 2008, estaba escribiendo un proveedor LINQ personalizado para mi empleador en ese momento, y en un momento tenía que poder distinguir "mis" clases anónimas de otras anónimas, lo que significaba que implementaran una interfaz que podría usar para escribir cheque ellos. 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, permitir que las clases anónimas implementen interfaces es factible , solo necesita doblar ligeramente las reglas para llegar allí.


8
@Gusdor, en este caso, teníamos un control completo sobre la compilación, y siempre se ejecutaba en una máquina dedicada. Además, dado que estábamos usando PostSharp, y lo que estábamos haciendo es completamente legal dentro de ese marco, nada podría salir mal siempre y cuando nos aseguramos de que PostSharp estuviera instalado en el servidor de compilación que estábamos usando.
Mia Clarke

16
@Gusdor Estoy de acuerdo en que debería ser fácil para otros programadores obtener el proyecto y compilarlo sin gran dificultad, pero ese es un problema diferente que se puede abordar por separado, sin evitar por completo las herramientas o marcos como postsharp. El mismo argumento que usted hace podría hacerse contra VS mismo o cualquier otro marco de MS no estándar que no sea parte de la especificación de C #. Necesitas esas cosas o de lo contrario "se vuelve pop". Eso no es un problema de la OMI. El problema es cuando la compilación se vuelve tan complicada que es difícil hacer que todas esas cosas funcionen bien juntas.
AaronLS

66
@ZainRizvi No, no lo hizo. Que yo sepa, todavía está en producción. La preocupación planteada me pareció extraña entonces, y arbitraria en el mejor de los casos ahora. Básicamente decía "¡No uses un marco, las cosas se romperán!". No lo hicieron, nada salió pop, y no estoy sorprendido.
Mia Clarke

3
Ahora es mucho más fácil; no es necesario modificar el IL después de que se genera el código; solo usa ImpromptuInterface . - Le permite vincular cualquier objeto (incluidos los objetos escritos anónimamente) a cualquier interfaz (por supuesto, habrá excepciones de vinculación tardía si intenta utilizar una parte de la interfaz que la clase no admite).
BrainSlugs83

44

La conversión de tipos anónimos a las interfaces ha sido algo que he deseado durante un tiempo, pero desafortunadamente la implementación actual lo obliga a tener una implementación de esa interfaz.

La mejor solución es tener algún tipo de proxy dinámico que cree la implementación para usted. Usando el excelente proyecto LinFu 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>();

17
Impromptu-Interface Project lo hará en .NET 4.0 usando el DLR y es más liviano que Linfu.
jbtule

¿Es DynamicObjectun tipo LinFu? System.Dynamic.DynamicObjectsolo tiene un constructor protegido (al menos en .NET 4.5).
jdmcnair

Si. Me refería a la implementación de LinFu DynamicObjectque es anterior a la versión DLR
Arne Claassen

15

Los 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 respaldar este escenario.

El método se puede usar así:

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; }
}

13

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


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

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

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

11

La mejor solución es simplemente no usar 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 convertir el resultado de la consulta al tipo de interfaz. Puede haber una mejor manera de hacerlo, pero no pude encontrarlo.


2
Podrías usar en values.OfType<IDummyInterface>()lugar de lanzar. Solo devuelve los objetos de su colección que en realidad se pueden convertir a ese tipo. Todo depende de lo que quieras.
Kristoffer L

7

La respuesta a la pregunta específicamente formulada es no. ¿Pero has estado buscando marcos burlones? Uso MOQ, pero hay millones de ellos 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);
}

1

Otra opción es crear una única clase de implementación concreta que tome lambdas en el constructor.

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

// "Generic" implementing class
public class Dummy : DummyInterface
{
    private readonly Func<string> _getA;
    private readonly Func<string> _getB;

    public Dummy(Func<string> getA, Func<string> getB)
    {
        _getA = getA;
        _getB = getB;
    }

    public string A => _getA();

    public string B => _getB();
}

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 Dummy // Syntax changes slightly
                     (
                         getA: () => value.A,
                         getB: () => 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);
        }
    }
}

Si todo lo que son cada vez va a hacer es convertir DummySourcea DummyInterface, entonces sería más fácil tener sólo una clase que toma una DummySourceen el constructor e implementa la interfaz.

Pero, si necesita convertir muchos tipos DummyInterface, esto es mucho menos placa de caldera.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.