Различия в декомпиляторе.NET между "использованием" и "попыткой... наконец"
Учитывая следующий код C#, в котором метод Dispose вызывается двумя различными способами:
class Disposable : IDisposable
{
public void Dispose()
{
}
}
class Program
{
static void Main(string[] args)
{
using (var disposable1 = new Disposable())
{
Console.WriteLine("using");
}
var disposable2 = new Disposable();
try
{
Console.WriteLine("try");
}
finally
{
if (disposable2 != null)
((IDisposable)disposable2).Dispose();
}
}
}
После компиляции с использованием конфигурации выпуска и дизассемблирования с помощью ildasm MSIL выглядит следующим образом:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 57 (0x39)
.maxstack 1
.locals init ([0] class ConsoleApplication9.Disposable disposable2,
[1] class ConsoleApplication9.Disposable disposable1)
IL_0000: newobj instance void ConsoleApplication9.Disposable::.ctor()
IL_0005: stloc.1
.try
{
IL_0006: ldstr "using"
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: leave.s IL_001c
} // end .try
finally
{
IL_0012: ldloc.1
IL_0013: brfalse.s IL_001b
IL_0015: ldloc.1
IL_0016: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_001b: endfinally
} // end handler
IL_001c: newobj instance void ConsoleApplication9.Disposable::.ctor()
IL_0021: stloc.0
.try
{
IL_0022: ldstr "try"
IL_0027: call void [mscorlib]System.Console::WriteLine(string)
IL_002c: leave.s IL_0038
} // end .try
finally
{
IL_002e: ldloc.0
IL_002f: brfalse.s IL_0037
IL_0031: ldloc.0
IL_0032: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0037: endfinally
} // end handler
IL_0038: ret
} // end of method Program::Main
Как декомпилятор.NET, такой как DotPeek или JustDecompile, делает разницу между использованием и попыткой... наконец?
2 ответа
Это не имеет значения на самом деле. Как говорит Марк в комментариях - если вы напишите тот же код, который компилятор сгенерирует для using
- декомпилятор не сможет изменить ситуацию.
Однако многие декомпиляторы, в том числе DotPeek, могут фактически использовать файлы символов отладки (.pdb), чтобы найти фактический исходный код, а затем использовать фактический исходный код, так что декомпиляция вообще не происходит. Кроме того, компиляция в режиме отладки также может повлиять на шаблон (то есть - ваша попытка имитировать using
оператор может иметь разные результирующие IL в отладочных версиях против компиляций релизов).
Чтобы DotPeek не использовал ваши файлы с исходным кодом, перейдите в Инструменты> Параметры> Декомпилятор и снимите флажок "Использовать источники из файлов символов, когда они доступны". Затем скомпилируйте ваш код в Release и обратите внимание, что DotPeek декомпилирует оба оператора как using
,
Как декомпилятор.NET, такой как DotPeek или JustDecompile, делает разницу между использованием и попыткой... наконец?
Декомпиляторы в основном работают над сопоставлением с образцом. Обычно IL переводится в простейшее эквивалентное представление, возможное на целевом языке (в данном случае, на C#). Эта модель кода затем проходит серию преобразований, которые пытаются сопоставить последовательности кода с хорошо известными шаблонами. С помощью отладочной сборки ILSpy вы можете фактически просмотреть выходные данные на разных этапах этого конвейера.
Конвейер декомпилятора может включать преобразования, такие как перезапись цикла. Переписчик цикла может восстановить for
петли, ища while
циклы, которым предшествует инициализатор переменной и которые также содержат общие итерационные операторы перед каждым обратным фронтом. Когда такой цикл обнаружен, он переписывается как более сжатый for
петля. Он не знает, что оригинальный код на самом деле содержал for
петля; он просто пытается найти наиболее краткий способ представления кода при сохранении правильности.
Аналогичным образом, using
переписчик будет искать try/finally
блоки, где finally
содержит простую нулевую проверку и Dispose()
позвоните, а затем переписать те, как using
блоки, которые являются более краткими, но все еще правильными в соответствии со спецификацией языка. Декомпилятор не знает, что код содержал using
блок, но так как почти никто не использует явное try/finally
форма, результаты, как правило, соответствуют исходному источнику.