491 votes

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

Est-il possible de faire en sorte qu'un type anonyme implémente une interface ?

J'ai un morceau de code que j'aimerais faire fonctionner, mais je ne sais pas comment m'y prendre.

J'ai eu quelques réponses qui disaient soit non, soit créer une classe qui implémente l'interface et construire de nouvelles instances de celle-ci. Ce n'est pas vraiment l'idéal, mais je me demande s'il existe un mécanisme permettant de créer une classe dynamique fine au-dessus d'une interface, ce qui simplifierait les choses.

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 Habillage dynamique des interfaces qui décrit une approche. Est-ce la meilleure façon de procéder ?

1 votes

Le lien semble obsolète, ceci pourrait être une alternative appropriée. liensberger.it/web/blog/?p=298 .

2 votes

Oui, vous pouvez le faire avec .NET 4 et plus (via le DLR), en utilisant la fonction ImpromptuInterface paquet nuget.

1 votes

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

376voto

SpaceghostAli Points 3732

Non, les types anonymes ne peuvent pas implémenter une interface. D'après 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 membre de classe, comme les méthodes ou les événements, n'est autorisé. Un type anonyme ne peut être converti en aucune interface ni aucun type, à l'exception de l'objet.

7 votes

Ce serait bien d'avoir ce genre de choses de toute façon. Si l'on parle de lisibilité du code, les expressions lambda ne sont généralement pas la voie à suivre. Si nous parlons de RAD, je suis tout à fait favorable à l'implémentation d'interfaces anonymes de type Java. D'ailleurs, dans certains cas, cette fonctionnalité est plus puissante que les délégués.

21 votes

@ArsenZahray : les expressions lambda, lorsqu'elles sont bien utilisées, augmentent réellement la lisibilité du code. Elles sont particulièrement puissantes lorsqu'elles sont utilisées dans des chaînes fonctionnelles, qui peuvent réduire ou éliminer le besoin de variables locales.

3 votes

Vous pourriez faire le tour de cette façon "Anonymous Implementation Classes - A Design Pattern for C#" (Classes d'implémentation anonymes - un modèle de conception pour C#). twistedoakstudios.com/blog/

93voto

Mia Clarke Points 4525

Bien que les réponses données dans le fil de discussion soient toutes vraies, je ne peux résister à l'envie de vous dire qu'en fait est possible pour qu'une classe anonyme implémente une interface, même s'il faut un peu de tricherie créative pour y parvenir.

En 2008, j'écrivais un fournisseur LINQ personnalisé pour mon employeur de l'époque et, à un moment donné, j'ai eu besoin de pouvoir distinguer "mes" classes anonymes d'autres classes anonymes, ce qui signifiait qu'elles devaient implémenter une interface que je pouvais utiliser pour vérifier leur type. La façon dont nous avons résolu ce problème était d'utiliser des aspects (nous avons utilisé PostSharp ), pour ajouter l'implémentation de l'interface directement dans l'IL. Donc, en fait, laisser des classes anonymes implémenter des interfaces est faisable vous devez juste contourner les règles un peu pour y arriver.

9 votes

@Gusdor, dans ce cas, nous avions un contrôle total sur la construction, et elle a toujours été exécutée sur une machine dédiée. De plus, puisque nous utilisions PostSharp, et que ce que nous faisions était totalement légal dans ce cadre, rien ne pouvait vraiment mal tourner tant que nous nous assurions que PostSharp était installé sur le serveur de construction que nous utilisions.

16 votes

@Gusdor Je suis d'accord pour dire 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 qui peut être traité séparément, sans éviter complètement les outils ou les frameworks comme postsharp. Le même argument que vous avancez pourrait être avancé 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 cela "saute". Ce n'est pas un problème. Le problème, c'est quand la construction devient si compliquée qu'il est difficile d'obtenir que ces choses fonctionnent correctement ensemble.

6 votes

@ZainRizvi Non, ce n'est pas le cas. Pour autant que je sache, il est toujours en production. La préoccupation soulevée me semblait étrange à l'époque, et au mieux arbitraire maintenant. Il s'agissait essentiellement de dire "N'utilisez pas de framework, les choses vont se briser !". Ils ne l'ont pas fait, rien n'a sauté, et je ne suis pas surpris.

48voto

Arne Claassen Points 2793

Le transfert de types anonymes vers des interfaces est quelque chose que j'ai voulu 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
};

con

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

19 votes

Projet Impromptu-Interface fera cela en .NET 4.0 en utilisant le DLR et sera plus léger que Linfu.

0 votes

Es DynamicObject un type de LinFu ? System.Dynamic.DynamicObject ne possède qu'un constructeur protégé (du moins dans .NET 4.5).

0 votes

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

13voto

Marc Gravell Points 482669

Non, un type anonyme ne peut rien faire d'autre que d'avoir quelques propriétés. Vous devez créer votre propre type. Je n'ai pas lu l'article en question 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 éléments suivants au sein même de C# tu ne peux pas faire ce que tu veux.

0 votes

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

2 votes

N'est-ce pas simplement une propriété qui se trouve être un délégué ? Ce n'est pas vraiment une méthode.

1 votes

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

11voto

ICR Points 6960

La meilleure solution est tout 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 existe peut-être un meilleur moyen de le faire, mais je ne l'ai pas trouvé.

2 votes

Vous pourriez utiliser values.OfType<IDummyInterface>() à la place de la fonte. Elle ne renvoie que les objets de votre collection qui peuvent effectivement être convertis en ce type. Tout dépend de ce que vous voulez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X