Что лучше использовать и почему в большом проекте:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
или
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
Что лучше использовать и почему в большом проекте:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
или
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
Это действительно зависит от того, что вы собираетесь делать:
#if DEBUG
: Код здесь даже не дойдет до релиза IL.[Conditional("DEBUG")]
: этот код достигнет ИЛ, однако вызовы метода будут опущены, если DEBUG не будет установлен, когда компилятор будет скомпилирован.Лично я использую оба в зависимости от ситуации:
Условный ( "DEBUG" ) Пример: Я использую это, поэтому мне не нужно возвращаться и редактировать свой код позже во время выпуска, но во время отладки я хочу быть уверенным, t сделать опечатки. Эта функция проверяет правильность ввода имени свойства при попытке использовать его в моем материале INotifyPropertyChanged.
[Conditional("DEBUG")]
[DebuggerStepThrough]
protected void VerifyPropertyName(String propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
Debug.Fail(String.Format("Invalid property name. Type: {0}, Name: {1}",
GetType(), propertyName));
}
Вы действительно не хотите создавать функцию с помощью #if DEBUG
, если только вы не захотите обернуть каждый вызов этой функции тем же #if DEBUG
:
#if DEBUG
public void DoSomething() { }
#endif
public void Foo()
{
#if DEBUG
DoSomething(); //This works, but looks FUGLY
#endif
}
против
[Conditional("DEBUG")]
public void DoSomething() { }
public void Foo()
{
DoSomething(); //Code compiles and is cleaner, DoSomething always
//exists, however this is only called during DEBUG.
}
#if Пример DEBUG: Я использую это при попытке установить различные привязки для обмена WCF.
#if DEBUG
public const String ENDPOINT = "Localhost";
#else
public const String ENDPOINT = "BasicHttpBinding";
#endif
В первом примере код существует, но он просто игнорируется, если не включен DEBUG. Во втором примере константа ENDPOINT установлена на "Localhost" или "BasicHttpBinding" в зависимости от того, установлен ли DEBUG или нет.
Обновление. Поскольку этот ответ является самым высоким голосовым ответом на вопрос, я обновляю этот ответ, чтобы прояснить важный и сложный вопрос. Если вы решите использовать ConditionalAttribute
, имейте в виду, что во время компиляции пропущенные вызовы отсутствуют, а не время выполнения. То есть:
MyLibrary.dll
[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}
[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}
Когда библиотека скомпилирована в соответствии с режимом деблокирования (т.е. без символа DEBUG), навсегда будет отключен вызов B()
из A()
, даже если включен вызов A()
, потому что DEBUG определен в вызывающая сборка.
Хорошо, стоит отметить, что они вовсе не означают одно и то же.
Если символ DEBUG не определен, то в первом случае сам SetPrivateValue
не будет вызываться... тогда как во втором случае он будет существовать, но любые вызывающие, которые скомпилированы без символа DEBUG, будут эти пропущенные вызовы.
Если код и все его вызывающие лица находятся в одной и той же сборке, это различие менее важно, но это означает, что в первом случае вам также нужно иметь #if DEBUG
вокруг вызывающего кода.
Лично я бы рекомендовал второй подход - но вам нужно сохранить разницу между ними в своей голове.
Я уверен, что многие со мной не согласятся, но, проведя время, когда парень сборки постоянно слышит "Но это работает на моей машине!", я придерживаюсь точки зрения, что вы почти никогда не будете использовать. Если вам действительно нужно что-то для тестирования и отладки, выясните способ сделать эту тестируемость отдельно от фактического производственного кода.
Проанализируйте сценарии, издеваясь над модульными тестами, сделайте одну из версий для одного сценария, который вы хотите протестировать, но не ставьте тесты на отладку кода для двоичных файлов, которые вы тестируете и записываете для выпуска продукции. Эти тесты отладки просто скрывают возможные ошибки от разработчиков, поэтому они не будут найдены дольше в этом процессе.
В первом примере SetPrivateValue
не будет существовать в сборке, если DEBUG
не определен, а второй пример вызовы - SetPrivateValue
не будет существовать в сборке если DEBUG
не определено.
В первом примере вам придется переносить любые вызовы на SetPrivateValue
с помощью #if DEBUG
.
Во втором примере вызовы SetPrivateValue
будут опущены, но имейте в виду, что сам SetPrivateValue
все еще будет скомпилирован. Это полезно, если вы создаете библиотеку, поэтому приложение, ссылающееся на вашу библиотеку, все еще может использовать вашу функцию (если условие выполнено).
Если вы хотите опустить вызовы и сохранить пространство вызываемого абонента, вы можете использовать комбинацию двух методов:
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
#if DEBUG
// method body here
#endif
}
Предположим, что у вашего кода также был оператор #else
, который определял нулевую функцию заглушки, обращаясь к одной из точек Jon Skeet. Там второе важное различие между ними.
Предположим, что функция #if DEBUG
или Conditional
существует в DLL, на которую ссылается ваш основной исполняемый файл проекта. Используя #if
, оценка условного значения будет выполняться в отношении настроек компиляции библиотеки. Используя атрибут Conditional
, оценка условного значения будет выполняться в отношении настроек компиляции вызывающего.
Это также может быть полезно:
if (Debugger.IsAttached)
{
...
}
У меня есть расширение SOAP WebService для регистрации сетевого трафика с использованием пользовательского [TraceExtension]. Я использую это только для сборки Debug и опускаю из сборки Release. Используйте #if DEBUG, чтобы обернуть атрибут [TraceExtension], тем самым удалив его из сборки Release.
#if DEBUG
[TraceExtension]
#endif
[System.Web.Service.Protocols.SoapDocumentMethodAttribute( ... )]
[ more attributes ...]
public DatabaseResponse[] GetDatabaseResponse( ...)
{
object[] results = this.Invoke("GetDatabaseResponse",new object[] {
... parmeters}};
}
#if DEBUG
[TraceExtension]
#endif
public System.IAsyncResult BeginGetDatabaseResponse(...)
#if DEBUG
[TraceExtension]
#endif
public DatabaseResponse[] EndGetDatabaseResponse(...)