Kann eine anonyme Klasse eine Schnittstelle implementieren?


463

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

Ich habe einen Code, mit dem ich gerne arbeiten würde, weiß aber nicht, wie ich das machen soll.

Ich habe einige Antworten erhalten, die entweder Nein sagen oder eine Klasse erstellen, die die Schnittstelle implementiert, um neue Instanzen davon zu erstellen. Das ist nicht wirklich ideal, aber ich frage mich, ob es einen Mechanismus gibt, um eine dünne dynamische Klasse über einer Schnittstelle zu erstellen, der dies einfach machen würde.

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

Ich habe einen Artikel Dynamic Interface Wrapping gefunden , der einen Ansatz beschreibt. Ist das der beste Weg, dies zu tun?


1
Link erscheint veraltet, dies ist möglicherweise eine geeignete Alternative liensberger.it/web/blog/?p=298 .
Phil Cooper

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

1
@PhilCooper Ihr Link ist wahrscheinlich seit mindestens 2016 nicht mehr verfügbar - aber zum Glück wurde er zuvor archiviert. web.archive.org/web/20111105150920/http://www.liensberger.it/…
Paul

Antworten:


361

Nein, anonyme Typen können keine Schnittstelle implementieren. Aus der C # -Programmieranleitung :

Anonyme Typen sind Klassentypen, die aus einer oder mehreren öffentlichen schreibgeschützten Eigenschaften bestehen. Andere Arten von Klassenmitgliedern wie Methoden oder Ereignisse sind nicht zulässig. Ein anonymer Typ kann nur für Objekte in eine Schnittstelle oder einen Typ umgewandelt werden.


5
wäre schön, dieses Zeug trotzdem zu haben. Wenn Sie von Lesbarkeit von Code sprechen, sind Lambda-Ausdrücke normalerweise nicht der richtige Weg. Wenn wir über RAD sprechen, bin ich alle in Java-ähnliche anonyme Schnittstellenimplementierung. Übrigens, in einigen Fällen ist diese Funktion mächtiger als Delegierte
Arsen Zahray

18
@ArsenZahray: Lambda-Ausdrücke erhöhen bei guter Verwendung die Lesbarkeit des Codes. Sie sind besonders leistungsfähig, wenn sie in Funktionsketten verwendet werden, wodurch die Notwendigkeit lokaler Variablen verringert oder beseitigt werden kann.
Roy Tinker

2
Sie könnten den Trick auf diese Weise machen "Anonyme Implementierungsklassen - Ein Entwurfsmuster für C #" - twistedoakstudios.com/blog/…
Dmitry Pavlov

3
@DmitryPavlov, das war überraschend wertvoll. Passanten: Hier ist die komprimierte Version.
Kdbanman

1
Sie können den anonymen Typ in ein anonymes Objekt mit denselben Feldern umwandeln. stackoverflow.com/questions/1409734/cast-to-anonymous-type
Zinov

89

Obwohl dies eine zwei Jahre alte Frage sein könnte und die Antworten im Thread alle wahr genug sind, kann ich dem Drang nicht widerstehen, Ihnen zu sagen, dass es tatsächlich möglich ist , dass eine anonyme Klasse eine Schnittstelle implementiert, obwohl es eine benötigt ein bisschen kreatives Betrügen, um dorthin zu gelangen.

Im Jahr 2008 schrieb ich einen benutzerdefinierten LINQ-Anbieter für meinen damaligen Arbeitgeber, und irgendwann musste ich in der Lage sein, "meine" anonymen Klassen von anderen anonymen zu unterscheiden, was bedeutete, dass sie eine Schnittstelle implementierten, über die ich die Prüfung überprüfen konnte Sie. Wir haben es gelöst, indem wir Aspekte verwendet haben (wir haben PostSharp verwendet ), um die Schnittstellenimplementierung direkt in der IL hinzuzufügen. In der Tat ist es machbar , anonyme Klassen Schnittstellen implementieren zu lassen . Sie müssen nur die Regeln leicht biegen, um dorthin zu gelangen.


8
@Gusdor, in diesem Fall hatten wir die vollständige Kontrolle über den Build und er wurde immer auf einem dedizierten Computer ausgeführt. Da wir PostSharp verwendet haben und das, was wir getan haben, in diesem Rahmen völlig legal ist, kann nichts wirklich platzen, solange wir sicherstellen, dass PostSharp auf dem von uns verwendeten Build-Server installiert ist.
Mia Clarke

16
@Gusdor Ich stimme zu, dass es für andere Programmierer einfach sein sollte, das Projekt ohne große Schwierigkeiten zu erhalten und zu kompilieren, aber das ist ein anderes Problem, das separat behandelt werden kann, ohne Tools oder Frameworks wie Postsharp vollständig zu vermeiden. Das gleiche Argument, das Sie vorbringen, könnte gegen VS selbst oder ein anderes nicht standardmäßiges MS-Framework vorgebracht werden, das nicht Teil der C # -Spezifikation ist. Du brauchst diese Dinge oder es geht "Pop". Das ist kein Problem IMO. Das Problem ist, wenn der Build so kompliziert wird, dass es schwierig ist, diese Dinge richtig zusammenzubringen.
AaronLS

6
@ ZainRizvi Nein, hat es nicht. Soweit ich weiß, ist es noch in Produktion. Die Besorgnis schien mir damals seltsam und jetzt bestenfalls willkürlich. Es hieß im Grunde "Benutze kein Framework, die Dinge werden kaputt gehen!". Sie haben es nicht getan, nichts ist Pop geworden, und ich bin nicht überrascht.
Mia Clarke

3
Es ist jetzt viel einfacher; Es ist nicht erforderlich, die IL zu ändern, nachdem der Code generiert wurde. Verwenden Sie einfach ImpromptuInterface . - Sie können jedes Objekt (einschließlich anonym getippter Objekte) an jede Schnittstelle binden (natürlich gibt es verspätete Bindungsausnahmen, wenn Sie versuchen, einen Teil der Schnittstelle zu verwenden, den die Klasse nicht unterstützt).
BrainSlugs83

44

Ich wollte schon seit einiger Zeit anonyme Typen in Schnittstellen umwandeln, aber leider zwingt Sie die aktuelle Implementierung dazu, diese Schnittstelle zu implementieren.

Die beste Lösung besteht darin, einen dynamischen Proxy zu haben, der die Implementierung für Sie erstellt. Mit dem hervorragenden LinFu-Projekt können Sie ersetzen

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

mit

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

17
Impromptu-Interface Project erledigt dies in .NET 4.0 mit dem DLR und ist leichter als Linfu.
jbtule

Ist DynamicObjectein LinFu-Typ? System.Dynamic.DynamicObjecthat nur einen geschützten Konstruktor (zumindest in .NET 4.5).
jdmcnair

Ja. Ich bezog mich auf die LinFu-Implementierung DynamicObjectvor der DLR-Version
Arne Claassen

15

Anonyme Typen können Schnittstellen über einen dynamischen Proxy implementieren.

Ich habe eine Erweiterungsmethode auf GitHub und einen Blog-Beitrag http://wblo.gs/feE geschrieben , um dieses Szenario zu unterstützen.

Die Methode kann folgendermaßen verwendet werden:

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

Nein; Ein anonymer Typ kann nur dazu gebracht werden, einige Eigenschaften zu haben. Sie müssen Ihren eigenen Typ erstellen. Ich habe den verlinkten Artikel nicht ausführlich gelesen, aber es sieht so aus, als würde er Reflection.Emit verwenden, um neue Typen im laufenden Betrieb zu erstellen. Wenn Sie die Diskussion jedoch auf Dinge in C # selbst beschränken , können Sie nicht tun, was Sie wollen.


Und wichtig zu beachten: Eigenschaften können auch Funktionen oder Hohlräume (Action) enthalten: select new {... MyFunction = new Func <string, bool> (s => value.A == s)} funktioniert, obwohl Sie nicht darauf verweisen können neue Eigenschaften in Ihren Funktionen (wir können nicht "A" anstelle von "value.A" verwenden).
cfeduke

2
Ist das nicht nur eine Eigenschaft, die zufällig ein Delegierter ist? Es ist eigentlich keine Methode.
Marc Gravell

1
Ich habe Reflection.Emit verwendet, um zur Laufzeit Typen zu erstellen, aber jetzt würde ich eine AOP-Lösung bevorzugen, um die Laufzeitkosten zu vermeiden.
Norman H

11

Die beste Lösung ist einfach, keine anonymen Klassen 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<IDummyInterface>());

    }

    public void DoSomethingWithDummyInterface(IEnumerable<IDummyInterface> 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 der Schnittstelle umwandeln müssen. Es könnte einen besseren Weg geben, aber ich konnte ihn nicht finden.


2
Sie könnten values.OfType<IDummyInterface>()anstelle von Besetzung verwenden. Es werden nur die Objekte in Ihrer Sammlung zurückgegeben, die tatsächlich in diesen Typ umgewandelt werden können. Es hängt alles davon ab, was Sie wollen.
Kristoffer L

7

Die Antwort auf die speziell gestellte Frage lautet nein. Aber haben Sie sich über spöttische Frameworks Gedanken gemacht? Ich verwende MOQ, aber es gibt Millionen von ihnen, mit denen Sie Schnittstellen (teilweise oder vollständig) inline implementieren / stubben können. Z.B.

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

Eine andere Möglichkeit besteht darin, eine einzelne konkrete Implementierungsklasse zu erstellen, die Lambdas im Konstruktor akzeptiert.

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

Wenn alles , was Sie jemals tun werden convert ist DummySourcezu DummyInterface, dann wäre es einfacher sein, nur eine Klasse, die eine nimmt DummySourceim Konstruktor und implementiert die Schnittstelle.

Wenn Sie jedoch viele Typen umrüsten müssen DummyInterface, ist dies viel weniger Kesselplatte.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.