#if DEBUG vs. Conditional ("ОТЛАДКА") - PullRequest
       2

#if DEBUG vs. Conditional ("ОТЛАДКА")

403 голосов
/ 24 сентября 2010

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

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

или

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

Ответы [ 8 ]

545 голосов
/ 24 сентября 2010

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

  • #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
    }

против:

[Conditional("DEBUG")]
public void DoSomething() { }

public void Foo()
{
    DoSomething(); //Code compiles and is cleaner, DoSomething always
                   //exists, however this is only called during DEBUG.
}

# если пример DEBUG: Я использую это при попыткенастроить различные привязки для связи WCF.

#if DEBUG
        public const String ENDPOINT = "Localhost";
#else
        public const String ENDPOINT = "BasicHttpBinding";
#endif

В первом примере весь код существует, но он просто игнорируется, если не включен DEBUG.Во втором примере const 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.

61 голосов
/ 24 сентября 2010

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

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

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

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

42 голосов
/ 24 сентября 2010

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

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

12 голосов
/ 30 сентября 2016

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

if (Debugger.IsAttached)
{
...
}
9 голосов
/ 24 сентября 2010

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

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

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

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

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
    #if DEBUG
    // method body here
    #endif
}
4 голосов
/ 24 сентября 2010

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

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

2 голосов
/ 09 мая 2015

У меня есть расширение SOAP WebService для регистрации сетевого трафика с использованием пользовательского [TraceExtension]. Я использую это только для отладочных сборок и опускаю из сборок выпуска. Используйте #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(...)
0 голосов
/ 11 апреля 2019

Обычно вам это нужно в Program.cs, где вы хотите решить, выполнять ли отладку в не-отладочном коде, и в основном в службах Windows. Поэтому я создал поле IsDebugMode только для чтения и установил его значение в статическом конструкторе, как показано ниже.

static class Program
{

    #region Private variable
    static readonly bool IsDebugMode = false;
    #endregion Private variable

    #region Constrcutors
    static Program()
    {
 #if DEBUG
        IsDebugMode = true;
 #endif
    }
    #endregion

    #region Main

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main(string[] args)
    {

        if (IsDebugMode)
        {
            MyService myService = new MyService(args);
            myService.OnDebug();             
        }
        else
        {
            ServiceBase[] services = new ServiceBase[] { new MyService (args) };
            services.Run(args);
        }
    }

    #endregion Main        
}
...