Предполагается, что ConditionalAttribute удаляет целые строки или только вызовы методов? - PullRequest
7 голосов
/ 28 сентября 2010

Согласно документации по классу ConditionalAttribute:

Применение условного атрибута к метод указывает компиляторам, что вызов метода не должен быть составлен в промежуточный Microsoft язык (MSIL), если не условно символ компиляции, который связан с условным атрибутом определен.

Для меня это говорит о том, что атрибут Conditional изменяет поведение только на уровне отдельного вызова метода. Но рассмотрим следующий фрагмент кода:

class InstanceType
{
    public InstanceType DoSideEffects()
    {
        Console.WriteLine("Side effects!");
        return this;
    }

    public InstanceType DoMoreSideEffects()
    {
        Console.WriteLine("More side effects!");
        return this;
    }

    [Conditional("DEBUG")]
    public void ConditionalMethod()
    {
        Console.WriteLine("Conditional method run.");
    }
}

class Program
{
    static void Main()
    {
        var x = new InstanceType();

        // The compiler appears to strip out this entire line
        // in a Release build.
        x.DoSideEffects().DoMoreSideEffects().ConditionalMethod();

        var y = new InstanceType();

        // When each method call appears on its own line,
        // the first two methods are included as expected.
        y.DoSideEffects();
        y.DoMoreSideEffects();
        y.ConditionalMethod();
    }
}

Сравните выходные данные сборок Debug и Release:

DEBUG                    RELEASE
Side effects!            Side effects!
More side effects!       More side effects!
Conditional method run.
Side effects!
More side effects!
Conditional method run.

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

Ответы [ 2 ]

2 голосов
/ 30 сентября 2010

Интересная особенность :-) Я никогда не замечал этого.

Я посмотрел на ИЛ. Это не объясняет поведение (процесс компиляции), но, в любом случае, оно документирует результат.

Вся строка кода C # явно не указана в IL:

  • В компиляции DEBUG новый объект создается (переменная x), сохраняется в месте 0 и загружен. потом три метода применяются последовательно: DoSideEffects (), DeMoreSideEffects () и ConditionalMethod ()
  • В компиляции RELEASE переменная по-прежнему создается, но, поскольку она не нужна, она сразу же появляется. Вместо этого переменная y хранится в ячейке 0 и загружается.

Для меня это похоже на ошибку, правда. Кажется, что было бы возможно просто исключить вызов ConditionalMethod () в IL. Но кажется, что вы правы, что вся линия опущена.

// DEBUG compilation
.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       58 (0x3a)
  .maxstack  1
  .locals init (class ConsoleApplication3.InstanceType V_0,
           class ConsoleApplication3.InstanceType V_1)
  IL_0000:  nop
  IL_0001:  newobj     instance void ConsoleApplication3.InstanceType::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoSideEffects()
  IL_000d:  callvirt   instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoMoreSideEffects()
  IL_0012:  callvirt   instance void ConsoleApplication3.InstanceType::ConditionalMethod()
  IL_0017:  nop
  IL_0018:  newobj     instance void ConsoleApplication3.InstanceType::.ctor()
  IL_001d:  stloc.1
  IL_001e:  ldloc.1
  IL_001f:  callvirt   instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoSideEffects()
  IL_0024:  pop
  IL_0025:  ldloc.1
  IL_0026:  callvirt   instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoMoreSideEffects()
  IL_002b:  pop
  IL_002c:  ldloc.1
  IL_002d:  callvirt   instance void ConsoleApplication3.InstanceType::ConditionalMethod()
  IL_0032:  nop
  IL_0033:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_0038:  pop
  IL_0039:  ret
} // end of method Program::Main

// RELEASE compilation
.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       33 (0x21)
  .maxstack  1
  .locals init ([0] class ConsoleApplication3.InstanceType y)
  IL_0000:  newobj     instance void ConsoleApplication3.InstanceType::.ctor()
  IL_0005:  pop
  IL_0006:  newobj     instance void ConsoleApplication3.InstanceType::.ctor()
  IL_000b:  stloc.0
  IL_000c:  ldloc.0
  IL_000d:  callvirt   instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoSideEffects()
  IL_0012:  pop
  IL_0013:  ldloc.0
  IL_0014:  callvirt   instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoMoreSideEffects()
  IL_0019:  pop
  IL_001a:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_001f:  pop
  IL_0020:  ret
} // end of method Program::Main
0 голосов
/ 04 февраля 2015

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

У меня есть догадка относительно того, что происходит.[Conditional] обрезает вызов ConditionalMethod() , а также любых выражений, которые действуют как параметры, передаваемые ему (согласно документации и другой ветке, связанной выше).

Я предполагаю, что неявный параметр this обрабатывается точно так же.В строке x.DoSideEffects().DoMoreSideEffects().ConditionalMethod(); выражение, которое передается как this, равно x.DoSideEffects().DoMoreSideEffects(), которое должным образом удаляется, устраняя побочные эффекты.

Если мы переписываем в псевдокод, где мы явно передаем this какПервый параметр для каждого метода становится намного понятнее:

ConditionalMethod( DoMoreSideEffects( DoSideEffects( x )));

...