507 Stimmen

Kann eine anonyme Klasse ein Interface implementieren?

Ist es möglich, dass ein anonymer Typ ein Interface implementiert?

Ich habe einen Codeabschnitt, der funktionieren soll, aber weiß nicht, wie das geht.

Ich habe ein paar Antworten erhalten, die entweder nein sagen oder eine Klasse erstellen, die das Interface implementiert, um neue Instanzen davon zu erstellen. Das ist nicht wirklich ideal, aber ich frage mich, ob es einen Mechanismus gibt, um eine schlanke dynamische Klasse über einem Interface zu erstellen, das dies einfacher macht.

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 values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Ich habe einen Artikel gefunden Dynamic interface wrapping, der einen Ansatz beschreibt. Ist das der beste Weg, um dies zu tun?

1 Stimmen

Link scheint veraltet zu sein, dies könnte eine geeignete Alternative sein liensberger.it/web/blog/?p=298.

2 Stimmen

Ja, Sie können dies mit .NET 4 und höher (über das DLR) mithilfe des ImpromptuInterface NuGet-Pakets tun.

1 Stimmen

@PhilCooper Dein Link war wahrscheinlich seit mindestens 2016 nicht verfügbar, aber zum Glück wurde er vorher archiviert. web.archive.org/web/20111105150920/http://www.liensberger.it‌​/…

11voto

ICR Punkte 13528

Die beste Lösung besteht einfach darin, anonyme Klassen nicht zu verwenden.

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());

    }

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

Beachten Sie, dass Sie das Ergebnis der Abfrage in den Typ des Interfaces casten müssen. Es gibt möglicherweise einen besseren Weg, um dies zu tun, aber ich konnte ihn nicht finden.

2 Stimmen

Du könntest stattdessen values.OfType() verwenden anstelle von cast. Es gibt nur die Objekte in deiner Sammlung zurück, die tatsächlich zu diesem Typ umgewandelt werden können. Es kommt ganz darauf an, was du möchtest.

9voto

Die Antwort auf die speziell gestellte Frage lautet nein. Aber haben Sie sich schon mit Mocking-Frameworks beschäftigt? Ich benutze MOQ, aber es gibt Millionen davon da draußen und sie ermöglichen es Ihnen, Schnittstellen inline zu implementieren/stubben (teilweise oder vollständig). Zum Beispiel.

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

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

2voto

Gordon Bean Punkte 3716

Eine weitere Option ist die Erstellung einer einzigen, konkreten Implementierungsklasse, die Lambdas im Konstruktor verwendet.

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

// "Generische" Implementierungsklasse
public class Dummy : DummyInterface
{
    private readonly Func _getA;
    private readonly Func _getB;

    public Dummy(Func getA, Func 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 values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Wenn alles, was Sie tun möchten, ist, DummySource in DummyInterface umzuwandeln, dann wäre es einfacher, einfach eine Klasse zu haben, die einen DummySource im Konstruktor akzeptiert und das Interface implementiert.

Aber wenn Sie viele Typen in DummyInterface umwandeln müssen, ist dies viel weniger Boilerplate.

0voto

Fidel Punkte 6363

Mit Roslyn können Sie dynamisch eine Klasse erstellen, die von einem Interface (oder einer abstrakten Klasse) erbt.

Ich benutze das Folgende, um konkrete Klassen aus abstrakten Klassen zu erstellen.

In diesem Beispiel ist AAnimal eine abstrakte Klasse.

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

Dann können Sie einige Objekte instanziieren:

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

Ohne Zweifel funktioniert dies nicht für jeden Fall, aber es sollte ausreichen, um Sie zum Laufen zu bringen:

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}() wurde aufgerufen"");
                                        }}";

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X