#if DEBUG vs. Conditional ("DEBUG")

что лучше использовать, и почему, на большом проекте:

#if DEBUG
    public void SetPrivateValue(int value)
    { ... }
#endif

или

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }

7 ответов


это действительно зависит от того, что вы собираетесь на:

  • #if DEBUG: код здесь даже не достигнет IL при выпуске.
  • [Conditional("DEBUG")]: этот код достигнет IL, однако звонки метод будет опущен, если DEBUG не установлен при компиляции вызывающего объекта.

лично я использую оба в зависимости от ситуации:

условный ("DEBUG") пример: я использую это, чтобы мне не пришлось вернитесь и отредактируйте мой код позже во время выпуска, но во время отладки я хочу быть уверен, что не сделал никаких опечаток. Эта функция проверяет правильность ввода имени свойства при попытке использовать его в моем 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
    }

versus:

[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

в первом примере код все существует, но просто игнорируется, если отладка не включена. Во втором примере конечная точка const имеет значение "Localhost" или "BasicHttpBinding" в зависимости от того, установлена отладка или нет.


Update: я обновляю этот ответ, чтобы прояснить важный и сложный момент. Если вы решите использовать ConditionalAttribute имейте в виду, что звонки опущено во время компиляции, и не выполнения. То есть:

папку "MyLibrary".dll файлы

[Conditional("DEBUG")]
public void A()
{
    Console.WriteLine("A");
    B();
}

[Conditional("DEBUG")]
public void B()
{
    Console.WriteLine("B");
}

когда библиотека компилируется в режиме выпуска (т. е. без символа отладки), она навсегда будет иметь вызов B() внутри A() опущено, даже если вызов A() включено, потому что DEBUG определен в вызывающей сборке.


ну, стоит отметить, что они вовсе не означают одно и то же.

Если символ debug не определен, то в первом случае SetPrivateValue сам не будет вызываться... тогда как во втором случае он будет существовать, но любой абоненты у тех, кто скомпилирован без символа отладки, эти вызовы будут опущены.

Если код и все его вызывающие объекты находятся в одной сборке, это различие меньше важно - но это означает, что в первом дело тебе и нужно #if DEBUG вокруг вызов код, а также.

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


Я уверен, что многие не согласятся со мной, но проведя время как парень сборки постоянно слышит "но он работает на моей машине!- Я придерживаюсь той точки зрения, что вы не должны использовать ни то, ни другое. Если вам действительно нужно что-то для тестирования и отладки, найдите способ сделать эту тестируемость отдельной от фактического производственного кода.

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


Это может быть полезно, а также:

if (Debugger.IsAttached)
{
...
}

С первым примером,SetPrivateValue не будет существовать в сборке, если DEBUG не определено, со вторым примером,звонки to SetPrivateValue не будет существовать в сборке, если DEBUG не определен.

в первом примере вам придется обернуть любые вызовы в SetPrivateValue С #if DEBUG как хорошо.

со вторым примером вызовы SetPrivateValue будет опущено, но имейте в виду, что SetPrivateValue сам по-прежнему будет скомпилирован. Это полезно, если вы строите библиотека, поэтому приложение, ссылающееся на вашу библиотеку, все еще может использовать вашу функцию (если условие выполнено).

если вы хотите опустить вызовы и сохранить пространство вызываемого абонента, вы можете использовать комбинацию двух методов:

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
    #if DEBUG
    // method body here
    #endif
}

давайте предположим, что ваш код также имел #else оператор, который определил нулевую функцию заглушки, обращаясь к одной из точек Джона Скита. Есть и второе важное различие между ними.

предположим #if DEBUG или Conditional функция существует в DLL, на которую ссылается ваш основной исполняемый файл проекта. С помощью #if, оценка условного будет выполнена в отношении параметров компиляции библиотеки. С помощью оценка условные будут выполняться в отношении настроек компиляции вызывающего.


У меня есть расширение SOAP WebService для регистрации сетевого трафика с помощью пользовательского [TraceExtension]. Я использую это только для отладочных сборок и исключить из выпусков. Используйте #if DEBUG, чтобы обернуть атрибут [TraceExtension], удалив его из сборок выпуска.

#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(...)