В C#/.NET почему sbyte[] такой же, как byte [], за исключением того, что это не так?


Я только что наблюдал странное явление в C#/. NET.

Я создал этот минимальный пример, чтобы продемонстрировать:

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

object o = new sbyte[5];

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

это будет бросать "почему???", но не "невозможно!". Он работает для всех массивов интегральных типов одинакового размера. Может кто-нибудь объяснить мне это? Я в замешательстве. Кстати, я использую .NET 4.

P. S.: Я знаю, что я могу получить ожидаемый результат с помощью o.GetType() == typeof(byte[]).

4   51  

4 ответа:

правила приведения среды CLR указывают, что это возможно. Правила C# говорят, что это невозможно. команда C# сознательно решила что они будут терпеть это отклонение от спецификации по разным причинам.

Почему CLR позволяет это? Наверное, потому что они могут это удобно реализовать. byte и sbyte имеют то же двоичное представление, так что вы можете "лечить" a byte[] как sbyte[]без нарушения безопасности памяти.

в тот же трюк работает для других примитивных типов с той же компоновкой памяти.

забавно, я был укушен этим в моем вопросе,почему этот Linq Cast терпит неудачу при использовании ToList?

Джон Скит (конечно) объясняет, что моя проблема заключается в том, что компилятор C# по какой-то причине считает, что они никогда не могут быть одинаковыми, и услужливо оптимизирует его до false. Однако, CLR тут пусть это произойдет. Приведение к объекту отбрасывает оптимизацию компилятора, поэтому она проходит через среду CLR.

соответствующие часть от ответ:

несмотря на то, что в C# вы не можете напрямую передать байт[] в sbyte [], среда CLR позволяет это:

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[]);

Джоэл задал интересный вопрос в комментариях: "это поведение контролируется флагом оптимизации кода (/o для компилятора)?"

учитывая этот код:

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

и составлен с csc /o- Code.cs (не оптимизировать), похоже, что компилятор оптимизирует его в любом случае. В результате Ил:

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 загружает 0 (false) непосредственно в стек, а затем вызывает WriteLine на IL_0009. Так что нет, флаг оптимизации не имеет значения. Если среда CLR были на консультации, на isinst инструкция будет привыкнуть. Вероятно, это будет выглядеть примерно так, начиная с 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)

Я бы согласился с поведением оптимизатора. Флаг оптимизации не должен изменять поведение программы.

VB.NET на самом деле" бросает " на compile время:

выражение типа ' 1-мерный массив SByte 'никогда не может быть типа'1-мерный массив байт'.

на эквиваленте первого if заявление.

и второй if успешно (т. е. он выдает кодированное исключение) во время выполнения, как ожидалось, потому что это та же среда CLR.

вот более простой пример, который показывает ту же проблему:

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
}

несогласованность возникает потому, что компилятор C# решает, что он знает результат (new byte[0]) is sbyte[] во время компиляции, а просто заменяет false. Возможно, он действительно должен заменить true, чтобы быть более совместимым с поведением среды CLR.

насколько я могу судить, это только эта небольшая оптимизация, которая непоследовательна. Это происходит только тогда, когда обе стороны is выражения статически типизированы как массив, тип элемента которого является целым числом со знаком или без знака или перечислением, и размеры целых чисел одинаковы.

хорошая новость заключается в том, что, хотя это может показаться непоследовательным, C# всегда будет выдавать предупреждение, когда он заменяет false в такие выражения-на практике, я думаю, что это может быть более полезным, чем спокойно вернуться true.