Roslyn C#6? Компиляция оператора выглядит беспорядочно в универсальных типах
Давайте рассмотрим следующее определение класса:
public class MyClass<T>
{
public T t;
public bool? c1(T obj) => obj?.Equals(null);
public bool? c2() => t?.Equals(null);
}
В конце концов, некоторые заметки:
MyClass<T>
не накладывать никаких ограничений наT
типа - так,T
может бытьclass
илиstruct
;c2() == c1(t)
ДОЛЖЕН БЫТЬ всегда верным.- Я использую сайт http://tryroslyn.azurewebsites.net/ чтобы скомпилировать некоторые фрагменты кода и посмотреть, что выделяет Рослин.
Теперь давайте проанализируем, как компилирует Roslyn MyClass<T>
:
1.) c1(T)
дело:
Как и ожидалось, после проверки сгенерированного кода компилятором Roslyn, мы можем увидеть следующее:
public bool? c1(T obj)
{
return obj != null ? new bool?(obj.Equals(null)) : null;
}
2.) c2()
дело:
То, что я ожидал, было таким же кодом, как c1(T). Но то, что я вижу, это:
public unsafe bool? c2()
{
T* arg_33_0 = ref this.t;
T t = default(T);
bool? arg_43_0;
if (t == null)
{
t = this.t;
arg_33_0 = ref t;
if (t == null)
{
arg_43_0 = null;
return arg_43_0;
}
}
arg_43_0 = new bool?(arg_33_0.Equals(null));
return arg_43_0;
}
ВАУ, почему выдается весь этот ненужный код? В режиме компиляции релиза мы видим, что код C1 имеет размер кода 39 байт, а метод C2 - 68 байтов. Это то, что можно оптимизировать?
1 ответ
c1
Код CIL будет неправильным для c2
дело.
в c1
версия, Equals
вызывается на копию obj
, в c2
версия, особое внимание должно быть уделено, чтобы позвонить Equals
на t
, а не копия t
, Это потому что T
может быть типом значения, который был переопределен Equals
изменить свои собственные данные экземпляра. Так как ты звонишь Equals
на t
модификация должна быть видна в t
,
Оптимизация возможна для c1
только потому, что ни у кого нет возможности проверить obj
после того, как метод вернулся, не имеет значения, является ли он obj
или копия obj
это потенциально может быть изменено.