Une classe anonyme peut-elle implémenter une interface?


463

Est-il possible qu'un type anonyme implémente une interface?

J'ai un morceau de code que j'aimerais travailler, mais je ne sais pas comment faire.

J'ai eu quelques réponses qui disent soit non, soit créer une classe qui implémente l'interface en construire de nouvelles instances. Ce n'est pas vraiment idéal, mais je me demande s'il existe un mécanisme pour créer une classe dynamique mince au-dessus d'une interface qui rendrait cela 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);
        }
    }
}

J'ai trouvé un article sur l' encapsulation d'interface dynamique qui décrit une approche. Est-ce la meilleure façon de procéder?


1
Le lien semble obsolète, c'est peut-être une alternative appropriée liensberger.it/web/blog/?p=298 .
Phil Cooper

1
Oui, vous pouvez le faire avec .NET 4 et supérieur (via le DLR), en utilisant le package de nuget ImpromptuInterface .
BrainSlugs83

1
@PhilCooper Votre lien est en panne, probablement depuis au moins 2016 - mais heureusement, il a été archivé avant cette date. web.archive.org/web/20111105150920/http://www.liensberger.it/…
Paul

Réponses:


361

Non, les types anonymes ne peuvent pas implémenter une interface. Depuis le guide de programmation C # :

Les types anonymes sont des types de classe qui consistent en une ou plusieurs propriétés publiques en lecture seule. Aucun autre type de membres de classe tels que les méthodes ou les événements n'est autorisé. Un type anonyme ne peut pas être transtypé en une interface ou un type à l'exception d'un objet.


5
ce serait bien d'avoir ce genre de choses de toute façon. Si vous parlez de lisibilité du code, les expressions lambda ne sont généralement pas la voie à suivre. Si nous parlons de RAD, je suis tous dans l'implémentation d'une interface anonyme de type Java. Soit dit en passant, dans certains cas, cette fonctionnalité est plus puissante que les délégués
Arsen Zahray

18
@ArsenZahray: les expressions lambda, lorsqu'elles sont bien utilisées, augmentent réellement la lisibilité du code. Ils sont particulièrement puissants lorsqu'ils sont utilisés dans des chaînes fonctionnelles, ce qui peut réduire ou éliminer le besoin de variables locales.
Roy Tinker

2
Vous pouvez faire l'astuce de cette façon "Classes d'implémentation anonymes - Un modèle de conception pour C #" - twistedoakstudios.com/blog/…
Dmitry Pavlov

3
@DmitryPavlov, c'était étonnamment précieux. Passants: voici la version condensée.
kdbanman

1
vous pouvez
convertir

90

Bien que cela puisse être une question vieille de deux ans, et bien que les réponses dans le fil soient toutes vraies, je ne résiste pas à l'envie de vous dire qu'il est en fait possible qu'une classe anonyme implémente une interface, même si cela prend un peu de triche créative pour y arriver.

En 2008, j'écrivais un fournisseur LINQ personnalisé pour mon employeur de l'époque, et à un moment donné, je devais pouvoir distinguer "mes" classes anonymes d'autres classes anonymes, ce qui signifiait les implémenter une interface que je pouvais utiliser pour taper check leur. Nous l'avons résolu en utilisant des aspects (nous avons utilisé PostSharp ), pour ajouter l'implémentation d'interface directement dans l'IL. Donc, en fait, laisser des classes anonymes implémenter des interfaces est faisable , il vous suffit de légèrement plier les règles pour y arriver.


8
@Gusdor, dans ce cas, nous avions un contrôle complet sur la construction, et elle était toujours exécutée sur une machine dédiée. De plus, puisque nous utilisions PostSharp et que ce que nous faisions était entièrement légal dans ce cadre, rien ne pouvait vraiment éclater tant que nous nous assurions que PostSharp était installé sur le serveur de build que nous utilisions.
Mia Clarke

16
@Gusdor Je suis d'accord qu'il devrait être facile pour d'autres programmeurs d'obtenir le projet et de le compiler sans grande difficulté, mais c'est un problème différent adressable séparément, sans éviter complètement les outils ou les frameworks comme postsharp. Le même argument que vous faites pourrait être fait contre VS lui-même ou tout autre framework MS non standard qui ne fait pas partie de la spécification C #. Vous avez besoin de ces choses ou bien ça "devient pop". Ce n'est pas un problème OMI. Le problème est lorsque la construction devient si compliquée qu'il est difficile de faire en sorte que tout fonctionne correctement.
AaronLS

6
@ZainRizvi Non, ce n'est pas le cas. Pour autant que je sache, il est toujours en production. L'inquiétude soulevée me semblait alors étrange, et arbitraire au mieux pour moi maintenant. Il disait essentiellement "N'utilisez pas de framework, les choses vont casser!". Ils ne l'ont pas fait, rien n'est devenu pop et je ne suis pas surpris.
Mia Clarke

3
C'est beaucoup plus facile maintenant; pas besoin de modifier l'IL après la génération du code; utilisez simplement ImpromptuInterface . - Il vous permet de lier n'importe quel objet (y compris des objets saisis de manière anonyme) à n'importe quelle interface (bien sûr, il y aura des exceptions de liaison tardive si vous essayez d'utiliser une partie de l'interface que la classe ne prend pas réellement en charge).
BrainSlugs83

44

La diffusion de types anonymes sur des interfaces est quelque chose que je voulais depuis un certain temps, mais malheureusement, l'implémentation actuelle vous oblige à avoir une implémentation de cette interface.

La meilleure solution est d'avoir un type de proxy dynamique qui crée l'implémentation pour vous. En utilisant l'excellent projet LinFu, vous pouvez remplacer

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

avec

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

17
Impromptu-Interface Project le fera dans .NET 4.0 en utilisant le DLR et est plus léger que Linfu.
jbtule

Est-ce DynamicObjectun type LinFu? System.Dynamic.DynamicObjectn'a qu'un constructeur protégé (au moins dans .NET 4.5).
jdmcnair

Oui. Je faisais référence à l'implémentation de LinFu DynamicObjectqui est antérieure à la version DLR
Arne Claassen

15

Les types anonymes peuvent implémenter des interfaces via un proxy dynamique.

J'ai écrit une méthode d'extension sur GitHub et un article de blog http://wblo.gs/feE pour prendre en charge ce scénario.

La méthode peut être utilisée comme ceci:

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

Non; un type anonyme ne peut rien faire sauf avoir quelques propriétés. Vous devrez créer votre propre type. Je n'ai pas lu l'article lié en profondeur, mais il semble qu'il utilise Reflection.Emit pour créer de nouveaux types à la volée; mais si vous limitez la discussion aux choses au sein de C # lui-même, vous ne pouvez pas faire ce que vous voulez.


Et il est important de noter que les propriétés peuvent également inclure des fonctions ou des vides (Action): sélectionnez new {... MyFunction = new Func <string, bool> (s => value.A == s)} fonctionne bien que vous ne puissiez pas vous référer à de nouvelles propriétés dans vos fonctions (nous ne pouvons pas utiliser "A" au lieu de "value.A").
cfeduke

2
Eh bien, n'est-ce pas seulement une propriété qui se trouve être un délégué? Ce n'est pas vraiment une méthode.
Marc Gravell

1
J'ai utilisé Reflection.Emit pour créer des types lors de l'exécution, mais je crois que je préférerais maintenant une solution AOP pour éviter les coûts d'exécution.
Norman H

11

La meilleure solution est simplement de ne pas utiliser de classes anonymes.

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

Notez que vous devez convertir le résultat de la requête en type d'interface. Il pourrait y avoir une meilleure façon de le faire, mais je ne l'ai pas trouvé.


2
Vous pouvez utiliser values.OfType<IDummyInterface>()au lieu de lancer. Il ne renvoie que les objets de votre collection qui peuvent réellement être convertis en ce type. Tout dépend de ce que vous voulez.
Kristoffer L

7

La réponse à la question spécifiquement posée est non. Mais avez-vous envisagé des cadres moqueurs? J'utilise MOQ mais il y en a des millions et ils vous permettent d'implémenter / stub (partiellement ou complètement) des interfaces en ligne. Par exemple.

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

Une autre option consiste à créer une seule classe d'implémentation concrète qui prend les lambdas dans le constructeur.

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 tout ce que vous allez jamais à faire est converti DummySourceà DummyInterface, il serait alors plus simple d'avoir une seule classe qui prend DummySourcedans le constructeur et implémente l'interface.

Mais, si vous devez convertir de nombreux types en DummyInterface, c'est beaucoup moins une plaque de chaudière.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.