2011-12-15 4 views
2

Je n'ai jamais fait beaucoup de travail avec les opérateurs de surcharge, en particulier les conversions implicites et explicites.Opérateur explicite et implicite avec des types numériques et des résultats inattendus

Cependant, j'ai plusieurs paramètres numériques qui sont fréquemment utilisés, donc je crée une structure comme un wrapper autour d'un type numérique pour taper fortement ces paramètres. Voici un exemple d'implémentation:

public struct Parameter 
{ 
    private Byte _value; 
    public Byte Value { get { return _value; } } 

    public Parameter(Byte value) 
    { 
     _value = value; 
    } 

    // other methods (GetHashCode, Equals, ToString, etc) 

    public static implicit operator Byte(Parameter value) 
    { 
     return value._value; 
    } 
    public static implicit operator Parameter(Byte value) 
    { 
     return new Parameter(value); 
    } 

    public static explicit operator Int16(Parameter value) 
    { 
     return value._value; 
    } 
    public static explicit operator Parameter(Int16 value) 
    { 
     return new Parameter((Byte)value); 
    } 
} 

Comme j'expérimentais avec ma mise en œuvre de test pour obtenir un blocage des opérateurs explicites et implicites, j'ai essayé de jeter explicitement Int64 à mon type Parameter et à ma grande surprise, il n'a pas jeté une exception, et encore plus surprenant, il a juste tronqué le nombre et a évolué. J'ai essayé d'exclure l'opérateur explicite personnalisé et il se comportait toujours de la même manière.

public void TestCast() 
{ 
    try 
    { 
     var i = 12000000146; 
     var p = (Parameter)i; 
     var d = (Double)p; 

     Console.WriteLine(i); //Writes 12000000146 
     Console.WriteLine(p); //Writes 146 
     Console.WriteLine(d); //Writes 146 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); //Code not reached 
    } 
} 

Je répète mon expérience avec une Byte plaine en place de mon struct et a le même comportement exact, donc de toute évidence ce comportement est normal, mais je pensais un casting explicite qui se traduit une perte de données jetterait une exception.

+0

Non, moulages explicites peuvent perdre des informations. Les conversions implicites ne devraient pas. –

+0

Compilez-vous avec 'AnyCPU',' x32' ou 'x64'? – ja72

+0

@ ja72 Désolé pour le délai de réponse, mais je compile avec x86 – psubsee2003

Répondre

7

Lorsque le compilateur est d'analyser une conversion explicite définie par l'utilisateur il est permis de mettre explicite conversion intégré dans sur « autre » (ou les deux) de la conversion. Ainsi, par exemple, si vous avez une conversion définie par l'utilisateur de int à Fred, et vous avez:

int? x = whatever; 
Fred f = (Fred)x; 

alors les raisons du compilateur « il y a une conversion explicite de int à Fred, afin que je puisse faire une explicite conversion de int? en int, puis conversion int en Fred

Dans votre exemple, il y a une conversion explicite intégrée de long en court, et il y a une conversion explicite définie par l'utilisateur de court à Paramètre, donc La conversion de long en paramètre est légale

La même chose est vraie pour les conversions implicites: le compilateur peut insérer des conversions implicites intégrées sur e. côté d'une conversion implicite définie par l'utilisateur.

Le compilateur n'enchaîne jamais deux conversions définies par l'utilisateur. Construire vos propres conversions explicites correctement est une tâche difficile en C#, et je vous encourage à cesser d'essayer de le faire jusqu'à ce que vous ayez une compréhension approfondie et approfondie du chapitre entier de la spécification qui couvre les conversions.

Pour certains aspects intéressants de conversions enchaînées, voir mes articles sur le sujet:

http://blogs.msdn.com/b/ericlippert/archive/2007/04/16/chained-user-defined-explicit-conversions-in-c.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/04/18/chained-user-defined-explicit-conversions-in-c-part-two.aspx

+0

Merci pour l'explication et les liens vers votre blog. J'ai raté certains des anciens posts, donc c'était très instructif. Le projet sur lequel je travaille est en quelque sorte un projet que j'utilise autant pour expérimenter de nouveaux aspects que j'utilise pour aider dans mon vrai travail, donc les conversions explicites/implicites nous autant expérimentons que n'importe quoi. – psubsee2003

+0

Et maintenant que j'ai lu votre blog, je pense comprendre les pièges des conversions explicites et implicites. De votre réponse, j'obtiens qu'il y a une conversion explicite entre long et octet, le compilateur insère cette conversion dans l'appel de méthode pour convertir de long en octet en paramètre. Et puisque la distribution de long en octet prendra les 8 bits les moins significatifs de la longueur, il me reste 146 avec la valeur assignée au paramètre. Donne un sens complet, merci. – psubsee2003

3

Cet objectif:

si je crée un struct comme une enveloppe autour d'un type numérique pour typer ces paramètres

Et ce code:

public static implicit operator Byte(Parameter value) 
{ 
    return value._value; 
} 
public static implicit operator Parameter(Byte value) 
{ 
    return new Parameter(value); 
} 

sont en contradiction totale. En ajoutant des opérateurs implicites à 2 voies, vous annulez toute sécurité de type que l'encapsuleur pourrait apporter.

Donc, supprimez les conversions implicites. Vous pouvez les changer en explicit.

+0

@Hank Je n'étais pas assez clair dans la description de la sécurité de type que je cherchais. Il y a plusieurs types de paramètres différents (pour l'argument, Parameter1, Parameter2, etc.). Le type de sécurité que je recherchais empêche quelqu'un d'utiliser accidentellement Parameter1 dans une méthode qui recherche Parameter2. Mais votre point a du sens malgré tout, donc je vais repenser l'approche de l'opérateur implicite. – psubsee2003

Questions connexes