Условная компиляция
Один мой друг как-то захотел сделать демо написанного собственным потом и кровью SDK на .NET. Естественно нужно было урезать часть очень важного функционала. Сделать это нужно было так как всем известно как в .NET происходит защита исходного кода. Плохо если не сказать другим словом – обфускация.
Обфускация по сути переименование и переименование это касается только приватной или внутренней реализации программы. Но SDK в силу того что нужно выставлять часть функционала наружу поддается защите обфускатором заметно хуже.
Поэтому необходимо было сделать демо. Можно было конечно покромсать код вручную – но это не выход. Во-первых, потому что это не разовая работа, а такое проделывать необходимо столько раз сколько билдов отдаются на растерзание публики и, во-вторых, это прямой путь к мешанине, неразберихе, путанице( так как какой-то код добавляется, какой-то удаляется) и как следствие к багам. Тем более, что на такую пустую работу при наличие большого количества “непубличного” функционала будет уходить достаточно много ценного времени.
Как же в такой ситуации быть? Есть необходимость собирать билд с включением или выключением определенного функционала условно.
В это случае поможет условная компиляция. Такую возможность C# как язык унаследовал от С/С++ (всем достаточно известно использование #define, #ifdef и т.п. в С/С++ ). Их хотя в шарпе уже не напишешь тех изощренных макросов как в си, но все же аналогичные директивы в C# имеют свое применение.
Пользоваться этим достаточно просто. Необходимо используя директиву #define объявить константу условной компиляции.
#define TEST
Также константу условной компиляции можно объявить используя опцию компилятора /define. А так как это можно сделать в опциях, тогда то же самое можно сделать и в свойствах проекта используя Visual Studio (вкладка Builde > Conditional compilation symbols):
Правда есть отличия константа объявленная директивой распространяет свое действие только на файл в котором она объявлена, для того чтобы ее действие распространялось на весь проект следует ее объявить через опции.
При создании нового проекта для него создаются две конфигурации – Debug и Release, для первой объявлено по умолчанию две константы DEBUG и TRACE, для релизной конфигурации – только TRACE.
После объявления такую константу можно использовать либо с #if, #endif, #else директивами, либо в связке с атрибутом ConditionalAttribute. Например, код можно обернуть директивами:
#if (DEBUG) Debug.Listeners.Remove("Default"); Debug.Listeners.Add(new SuperAssertTraceListener()); #endif
или с #else:
#if (DEBUG) stream = new StreamWriter("c:\\temp\\WorkTimeTracer.txt"); #else stream = null; #endif
На директивы никаких ограничений в отличии атрибута – нет, если что-то не так код не скомпилируется. Но с атрибутом код более читабелен. На метод помеченные ConditionalAttribute накладываются следующие ограничения:
- Такой метод не должен возвращать значений
- Не должен быть объявлен в интерфейсе
- Не должен быть методом с идентификатором override.
Суть этих ограничений вполне логична. Ведь в конфигурациях отличных от указанной в атрибуте, тело метода будет восприниматься как комментарий. Если посмотреть на класс System.Diagnostics.Debug к примеру то там многие методы помечены этим атрибутом.
[Conditional("DEBUG")] public static void Trace(string message) { Console.WriteLine(message); }
На самом деле применять условную компиляцию можно не только для того чтобы исключать функционал нужный для дебага, или для того чтобы сделать демку. Условную компиляцию стоит применять в “кроссплатформенной” разработке, например когда пишется клиентская часть на Silverlight и WPF, или для обычного и .NET Compact Framework
SabMakc: Следует помнить, что использовать такие директивы следует с очень большой осторожностью, т.к. могут возникнуть труднонаходимые ошибки (особенно если проект развивается длительное время).
Этот блог зеркалируется. Оригинал взят с regfordev.blogspot.com
Компании из статьи
Microsoft Украина | Украинское подразделение компании Microsoft. |