2010-09-24 14 views
349

Что лучше использовать, и почему, на большом проекте:#if DEBUG против Conditional ("DEBUG")

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

или

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

См http://blogs.msdn.com/b/ericlippert/archive/2009/09/10/what-s-the- разницу между условным компиляцией и условным атрибутом.aspx для некоторых мыслей по этому вопросу. –

+0

вы также можете использовать это: if (Debugger.IsAttached) {...} – sofsntp

ответ

471

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

  • #if DEBUG: Код здесь даже не достигнет ИЛ на выпуск.
  • [Conditional("DEBUG")]: Этот код достигнет ИЛ, однако вызовы к методу будут опущены, если 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 
    } 

против:

[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 или нет.


Update: Потому что этот ответ является наивысшим голосовала ответ на этот вопрос, я обновляя этот ответ прояснить важный и непростой момент. Если вы решите использовать 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 определен в вызывающей сборке.

+9

#if Отладка для DoSomething не нуждается во всех вызывающих операторах, окруженных #if DEBUG. вы можете либо 1: просто #if DEBUG внутри DoSomething, либо сделать #else с пустым определением DoSomething. Тем не менее ваш комментарий помог мне понять разницу, но #if DEBUG не обязательно быть таким же уродливым, как вы продемонстрировали. – Apeiron

+2

Если вы просто #if DEBUG содержимое, JIT может по-прежнему включать вызов функции, когда ваш код работает в не-отладочной сборке. Использование условного атрибута означает, что JIT знает, что даже не выводить callsite, если он создан в составе, отличном от DEBUG. –

+0

Условное не исключает код. Он отмечает его метаданными. Значение DEBUG сопоставляется во время компиляции кода CALLING. Это может быть внутри сборки, которая имеет маркированный метод или вне ее. –

54

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

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

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

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

+3

+1 для вызова кода также должны быть инструкции #if. Это означает, что будет распространяться операторы #if ... –

+0

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

9

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

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

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

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

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

@P Daddy: Wrapping '#if DEBUG' вокруг' Conditional ("DEBUG") 'не удаляет вызовы этой функции, он просто удаляет функцию из IL alltogether, поэтому у вас все еще есть вызовы функции, t существует (ошибки компиляции). –

+0

Если вы не хотите, чтобы код существовал в версии, следует обернуть тело метода в «#if DEBUG», возможно, с заглушкой #else (с возвратным значением throw или dummy) и использовать атрибут для что вызывающие абоненты не беспокоятся о звонке? Это казалось бы лучшим из обоих миров. – supercat

+0

@myermian, @supercat: Да, вы оба правы. Виноват. Я отредактирую по предложению суперкара. –

4

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

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

36

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

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

+3

Я полностью согласен с тобой, Джимми. Если вы используете DI и высмеиваете ваши тесты, зачем вам нужно '#if debug' или любую аналогичную конструкцию в вашем коде? –

+0

@RichardEv Может быть лучший способ справиться с этим, но в настоящее время я использую его, чтобы позволить себе играть роль разных пользователей с помощью строки запроса. Я не хочу, чтобы это было на производстве, но я хочу, чтобы это было для отладки, поэтому я могу контролировать рабочий процесс, который прошел через него, не создавая нескольких пользователей и не заходите в оба аккаунта, чтобы пройти через поток. Хотя это первый раз, когда я действительно должен был его использовать. – Tony

+3

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

0

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

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

if (Debugger.IsAttached) 
{ 
... 
} 
Смежные вопросы