2015-04-14 7 views
3

У меня есть небольшая программа на C#, которая производит разные выходные данные между версиями Debug и Release. Пустой вывод из версии Release, я думаю, соответствует спецификации языка C#. Эта программа не должна выводить результат, но версия Debug.Различные выходные C# между выпуском и отладкой

Я запустил обе версии Release и Debug из командной строки (вне среды VS) и получил тот же непоследовательный вывод. Я декомпилировал версию Debug (используя ILDASM), а затем повторно скомпилировал ее с помощью ILASM. Когда я это делаю, новая скомпилированная программа ведет себя точно так же, как версия Release. Я могу только представить себе, что когда я декомпилирую, а затем повторно компилирую что-то, меня оставляют без внимания, но я не смог определить, что изменилось.

Что касается размера EXE-файла: обе версии выпуска и отладки, выпущенные VS, имеют одинаковый размер файла: 5,120 байт. Когда я декомпилирую и повторно компилирую, обе версии снова имеют одинаковый, но меньший размер файла: 3,072.

Программа довольно маленькая, и я посмотрел на IL в Reflector, и я не вижу ничего, что могло бы вызвать разницу в выходе.

У кого-нибудь есть (надеюсь, подробное) объяснение, почему именно существует разница?

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

Вспомните, что я сказал выше - обе версии отладки и выпуска производят другой выход , даже если они запускаются из командной строки. Если вы скажете мне, что среда выполнения делает какую-то оптимизацию для версии Release, но не для версии Debug, то в сборках версии Debug/Release должно быть что-то, что говорит, что среда выполнения включает в себя/выключает оптимизацию. Что такое встроенное «что-то» и почему оно не переносится при использовании ILDASM/ILASM?

Вот код:

using System; 

class Test { 
    static int value = 0; 
    static int a = Initialize("Assigning a"); 
    static int b = Initialize("Assigning b"); 
    static String name = "Fred"; 
    static int c = Initialize("Assigning c"); 

    static int Initialize(String mssg) { 
     ++value; 
     Console.WriteLine("In Initialize() :: {0}, name={1}, returning {2}", mssg, name, value); 
     return value; 
    } // Initialize() 

    static void Main() { 
    } // Main() 
} // class Test 

А вот выход из Visual Studio создается версия Debug:

In Initialize() :: Assigning a, name=, returning 1 
In Initialize() :: Assigning b, name=, returning 2 
In Initialize() :: Assigning c, name=Fred, returning 3 

Запуск версии выпуска не генерирует никакого вывода.

+3

Скорее всего, в режиме деблокирования тестовый класс не загружен, так как нет ссылки на него. Попробуйте добавить 'Console.WriteLine (Test.name);' к вашему основному методу. – EZI

+1

Я бы порекомендовал поставить 'Console.WriteLine (« In Main »);' показать, что main на самом деле вызывает вызов. (Я просто попробовал, что вы все равно получаете другой результат, однако, если вы делаете больше, например 'Console.WriteLine (« In Main() :: {0} », name);', то отлаживайте и отпускайте начало, ведущее себя так же) –

+0

paging mr skeet, mr skeet, пожалуйста, зайдите на страницу SO – pm100

ответ

0

Фактически вы не используете переменные a, b или c в приложении (внутри Main), поэтому я предполагаю, что они оптимизированы. Когда я запускаю этот код в LinqPad с отключением оптимизации, он показывает вывод, который вы описываете, и с оптимизацией включен, он не показывает ничего, кроме выполнения Main(). Если я обновляю код в Main(), чтобы ссылаться на одну из этих переменных, выход согласуется с оптимизацией off build.

2

Инициализатор статического класса не вызывается до его необходимости. Понятно, что версии отладки и выпуска по-разному решались по мере необходимости. В частности, моя догадка заключается в том, что сборка релизов полностью оптимизировала основное назначение и никогда не загружала класс. Кажется, он решил, что, поскольку основной ничего не делает, он может оптимизировать все: в этом случае это кажется плохим решением.

+0

* «В частности, я предполагаю, что релиз-сборка полностью оптимизировала основную цель и поэтому никогда не загружала класс». * Это не тот случай, поставьте 'Console.WriteLine (« In Main() »);' в main и вы все равно получите такое же поведение, но оно будет печатать «In Main()» –

+0

Это не очень хорошо :-( – pm100

2

Включение оптимизаций (режим освобождения) влияет на генерируемый IL, а также на поведение JITter ,

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

Это объясняет поведение ILDASM/ILASM и тот факт, что в IL нет существенной разницы.

Я подозреваю, что это поведение контролируется величиной CorDebugJITCompilerFlags флага где-то в заголовке CLR .. См Does C# compiler /optimize command line option affect JITter?

+0

Если вы положили 'Console.WriteLine (« В Main() »),' который будет печатать без статических функций, являющихся так что Main полностью не устраняется. –

+0

Итак, я вижу. Я полагаю, что только инициализация неиспользуемых переменных устранена. – Blorgbeard

2

После дополнительных исследований, я нашел ответ, который я искал (спасибо Blogbeard для указывая меня в правильном направлении).

Оказывается, что при компиляции для отладки, генерируемые сборок есть, по умолчанию, украшенный DebuggableAttribute которого «Debugging Mode» является

DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.Default 

По-видимому, эта комбинацией флагов, которые, кажется, превращаются отключить оптимизацию JIT, в результате чего результат я увидел в версии Debug программы. Версия версии программы имеет другой «режим отладки», позволяющий продолжить оптимизацию JIT.

Конечно, если вы вручную установите DebuggableAttribute в AssemblyInfo (как и во время тестирования) для сборки Debug, вы можете переопределить поведение по умолчанию.

Я уверен, что есть некоторые юристы CLR/JIT, которые могут объяснить более подробно.

Смежные вопросы