2010-07-08 2 views
3

J'ai un ensemble de classes que je n'ai pas écrites, et elles sont en lecture seule. Disons que, pour l'exemple, que ces classes sont les suivantes:C#: polymorphisme dynamique avec des classes non polymorphes

public class Base { } 
public class B : Base { } 
public class C : B { } 
public class D : Base { } 

Je veux ajouter une méthode Foo() sur toutes ces classes, je suis en utilisant des méthodes d'extension:

public static class Extensions { 

    public static void Foo(this Base obj) { 
     dynamic dynobj = obj; 
     try { 
     dynobj.Foo(); 
     } 
     catch (RuntimeBinderException ex) { 
      Console.WriteLine(ex.Message); 
     } 
    } 

    public static void Foo(this B b) { 
     Console.WriteLine("Foo in B"); 
    } 

    public static void Foo(this C c) { 
     Console.WriteLine("Foo in C"); 
    } 

} 

Comme vous peut voir, j'essaie d'utiliser le mot-clé dynamic, s'attendant à connaître le type réel de mon objet et d'appeler sa méthode Foo(). Mais ... dynobj.Foo() échoue toujours. Je sais que je pourrais utiliser le modèle d'adaptateur, mais j'ai vraiment trop de classes.

Est-ce une bonne idée de le faire avec dynamic? Est-il possible de faire ce travail?

Merci.

Répondre

1

je sorte de résolu mon problème en ne gardant que la méthode d'extension « Foo() base et de faire les autres méthodes d'extensions de méthodes statiques régulières prenant B, C ou D comme paramètre.

public static void Foo(this Base obj) { 
     try { 
     Foo_(obj as dynamic); 
     } 
     catch (RuntimeBinderException ex) { 
      Console.WriteLine(ex.Message); 
     } 
    } 

    private static void Foo_(B b) { 
     Console.WriteLine("Foo in B"); 
     b.ListOfBs.ForEach(x => x.Foo()); 
    } 

    private static void Foo_(C c) { 
     Console.WriteLine("C.Name = {0}", c.Name); 
    } 
5

Cela est dû au fait que Foo n'est jamais ajouté en tant que méthode. Les méthodes d'extension ne sont encore que des méthodes statiques dans les classes statiques, pas directement associées à la classe en question. Ils ne sont pas comme des mix-ins (ruby). Le compilateur traduit juste ce qui ressemble à un appel sur votre objet à la méthode statique.

Identique à appeler: Extensions.Foo(thing)

Mon meilleur conseil est de créer une table de recherche (dictionnaire) que vous pouvez enregistrer les différentes versions de Foo.

Quelque chose comme ça (non testé), peut-être?

public static class Extensions { 

    private static Dictionary<Type, Action<Base>> callFoo = new Dictionary<Type, Action<Base>> 
    { 
    {typeof(B), b => (b as B).Foo()}, 
    {typeof(C), b => (b as C).Foo()} 
    }; 

    public static void Foo(this Base obj) { 
     try { 
     callFoo[typeof(obj)](obj); 
     } 
     catch (RuntimeBinderException ex) { 
      Console.WriteLine(ex.Message); 
     } 
    } 

    public static void Foo(this B b) { 
     Console.WriteLine("Foo in B"); 
    } 

    public static void Foo(this C c) { 
     Console.WriteLine("Foo in C"); 
    } 

} 
+0

Oh, d'accord, merci. C'est bien, mais je voulais quelque chose d'un peu plus dynamique. –

+0

Je ne pense pas que vous trouverez une solution dynamique aux méthodes d'extension. :(Je pourrais avoir tort par quelqu'un de plus créatif que moi, cependant :) –

+0

Oui, vous avez raison. Mais j'ai réalisé que Foo() de Base est suffisant car il sera disponible pour tous les autres types qui héritent en quelque sorte de Base. –

0

Il y a encore une autre raison pour laquelle votre solution ne serait pas une bonne idée - Base n'est pas définie comme une personne classe abstraite, donc il y a une chance pourrait instancier et, même si votre technique a travaillé, courir en un débordement de pile s'ils essayaient d'appeler sa méthode Foo(). Si vous supprimez la méthode Foo() de Base, Foo() n'est-il pas appelé comme prévu (l'appel d'une instance de la méthode d'extension Foo() de la classe C finit par atteindre la méthode d'extension Foo() de B)?

Si cela ne fonctionne pas, vous pouvez toujours être démodé et créer des classes wrapper.

+0

Eh bien, mon mauvais, vous avez raison, 'Base' est une classe abstraite, je l'ai laissé de côté quand j'ai simplifié mon exemple. En fait, je veux vraiment la méthode 'Foo()' de 'Base' puisque c'est celle qui fera l'expédition. –