web-dev-qa-db-fra.com

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

Est-il possible d'avoir un type anonyme pour implémenter une interface?

J'aimerais travailler sur un code, mais je ne sais pas comment faire cela.

J'ai eu quelques réponses qui disent non, ou créent une classe qui implémente l'interface, construisent de nouvelles instances de cela. Ce n'est pas vraiment idéal, mais je me demande s'il existe un mécanisme permettant de créer une classe dynamique mince au-dessus d'une interface, ce qui simplifierait la tâche.

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 wrapping d'interface dynamique qui décrit une approche. Est-ce la meilleure façon de faire cela?

430
Nick Randell

Non, les types anonymes ne peuvent pas implémenter une interface. Du Guide de programmation C # :

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

338
SpaceghostAli

Même si cette question remonte à deux ans et que les réponses du fil de discussion sont toutes suffisamment vraies, je ne peux résister à l’impulsion de vous dire que cela est en fait possible pour qu'une classe anonyme implémente une interface, même s'il faut un peu de tricherie créative pour y arriver.

En 2008, j’étais en train d’écrire un fournisseur LINQ personnalisé pour mon employeur de l’époque. À un moment donné, j’avais besoin de pouvoir distinguer "mes" classes anonymes d’autres classes anonymes, ce qui voulait dire qu’ils devaient implémenter une interface que je pouvais utiliser pour dactylographier chèque. leur. Nous avons résolu le problème en utilisant des aspects (nous avons utilisé PostSharp ) pour ajouter l'implémentation d'interface directement dans l'IL. Donc, en fait , laisser les classes anonymes implémenter des interfaces est faisable , il vous suffit de légèrement plier les règles pour y arriver.

86
Mia Clarke

Transformer des types anonymes en interfaces est quelque chose que je souhaitais depuis longtemps, mais malheureusement, la mise en œuvre 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 LinF 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>();
43
Arne Claassen

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
Jason Bowers

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

12
Marc Gravell

La meilleure solution consiste simplement à ne pas utiliser les 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 y aurait peut-être une meilleure façon de le faire, mais je ne pouvais pas le trouver.

11
ICR

La réponse à la question spécifiquement posée est non. Mais avez-vous été à la recherche de 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);
}
7
Nine Tails

Une autre option consiste à créer une classe d'implémentation concrète unique qui prend 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 faire est de convertir DummySource en DummyInterface, il serait alors plus simple de n'avoir qu'une seule classe prenant un DummySource dans le constructeur et implémentant l'interface.

Mais, si vous avez besoin de convertir plusieurs types en DummyInterface, la plaque de la chaudière est beaucoup moins puissante.

0
Gordon Bean