¿Puede la clase anónima implementar la interfaz?

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

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

He tenido un par de respuestas que dicen que no, o que 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 simplificaría.

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 articulo Envoltura de interfaz dinámica que describe un enfoque. ¿Es esta la mejor forma de hacer esto?

preguntado Oct 10 '08, 10:10

El enlace parece desactualizado, esta puede ser una alternativa adecuada liensberger.it/web/blog/?p=298. -

Sí, puede hacer esto con .NET 4 y superior (a través del DLR), utilizando el Interfaz improvisada paquete nuget. -

@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/… -

en C # 9 puede usar registros (devblogs.microsoft.com/dotnet/welcome-to-c-9-0) Sé que esto no es lo mismo, pero también es un enfoque muy ligero para implementar interfaces "simples" :) -

9 Respuestas

No, los tipos anónimos no pueden implementar una interfaz. Desde el Guía de programación de C #:

Los tipos anónimos son tipos de clase 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 a object.

Respondido 17 Jul 14, 13:07

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, me gusta la implementación de interfaz anónima similar a Java. Por cierto, en algunos casos esa característica es más poderosa que los delegados: Arsen Zahray

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

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

@DmitryPavlov, eso fue sorprendentemente valioso. Transeúntes: aquí es la versión condensada. - kdbanman

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

Si bien las respuestas en el hilo son lo suficientemente verdaderas, no puedo resistir la tentación de decirte que, de hecho, es posible tener una clase anónima que implemente una interfaz, aunque se necesita un poco de trampa creativa para llegar allí.

En 2008, estaba escribiendo un proveedor LINQ personalizado para mi empleador de entonces, y en un momento necesitaba poder distinguir "mis" clases anónimas de otras anónimas, lo que significaba que implementaran una interfaz que pudiera usar para escribir check ellos. La forma en que lo resolvimos fue usando aspectos (usamos 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 necesitas doblar las reglas un poco para llegar allí.

Respondido 24 Feb 21, 19:02

@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 explotar siempre y cuando nos aseguráramos de que PostSharp estuviera instalado en el servidor de compilación que estábamos usando. - Mia Clarke

@Gusdor Estoy de acuerdo en que debería ser fácil para otros programadores obtener el proyecto y compilar sin una gran cantidad de 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 presenta podría hacerse contra el propio VS 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 "explota". Eso no es un problema en mi opinión. El problema es cuando la compilación se vuelve tan complicada que es difícil hacer que todas esas cosas funcionen bien juntas. - AaronLS

@ZainRizvi No, no fue así. Hasta donde yo sé, todavía está en producción. La preocupación planteada me pareció extraña entonces y, en el mejor de los casos, arbitraria para mí ahora. Básicamente decía "¡No uses un framework, las cosas se romperán!". No lo hicieron, nada salió mal, y no me sorprende. - Mia Clarke

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

La transmisión de tipos anónimos a las interfaces ha sido algo que quería durante un tiempo, pero desafortunadamente la implementación actual te obliga a tener una implementación de esa interfaz.

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

contestado el 23 de mayo de 15 a las 13:05

Proyecto de interfaz improvisada hará esto en .NET 4.0 usando el DLR y es más liviano que Linfu. - jbtule

Is DynamicObject un tipo LinFu? System.Dynamic.DynamicObject solo tiene un constructor protegido (al menos en .NET 4.5). - jdmcnair

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

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 apoyar este escenario.

El método se puede utilizar 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; }
}

respondido 09 nov., 14:05

No; un tipo anónimo no puede hacer 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 limitas la discusión a cosas dentro de C # mismo no puedes hacer lo que quieres.

Respondido 10 Oct 08, 13:10

Y es importante tener en cuenta: las propiedades también pueden incluir funciones o vacíos (Acción): seleccione new {... 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 "value.A"). - cfeduke

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

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

La mejor solución es 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 convertir el resultado de la consulta al tipo de interfaz. Puede que haya una forma mejor de hacerlo, pero no pude encontrarla.

contestado el 26 de mayo de 15 a las 10:05

Podrías usar values.OfType<IDummyInterface>() en lugar de fundir. Solo devuelve los objetos de su colección que realmente se pueden convertir a ese tipo. Todo depende de lo que quieras. - kristoffer l

La respuesta a la pregunta formulada específicamente es no. Pero, ¿ha estado buscando marcos de burla? Yo 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);
}

Respondido 31 Jul 14, 17:07

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 vas a hacer es convertir DummySource a DummyInterface, entonces sería más sencillo tener una sola clase que requiera DummySource en el constructor e implementa la interfaz.

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

Respondido 10 Abr '19, 22:04

Con Roslyn, puede crear dinámicamente una clase que hereda de una interfaz (o clase abstracta).

Utilizo lo siguiente para crear clases concretas a partir de clases abstractas.

En este ejemplo, AAnimal es una clase abstracta.

var personClass = typeof(AAnimal).CreateSubclass("Person");

Entonces puedes instanciar algunos objetos:

var person1 = Activator.CreateInstance(personClass);
var person2 = Activator.CreateInstance(personClass);

Sin duda, esto no funcionará para todos los casos, pero debería ser suficiente para comenzar:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Publisher
{
    public static class Extensions
    {
        public static Type CreateSubclass(this Type baseType, string newClassName, string newNamespace = "Magic")
        {
            //todo: handle ref, out etc.
            var concreteMethods = baseType
                                    .GetMethods()
                                    .Where(method => method.IsAbstract)
                                    .Select(method =>
                                    {
                                        var parameters = method
                                                            .GetParameters()
                                                            .Select(param => $"{param.ParameterType.FullName} {param.Name}")
                                                            .ToString(", ");

                                        var returnTypeStr = method.ReturnParameter.ParameterType.Name;
                                        if (returnTypeStr.Equals("Void")) returnTypeStr = "void";

                                        var methodString = @$"
                                        public override {returnTypeStr} {method.Name}({parameters})
                                        {{
                                            Console.WriteLine(""{newNamespace}.{newClassName}.{method.Name}() was called"");
                                        }}";

                                        return methodString.Trim();
                                    })
                                    .ToList();

            var concreteMethodsString = concreteMethods
                                        .ToString(Environment.NewLine + Environment.NewLine);

            var classCode = @$"
            using System;

            namespace {newNamespace}
            {{
                public class {newClassName}: {baseType.FullName}
                {{
                    public {newClassName}()
                    {{
                    }}

                    {concreteMethodsString}
                }}
            }}
            ".Trim();

            classCode = FormatUsingRoslyn(classCode);


            /*
            var assemblies = new[]
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(baseType.Assembly.Location),
            };
            */

            var assemblies = AppDomain
                        .CurrentDomain
                        .GetAssemblies()
                        .Where(a => !string.IsNullOrEmpty(a.Location))
                        .Select(a => MetadataReference.CreateFromFile(a.Location))
                        .ToArray();

            var syntaxTree = CSharpSyntaxTree.ParseText(classCode);

            var compilation = CSharpCompilation
                                .Create(newNamespace)
                                .AddSyntaxTrees(syntaxTree)
                                .AddReferences(assemblies)
                                .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using (var ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);
                //compilation.Emit($"C:\\Temp\\{newNamespace}.dll");

                if (result.Success)
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    Assembly assembly = Assembly.Load(ms.ToArray());

                    var newTypeFullName = $"{newNamespace}.{newClassName}";

                    var type = assembly.GetType(newTypeFullName);
                    return type;
                }
                else
                {
                    IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                        diagnostic.IsWarningAsError ||
                        diagnostic.Severity == DiagnosticSeverity.Error);

                    foreach (Diagnostic diagnostic in failures)
                    {
                        Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                    }

                    return null;
                }
            }
        }

        public static string ToString(this IEnumerable<string> list, string separator)
        {
            string result = string.Join(separator, list);
            return result;
        }

        public static string FormatUsingRoslyn(string csCode)
        {
            var tree = CSharpSyntaxTree.ParseText(csCode);
            var root = tree.GetRoot().NormalizeWhitespace();
            var result = root.ToFullString();
            return result;
        }
    }
}

Respondido el 12 de Septiembre de 20 a las 14:09

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.