#if DEBUG против условного ( "DEBUG" )

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

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

или

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

Ответ 1

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

  • #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 определен в вызывающая сборка.

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

В первом примере вам придется переносить любые вызовы на SetPrivateValue с помощью #if DEBUG.

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

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

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

Ответ 5

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

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

Ответ 6

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

if (Debugger.IsAttached)
{
...
}

Ответ 7

У меня есть расширение 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(...)