Подтвердить что ты не робот

Почему компилятор позволяет мне вводить нуль для определенного типа в С#?

Рассмотрим этот код:

var str = (string)null;

При написании кода это мой код IL:

IL_0001:  ldnull

И IL имеет оператор Cast, но:

var test = (string) new Object();

Код IL:

IL_0008:  castclass  [mscorlib]System.String

Так что литье null в string было проигнорировано.

Почему компилятор разрешил мне использовать null для определенного типа?

4b9b3361

Ответ 1

В ИЛ на этом уровне null просто null. Компилятор знал, что это было null, потому что это то, что вы написали, поэтому компилятор вообще не требует вызова оператора трансляции. Листинг null для объекта просто даст null.

Итак, это оптимизация времени компиляции или упрощение, если вы это сделаете.

Так как это является законным, для приведения null к другому типу объекта, об этом не сообщается ни предупреждение, ни сообщение об ошибке.

Обратите внимание, что, по-видимому, компилятор этого не сделает, даже подумал, что он может проверить, что фактически выполняемое значение будет null, если оно не является литералом.

Ваш пример:

void Main()
{
    var s = (string)null;
    GC.KeepAlive(s);
}

IL:

IL_0000:  ldnull      
IL_0001:  stloc.0     // s
IL_0002:  ldloc.0     // s
IL_0003:  call        System.GC.KeepAlive

(Я добавил вызов GC.KeepAlive, чтобы компилятор не удалял всю переменную из-за того, что он нигде не использовался.)

Если я сначала вложу null в объект, без возможности его изменения:

void Main()
{
    object o = null;
    var s = (string)o;
    GC.KeepAlive(s);
}

IL:

IL_0000:  ldnull      
IL_0001:  stloc.0     // o
IL_0002:  ldloc.0     // o
IL_0003:  castclass   System.String
IL_0008:  stloc.1     // s
IL_0009:  ldloc.1     // s
IL_000A:  call        System.GC.KeepAlive

Ответ 2

В Java есть хотя бы один случай, когда вам нужно придать null некоторому типу, а именно при использовании перегруженных методов сообщить компилятору, какой метод вы хотите выполнить (я предполагаю, что это имеет место в С# как Что ж). Так как a null - 0 (или любой другой указатель null), независимо от того, какой тип это вы не увидите никакой разницы в скомпилированном коде (кроме того, который был вызван).

Ответ 3

Потому что спецификация говорит так. См. §6.1.5, §6.2 и §7.7.6 стандарта С# 5. Чтобы процитировать только соответствующие части:

§7.7.6 Литые выражения

Листинг-выражение формы (T)E, где T является типом, а E является унарным выражением, выполняет явное преобразование (§6.2) значения E для ввода T. [... T] Результатом является результат, выражаемый явным преобразованием.

§6.2 Явные преобразования

Следующие преобразования классифицируются как явные преобразования:

  • Все неявные преобразования.

§6.1.5 Неявные ссылочные преобразования

Неявные ссылочные преобразования:

  • От нулевого литерала до любого ссылочного типа.

Ответ 4

Задание нулевого значения вполне допустимо - иногда это необходимо при передаче аргументов перегруженным методам, чтобы сообщить компилятору, какой метод вызывается.

См. следующий связанный с этим вопрос:

Кастинг null как объекта?

Ответ 5

Точка броска должна определять str как тип String, поэтому меньше о том, можете ли вы использовать null как тип и больше об определении типа переменной.

Ответ 6

Не все, что выглядит как (SomeType)expression, действительно является отличным, на С#. Иногда выражение должно приобретать тип, которого у него еще не было. Некоторые другие примеры:

var a = (short)42;
var b = (Func<int, int>)(i => i + 1);
var c = (IConvertible)"Hello";

В каждом из этих случаев можно было бы написать тип слева, а не var, если это было предпочтительнее. Но это не всегда так, когда выражение является частью большего выражения, например:

CallOverloadedMethod((short)42); // CallOverloadedMethod has another overload that we don't want
var y = b ? x : (IConvertible)"Hello"; // the ?: operator might not be able to figure out the type without this hint

В вашем примере буквальный null не имеет типа. В некоторых случаях, например, при выборе между многими перегрузками, например, при использовании тернарного оператора ?: или при объявлении переменной с синтаксисом var, необходимо иметь выражение, которое все еще null, но также имеет тип.

Обратите внимание, что перегрузки также включают операторы, например, в:

public static bool operator ==(Giraffe g1, Giraffe g2)
{
  if (g1 == (object)null && g2 != (object)null
    || g1 != (object)null && g2 == (object)null)
  {
    return false;
  }

  // rest of operator body here ...
}

Синтаксис (object)null используется для обеспечения того, чтобы пользовательская перегрузка == не называлась рекурсивно.

Ответ 7

Ваш синтаксис верен, и в С# нет ограничений спецификации. Это правила спецификации:

Неявные ссылочные преобразования:

  • От любого ссылочного типа к объекту и динамическому.

  • От любого класса S до любого класса T, если S получен от T.

  • От любого класса S до любого типа интерфейса T, если S реализует Т.

  • От любого типа интерфейса S до любого типа интерфейса T, если S является полученный из T.

  • Из массива типа S с типом элемента SE в массив типа T с тип элемента TE, при условии, что все следующее верно: o S и T отличаются только типом элемента. Другими словами, S и T имеют одинаковые количество измерений. o SE и TE являются ссылочными типами. o неявное преобразование ссылок существует от SE до TE.

  • От любого типа массива до System.Array и интерфейсов инвентарь.

  • Из одномерного массива типа S [] в System.Collections.Generic.IList и его базовые интерфейсы, предоставленные что существует неявное преобразование идентичности или ссылки с S на Т.

  • От любого типа делегата до System.Delegate и его интерфейсов инвентарь.

  • От нулевого литерала до любого ссылочного типа.

  • От любого ссылочного типа к ссылочному типу T, если он имеет неявный идентичность или ссылочное преобразование в ссылочный тип T0 и T0 имеет преобразование идентичности в T.

  • От любого ссылочного типа до интерфейса или типа делегата T, если он имеет неявное преобразование идентичности или ссылки в интерфейс или тип делегата T0 и T0 является дисперсионно-конвертируемым (§13.1.3.2) в T.

  • Неявные преобразования, включающие параметры типа, которые, как известно, ссылочные типы. Более подробную информацию о неявных преобразованиях см. В разделе 6.1.1.10 с использованием параметров типа. Неявные ссылочные преобразования эти преобразования между ссылочными типами, которые можно доказать всегда преуспеть, и поэтому не требуют проверок во время выполнения. Справка преобразования, неявные или явные, никогда не изменяют ссылочный идентичность преобразуемого объекта. Другими словами, хотя обращение ссылки может изменить тип ссылки, это никогда изменяет тип или значение объекта, на который ссылается.

Ответ 8

Предположим, что компилятор возвращает предупреждение для var str = (string)null;. Поэтому, если эта строка будет предупреждена?: var str = SomeFunctionThatReturnsNull() При использовании компилятора var должен знать, какой тип он должен инициализировать во время компиляции, а также не имеет значения, хотите ли вы поместить в него нулевое значение, если оно объявленный как нулевой тип. Нет ничего удивительного в том, что компилятор не звонит в листинг в нулевом случае, потому что нечего делать.

Ответ 9

Я думаю, вы должны прочитать спецификацию. Вы можете указать нулевое значение для всех, что захотите. Посмотрите:

• От нулевого литерала до любого ссылочного типа.

Прежде чем использовать значение, которое вы проверяете для null.