2009-08-27 7 views
3

J'ai une propriété qui retourne une interface. Pendant le débogage, je peux casser ce qui a été retourné et tant que c'est l'interface, Visual Studio est assez intelligent pour connaître le type dérivé qu'il est réellement. Je suppose que c'est en utilisant la réflexion ou quelque chose. Je ne suis pas sûr. Ma question est, est-ce que je peux avoir cette même information disponible à moi pendant l'exécution ainsi je peux créer une variable du type approprié et jeter l'interface comme cela? Voici ce que je dis:Comment lancer une interface en tant que type dans C#?

IPreDisplay preDisplay = cb.PreDisplay; 

Si preDisplay est un RedPreDisplay je voudrais être capable de coder

RedPreDisplay tmp = preDisplay as RedPreDisplay; 

Ou si preDisplay était un GreenPreDisplay ...

GreenPreDisplay tmp = preDisplay as GreenPreDisplay; 

etc ... Je voudrais éviter une déclaration de commutation désordonnée si possible, et si je pouvais utiliser des génériques, ce serait génial.

Si vous avez des conseils ou des exemples de comment je peux le faire, s'il vous plaît partager.

+9

pourquoi voudriez-vous faire cela? L'interface de retour n'est-elle pas perdue lorsque vous faites un cast à un type correct, quand vous voulez l'utiliser? – shahkalpesh

+2

Votre question révèle une confusion fondamentale, (ou vous êtes bâclé dans le langage) rien ne peut «retourner» une interface, car les interfaces n'existent pas à l'exécution. Il retourne un objet de type concret, 'CAST' à une interface. Mais la distribution ne change pas l'objet, elle change simplement la nature de la variable contenant la référence à l'objet. –

+0

@Charles Bretana: Il est regrettable que vous ressentiez le besoin d'énoncer ce que vous venez de faire; trop de gens ne comprennent pas la différence entre l'instance d'objet et le type contre lequel elle est lancée. – Randolpho

Répondre

4

Selon ce que vous essayez de faire, vous devriez probablement ajouter une méthode/propriété d'action à l'interface, de cette façon vous n'avez pas besoin de connaître le type - qui est le polymorphisme.

par exemple:

IResultLiteEntity preDisplay = cb.PreDisplay; 
preDisplay.Render(); // New Render method on the interface... 
17

Quand vous frappez une situation où vous avez besoin de faire cela, cela signifie que vous faites quelque chose de mal. Vous devez sauvegarder et comprendre pourquoi votre conception exige que vous le fassiez. Si vous vous trouvez coincé là, je vous recommande fortement de poster une nouvelle question pour obtenir de l'aide pour la conception - il y a beaucoup de gens intelligents ici qui peuvent vous aider.

Pour répondre à votre question directement, non - vous ne pouvez pas le faire sans une sorte de if/else ou conditionnel, car vous devez être explicite avec des types statiques. Vous pouvez utiliser la réflexion pour appeler la méthode, mais puisque vous semblez avoir besoin d'appeler quelque chose que l'interface ne supporte pas - mais certains objets le font - vous devrez quand même coder une condition de type statique pour appeler cette méthode. Il suffit de coder les types directement.

Modifier: par la discussion dans les commentaires, la meilleure solution à ceci est d'ajouter une deuxième interface aux classes qui ont cette autre propriété ou méthode. Ensuite, vous pouvez faire une simple vérification:

IPreDisplay display = cb.PreDisplay; 
IOtherInterface displayAsOther = display as IOtherInterface; 
if(displayAsOther != null) 
{ 
    displayAsOther.OtherMethod(); 
} 
+1

Il est possible sans un interrupteur/Sinon, dans C# 4.0, vous pouvez utiliser le mot-clé dynamic pour envoyer à l'exécution un type à une méthode avec une signature spécifique. – LBushkin

+0

@LBushkin très vrai, mais C# 4.0 n'est pas encore publié et il n'est pas raisonnable de supposer que tout le monde qui a des problèmes 4 peut résoudre peut simplement passer à la version bêta. Ou même quand c'est RTM. –

+0

Et même s'il est possible de faire en C# 4, ce n'est probablement pas une bonne idée. Même dans les langages qui le supportent, comme VBScript, je ne l'utilise jamais. – Guffa

6

Le but entier d'utiliser des interfaces est que le code d'exécution ne doit pas être au courant du type exact. Essayez d'exposer toutes les informations dont vous pourriez avoir besoin via l'interface elle-même, de sorte que vous n'avez pas besoin de lancer. Naturellement, il se peut que vous ayez encore besoin de lancer une interface vers une implémentation concrète (type spécifique) en de rares occasions. Si vous pouviez fournir un peu plus de contexte, cela pourrait être utile.

3

@Rex M est absolument correct. Le problème réside avec votre code et la structure sous-jacente. En règle générale, vous ne devriez pas faire ce que vous essayez de faire; code contre l'interface seulement. Cela dit, il y a l'opérateur is qui pourrait vous aider si vous avez hérité de mauvais code et que vous avez besoin de le corriger.Par exemple:

if(myInstance is MyBaseType) 
{ 
    MyBaseType myInstanceAsBaseType = myInstance as MyBaseType; 
    // handle MyBaseType specific issue 
} 
else if(myInstance is MyOtherBaseType) 
{ 
    MyOtherBaseType myInstanceAsOtherBaseType = myInstance as MyOtherBaseType; 
    // handle MyOtherBaseType specific issue. 
} 

Les génériques ne vous aideront pas, et vous ne pourrez pas le faire dans le cadre d'une instruction switch. Mais ça va vous donner quelque chose qui fonctionne, mais qui fonctionne de façon très laide.

3

Comme d'autres répondants l'ont souligné, vous devriez probablement réfléchir à la raison pour laquelle votre conception requiert une logique différente pour différents types qui ne peuvent pas être retirés dans une interface.

Cependant, en supposant qu'il ya de bonnes raisons pour cela, vous avez seulement quelques options:

  1. réflexion utilisation. Il s'agit généralement d'un code lent et sujet aux erreurs, qui est également fragile lorsque votre implémentation change (par exemple, les méthodes sont renommées, etc.).
  2. Utilisez un modèle if/else if/else à distribuer en fonction des vérifications d'exécution du type. C'est à peu près votre seul autre choix dans les versions antérieures à la version 4.0 de C#.
  3. Si vous utilisez C# 4.0, vous pouvez utiliser l'objet attribuer à un var dynamique et à l'envoi d'exécution à une méthode surchargée dont la signature varie pour chacun des types pris en charge (voir exemple ci-dessous).

Voici un exemple de répartition dynamique C# 4.0:

void Foo() 
{ 
    dynamic preDisplay = cb.PreDisplay; 
    DoSomethingWith(preDisplay); // runtime dispatch using dynamic runtime (DLR) 
} 

void DoSomethingWith(RedPreDisplay r) { ... } // code specific to RefPreDisplay 
void DoSomethingWith(GreenPreDisplay g) { ... } // code specific to GreenPreDisplay 
void DoSomethingWIth(IPreDisplay o) { ... } // catch-all 
Questions connexes