51 votes

En C # / .NET, pourquoi sbyte [] est-il identique à byte [] sauf que ce n'est pas le cas?

Je viens d’observer un phénomène étrange en C # / .NET.

J'ai créé cet exemple minimal pour démontrer:

 if (new sbyte[5] is byte[])
{
 throw new ApplicationException("Impossible!");
}

object o = new sbyte[5];

if (o is byte[])
{
 throw new ApplicationException("Why???");
}
 

Cela jettera "Pourquoi ???", mais pas "Impossible!". Cela fonctionne pour tous les tableaux de types intégraux de la même taille. Quelqu'un peut m'expliquer cela? Je suis confus. J'utilise .NET 4 au fait.

PS: Je sais que je peux obtenir le résultat attendu en utilisant o.GetType() == typeof(byte[]) .

51voto

usr Points 74796

Le CLR règles de la coulée de préciser que cela est possible. Le C# règles de dire qu'il n'est pas possible. L'équipe C#, consciemment décidé qu'ils seraient tolérer cette déviation de la spec pour diverses raisons.

Pourquoi ne le CLR permettre cela? Probablement parce qu'ils peuvent facilement mettre en œuvre. byte et sbyte ont la même représentation binaire de sorte que vous pouvez "traiter" un byte[] comme sbyte[] sans porter atteinte à la mémoire de la sécurité.

La même astuce fonctionne pour les autres types de primitives avec la même disposition de la mémoire.

28voto

vcsjones Points 51910

C'est drôle, je suis mordu par que dans ma question, Pourquoi est-ce que Linq Fonte d'Échouer lors de l'utilisation de ToList?

Jon Skeet (bien sûr) explique que mon problème est le compilateur C#, pour quelque raison que ce soit, pense qu'ils ne pourraient jamais être la même chose, et utilement optimise la valeur false. Cependant, le CLR ne laisser cela se produire. Le casting pour objet déclenche l'optimisation du compilateur, de sorte qu'il passe par le CLR.

La partie pertinente de sa réponse:

Même si en C# vous ne pouvez pas jeter un byte[] pour un sbyte[] directement, le CLR permet:

var foo = new byte[] {246, 127};
// This produces a warning at compile-time, and the C# compiler "optimizes"
// to the constant "false"
Console.WriteLine(foo is sbyte[]);

object x = foo;
// Using object fools the C# compiler into really consulting the CLR... which
// allows the conversion, so this prints True
Console.WriteLine(x is sbyte[]);

Joel posé une question intéressante dans les commentaires, "Est-ce un comportement contrôlé par le Optimiser le Code du drapeau (/o pour le compilateur)?"

Compte tenu de ce code:

static void Main(string[] args)
{
    sbyte[] baz = new sbyte[0];
    Console.WriteLine(baz is byte[]);
}

Et compilé avec csc /o- Code.cs (ne pas optimiser), il semble que le compilateur optimise de toute façon. L'résultant de l'IL:

IL_0000:  nop
IL_0001:  ldc.i4.0
IL_0002:  newarr     [mscorlib]System.SByte
IL_0007:  stloc.0
IL_0008:  ldc.i4.0
IL_0009:  call       void [mscorlib]System.Console::WriteLine(bool)
IL_000e:  nop
IL_000f:  ret

IL_0008 charges 0 (false) directement sur la pile, puis appelle WriteLine sur IL_0009. Donc, non, l'option d'optimisation ne permet pas de faire une différence. Si le CLR devait être consulté, l' isinst enseignement utilisées. Il serait probablement ressembler à quelque chose comme ceci à partir de IL_0008:

IL_0008:  ldloc.0
IL_0009:  isinst     uint8[]
IL_000e:  ldnull
IL_000f:  cgt.un
IL_0011:  call       void [mscorlib]System.Console::WriteLine(bool)

Je suis d'accord avec l'optimiseur de comportement. L'option d'optimisation ne doit pas modifier le comportement de votre programme.

2voto

Mark Hurd Points 4746

VB.NET "jette" en fait lors de la compilation :

L'expression de type 'tableau à 1 dimension de SByte' ne peut jamais être de type 'tableau à 1 dimension de Byte'.

sur l’équivalent du premier relevé if .

Et l'équivalent du deuxième if réussit (c'est-à-dire qu'il lève l'exception codée) à l'exécution, comme prévu, car il s'agit du même CLR.

1voto

romkyns Points 17295

Voici un exemple simple qui montre la même question:

static void Main(string[] args)
{
    bool a = ((object) new byte[0]) is sbyte[];
    bool b = (new byte[0]) is sbyte[];

    Console.WriteLine(a == b); // False
}

La contradiction car le compilateur C# décide qu'il connaît le résultat de l' (new byte[0]) is sbyte[] au moment de la compilation, et juste substituts false. Peut-être qu'il devrait vraiment se substituer true, pour plus de cohérence avec le CLR comportement.

Aussi loin que je peux dire, c'est seulement de cette petite optimisation qui n'est pas cohérent. Il se produit uniquement lorsque les deux côtés de l' is expression sont statiquement typé comme un tableau dont le type d'élément est un entier signé ou non signé ou un enum, et les tailles de tous les nombres entiers sont les mêmes.

La bonne nouvelle, c'est que même si cela peut sembler contradictoire, C# sera toujours émettre un avertissement quand il substitue false dans de telles expressions – dans la pratique, je pense que cela pourrait être plus utile que tranquillement en rentrant true.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X