Почему компилятор позволяет мне привести null к определенному типу В C#?

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

var str = (string)null;

когда пишете код это мой IL код:

IL_0001:  ldnull

и IL имеет любой оператор Cast, но:

var test = (string) new Object();

на IL код:

IL_0008:  castclass  [mscorlib]System.String

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

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

9 ответов


в IL на этом уровне,null это просто null. Компилятор знал, что это null потому что это то, что вы написали, как таковой компилятору не нужно вызывать оператор cast вообще. Кастинг 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

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


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

§7.7.6 литой выражения

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

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

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

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

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

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

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

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

посмотреть следующие вопросы:

приведение null в качестве объекта?


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


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

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 ...
}

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


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

неявного преобразования ссылки:

  • от любого ссылочного типа к Object и Dynamic.

  • от любого типа класса S к любому классу-типа T, если происходит от Т.--1-->

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

  • от любого типа интерфейса S к любому типу интерфейса T, при условии, что S полученные от Т.

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

  • из любого типа массива в систему.Массив и интерфейсы it инвентарь.

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

  • из любого типа делегата в систему.Делегатов и интерфейсов инвентарь.

  • от значение null для любого ссылочного типа.

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

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

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


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


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

• от значение null для любого ссылочного типа.

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