2008-10-08 13 views
2

j'ai une classe qui a un type générique « G »compilateur ne parvient pas la conversion d'un type générique contraint

Dans mon modèle de classe j'ai

public class DetailElement : ElementDefinition 

Disons que j'ai une méthode comme celui-ci

 public void DoSomething<G>(G generic) 
      where G : ElementDefinition 
     { 
      if (generic is DetailElement) 
      { 
       ((DetailElement)generic).DescEN = "Hello people"; //line 1 
       ////// 
       ElementDefinition element = generic; 
       ((DetailElement)element).DescEN = "Hello again"; //line 3 
       ////// 
       (generic as DetailElement).DescEN = "Howdy"; //line 5 
      } 
      else 
      { 
       //do other stuff 
      } 
     } 

compilateur signale une erreur dans la ligne 1:

Cannot convert type 'G' to 'DetailElement' 

Mais la ligne 3 fonctionne bien. Je peux résoudre ce problème en faisant le code écrit dans la ligne 5.

Ce que je voudrais savoir est pourquoi le compilateur signale l'erreur dans la ligne 1 et non celle de la ligne 3, étant donné que, dans la mesure comme je le sais, ils sont identiques.

modifier: Je crains que je pourrais être absent une partie importante de la logique du cadre

de Edit2: Bien que les solutions pour l'erreur du compilateur sont importants, ma question est de savoir pourquoi le compilateur signale une erreur sur la ligne 1 et pas dans la ligne 3.

Répondre

7

Si G a été contraint d'être un DetailElement (where G : DetailElement), vous pouvez aller de l'avant et jeter G à ElementDefinition, à savoir "(ElementDefinition) generic". Mais parce que G pourrait être une autre sous-classe de ElementDefinition autre que DetailElement au moment de l'exécution, il ne l'autorisera pas à la compilation lorsque le type est inconnu et invérifiable.

Dans la ligne 3 du type que vous lancez de est connu pour être un ElementDefinition tout ce que vous faites est un up moulé sous pression. Le compilateur ne sait pas si ce sera une distribution réussie au moment de l'exécution, mais il vous fera confiance. Le compilateur n'est pas si confiant pour les génériques.L'opérateur as de la ligne 5 peut également renvoyer une valeur null et le compilateur ne vérifie pas statiquement le type pour voir s'il est sûr dans ce cas. Vous pouvez utiliser as avec n'importe quel type, et pas seulement ceux qui sont compatibles avec ElementDefinition.

De Can I Cast to and from Generic Type Parameters? sur MSDN:

Le compilateur ne vous laissera implicitement converti paramètres de type générique à l'objet ou à la contrainte spécifiées types.

Cette coulée implicite est de type cours en toute sécurité, car toute incompatibilité est découverte à la compilation.

Le compilateur vous permettra de convertir explicitement les paramètres de type générique à toute interface, mais pas à une classe:

interface ISomeInterface {...} 
    class SomeClass {...} 
    class MyClass<T> 
    { 
     void SomeMethod(T t) 
     { 
     ISomeInterface obj1 = (ISomeInterface)t;//Compiles 
     SomeClass  obj2 = (SomeClass)t;  //Does not compile 
     } 
    } 

Cependant, vous pouvez forcer une distribution à partir d'un paramètre de type générique à tout autre type en utilisant un temporaire objet variable

void SomeMethod<T>(T t) 
    { object temp = t; 
    MyOtherClass obj = (MyOtherClass)temp; 
    } 

Inutile de dire que cette coulée explicite est dangereuse, car elle peut lancer une exception au moment de l'exécution si le type de béton utilisé à la place du paramètre de type générique ne dérive pas du type que vous explicitement jeté à. Au lieu de risquer une exception de diffusion, une meilleure approche consiste à utiliser les opérateurs is ou as. L'opérateur is renvoie true si le paramètre de type générique est du type interrogé et as effectuera une conversion si les types sont compatibles et retournera une valeur nulle dans le cas contraire.

public void SomeMethod(T t) 
{ 
    if(t is int) {...} 

    string str = t as string; 
    if(str != null) {...} 
} 
1

Votre clause where ne devrait-elle pas être "où G: DetailElement"?

Dans le code que vous avez écrit, DetailElement est une ElementDefinition, mais une ElementDefinition n'est pas nécessairement un DetailElement. Donc la conversion implicite est illégale.

Existe-t-il d'autres types de ElementDefinition que vous pourriez transmettre à cette méthode? Si c'est le cas, ils lèvent une exception lorsque vous essayez de les convertir en instances DetailElement.

EDIT:

Bon, alors maintenant que vous avez changé votre liste de code, je peux voir que vous vérifiez le type pour vous assurer qu'il est vraiment un DetailElement avant d'entrer dans ce bloc de code. Malheureusement, le fait est que vous ne pouvez pas implicitement downcast, même si vous avez déjà vérifié les types vous-même. Je pense que vous devriez vraiment utiliser le « comme » mot-clé au début de votre bloc:

DetailElement detail = generic as DetailElement; 
if (detail == null) { 
    // process other types of ElementDefinition 
} else { 
    // process DetailElement objects 
} 

Mieux encore, pourquoi ne pas utiliser le polymorphisme pour permettre à chaque type de ElementDefinition de définir sa propre méthode DoSomething, et laisser le CLR prendre soin de la vérification de type et de l'invocation de méthode pour vous?

+0

Par souci de simplicité, je ne l'ai pas mis le tout code ici. je vais l'éditer pour être correct –

0

Cela conduira à un peu plus de code si vous avez beaucoup de ElementDefinitions vous êtes inquiet, mais est probablement le slickest vous obtiendrez qui n'implique pas est alors comme un non-sens.

public void DoSomething<G>(G generic) 
     where G : ElementDefinition 
    { 
     DetailElement detail = generic as DetailElement; 
     if (detail != null) 
     { 
      detail.DescEN = "Hello people"; 
     } 
     else 
     { 
      //do other stuff 
     } 
    } 

Une autre solution possible que je l'ai utilisé quand je avais besoin ces informations, Loo d'une variable d'objet temporaire.

DetailElement detail = (DetailElement)(object)generic; 

Il fonctionne, mais comme forme est probablement le meilleur.

1

Généralement, le upcasting est une odeur de code. Vous pouvez l'éviter en surchargeant la méthode. Essayez ceci:

public void DoSomething(DetailElement detailElement) 
{ 
    // do DetailElement specific stuff 
} 

public void DoSomething<G>(G elementDefinition) 
    where G : ElementDefinition 
{ 
    // do generic ElementDefinition stuff 
} 

Vous pouvez alors profiter de la surcharge de méthode en utilisant ce code:

DetailElement foo = new DetailElement(); 

DoSomething(foo); // calls the non-generic method 
DoSomething((ElementDefinition) foo); // calls the generic method