Условный атрибут C # на элементе интерфейса - PullRequest
8 голосов
/ 09 марта 2011

Я пытаюсь избавиться от директив "#if TRACE" в моем коде, используя вместо этого атрибут Conditional, но не могу легко применить этот подход к интерфейсам. У меня есть способ обойти это, но это довольно уродливо, и я ищу лучшее решение.

например. У меня есть интерфейс с условно скомпилированным методом.

interface IFoo
{
#if TRACE
    void DoIt();
#endif
}

Я не могу использовать условный атрибут в интерфейсе:

// Won't compile.
interface IFoo
{
    [Conditional("TRACE")]
    void DoIt();
}

Я мог бы иметь метод интерфейса, просто вызвав условный закрытый метод в конкретном классе:

interface IFoo
{
    void TraceOnlyDoIt();
}

class Foo : IFoo
{
    public void TraceOnlyDoIt()
    {
        DoIt();
    }

    [Conditional("TRACE")]
    void DoIt()
    {
        Console.WriteLine("Did it.");
    }
}

Это оставило бы мой клиентский код с избыточными вызовами к методу 'nop' TraceOnlyDoIt () в сборке не TRACE. Я могу обойти это с помощью метода условного расширения интерфейса, но он становится немного уродливым.

interface IFoo
{
    void TraceOnlyDoIt();
}

class Foo : IFoo
{
    public void TraceOnlyDoIt()
    {
        Console.WriteLine("Did it.");
    }
}

static class FooExtensions
{
    [Conditional("TRACE")]
    public static void DoIt(this IFoo foo)
    {
        foo.TraceOnlyDoIt();
    }
}

Есть ли лучший способ сделать это?

Ответы [ 5 ]

5 голосов
/ 21 марта 2011

Метод трассировки не должен появляться в интерфейсе, так как это деталь реализации.

Но если вы застряли с интерфейсом и не можете его изменить, то я бы использовал подход #if ... #endif, с которого вы начали.

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

4 голосов
/ 25 августа 2016

Мне нравится метод расширения метода. Это может быть сделано немного лучше / надежнее, по крайней мере для абонентов:

    public interface IFoo
    {
        /// <summary>
        /// Don't call this directly, use DoIt from IFooExt
        /// </summary>
        [Obsolete]
        void DoItInternal();
    }

    public static class IFooExt
    {
        [Conditional("TRACE")]
        public static void DoIt<T>(this T t) where T : IFoo
        {
#pragma warning disable 612
            t.DoItInternal();
#pragma warning restore 612
        }
    }

    public class SomeFoo : IFoo
    {
        void IFoo.DoItInternal() { }

        public void Blah()
        {
            this.DoIt();
            this.DoItInternal(); // Error
        }
    }

Общие ограничения типов используются, чтобы избежать виртуального вызова и потенциальной упаковки типов значений: оптимизатор должен хорошо с этим справиться. По крайней мере, в Visual Studio он генерирует предупреждения, если вы вызываете внутреннюю версию через из-за устаревания. Явная реализация интерфейса используется для предотвращения случайного вызова внутренних методов для конкретных типов: также помечает их как [Устаревшие].

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

1 голос
/ 18 августа 2014

Вы должны оставить задачу оптимизатору (или JIT-компилятору) и использовать:

interface IWhatever
{
  void Trace(string strWhatever);
}

class CWhatever : IWhatever
{
    public void Trace(string strWhatever)
    {
#if TRACE
       // Your trace code goes here
#endif
    }
}

Ни оптимизатор, ни JIT-компилятор не удаляют вызов, который вы должны написать гневным электронным письмам этим разработчикам;).

1 голос
/ 09 марта 2011

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

interface IFoo
{
    void DoIt();
}

class NullFoo : IFoo
{
    public void DoIt()
    {
      // do nothing
    }
}

class TraceFoo : IFoo
{
    public void DoIt()
    {
        Console.WriteLine("Did it.");
    }
}
1 голос
/ 09 марта 2011

Что по этому поводу:

interface IFoo
{
  // no trace here
}

class FooBase : IFoo
{
#if TRACE
    public abstract void DoIt();
#endif
}

class Foo : FooBase
{
#if TRACE
    public override void DoIt() { /* do something */ }
#endif
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...