web-dev-qa-db-fra.com

Passer des paramètres C # qui peuvent "s'adapter" à une interface, mais ne l'implémentent pas réellement

Remarque: je sais que c'est une idée horrible dans la pratique; je suis simplement curieux de savoir ce que le CLR vous permet de faire, dans le but de créer une sorte de préprocesseur "modifier une classe après l'avoir créée". =

Supposons que j'ai la classe suivante, qui a été définie dans un autre assembly, donc je ne peux pas la changer.

class Person {
    public string Greet() => "Hello!";
}

Je définis maintenant une interface et une méthode, comme celle-ci:

interface IGreetable {
    string Greet();
} 

// ...

void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());

La classe Person n'implicite pas explicitement IGreetable, mais elle pourrait se passer de toute modification de ses méthodes.

Avec cela, existe-t-il un moyen quelconque, en utilisant Reflection, le DLR ou autre chose, dans lequel une instance de Person pourrait être passée avec succès à PrintGreeting sans modifier le code ci-dessus?

33
Aaron Christiansen

Essayez d'utiliser la bibliothèque Impromptu-Interface

[Le Impromptu-Interface] framework pour vous permettre d'envelopper n'importe quel objet (statique ou dynamique) avec une interface statique même s'il n'en a pas hérité. Pour ce faire, il émet du code de liaison dynamique mis en cache dans un proxy.

Cela vous permet de faire quelque chose comme ceci:

var person = new Person();
var greeter = person.ActLike<IGreetable>();
29
Roy Sanchez

Vous pouvez utiliser un objet wrapper dynamic pour câbler cela vous-même, mais vous perdez la sécurité de type dans la classe wrapper:

class GreetableWrapper : IGreetable
{
    private dynamic _wrapped;
    public GreetableWrapper(dynamic wrapped)
    {
        _wrapped = wrapped;
    }

    public string Greet()
    {
        return _wrapped.Greet();
    }
}

static void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());
static void Main(string[] args)
{
    PrintGreeting(new GreetableWrapper(new Person()));
    Console.ReadLine();
}
7
John Koerner

Cela pourrait être assez facile bientôt. Les classes de types peuvent être introduites dans C # comme formes où vous pourrez définir les fonctionnalités d'une classe et coder par rapport à cela forme puis utilisez votre code avec n'importe quel type qui correspond sans que l'auteur de ce code ait à déclarer quoi que ce soit, à peu près comme vous le décrivez.

La chose la plus proche en C # en ce moment est peut-être comment foreach fonctionne avec un type qui a une GetEnumerator() renvoyant un objet d'un type avec une MoveNext() et Current même s'ils n'implémentent pas IEnumerable etc. uniquement alors que c'est un concept intégré que le compilateur traite, vous pouvez les définir ici.

Fait intéressant, il vous permettra également de définir des membres statiques.

6
Jon Hanna

Je ne pense pas que ce soit possible. Le compilateur doit voir quelque chose qui implémente explicitement l'interface ou la classe afin que le compilateur puisse confirmer que tout est implémenté.

Si vous pouviez le faire en utilisant la redirection, vous pourriez échouer à implémenter quelque chose. Et cela va à l'encontre de l'approche de sécurité adoptée par .NET.

4
Jonathan Wood

Si vous avez le contrôle du code externe et que vous êtes prêt à envelopper l'objet (et il semble que toutes les réponses sont ici enveloppées), la liaison dynamique et les bibliothèques comme Impromptu-Interface me semblent beaucoup de mal pour quelque chose qui est essentiellement un bon mot.

class GreetablePerson : Person, IGreetable { }

Et tu as fini.

Lorsque le compilateur crée la classe GreetablePerson, la méthode de Person finit par faire une implémentation implicite de l'interface, et tout "fonctionne juste". La seule irritation est que le code extérieur doit instancier GreetablePerson objets, mais dans la terminologie orientée objet standard, une instance de GreetablePersonis une instance de Person, cela me semble donc être une réponse valable à la question posée.

Si les exigences sont modifiées de telle sorte que vous avez également des instances préexistantes de Person, alors quelque chose comme Impromptu-Interface peut devenir plus tentant, mais même alors, vous voudrez peut-être envisager de donner à GreetablePerson un constructeur qui copie à partir de Person. Choisir le meilleur chemin à suivre à partir de là nécessite d'obtenir plus de détails sur les exigences et les détails d'implémentation réels de la classe Personne en question.

4
GrandOpener

Une option crée une classe wrapper sur la personne et transmet ce wrapper à la méthode, le wrapper doit implémenter explicitement l'interface.

4
Mauri

Dans une sorte de non apparenté, c'est quelque chose qui est couramment fait dans d'autres langues, comme Scala et Haskell.

Il est connu comme utilisant ce qu'on appelle des "classes de type". Les classes de types vous permettent essentiellement de définir le comportement d'un type comme s'il implémentait explicitement une interface, sans pour autant l'exiger. Vous pouvez en savoir plus ici .

1
wheeler