Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# conditional attribute on interface member

I'm trying to get rid of the "#if TRACE" directives in my code, by using the Conditional attribute instead, but can't apply this approach easily to interfaces. I have a way round this but it's pretty ugly, and I'm looking for a better solution.

E.g. I have an interface with a conditionally compiled method.

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

I can't use the conditional attribute in an interface:

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

I could have the interface method just call a conditional private method in the concrete class:

interface IFoo
{
    void TraceOnlyDoIt();
}

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

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

This would leave my client code with redundant calls to the 'nop' TraceOnlyDoIt() method in a non-TRACE build. I can get round that with a conditional extension method on the interface, but it's getting a bit ugly.

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();
    }
}

Is there a better way to do this?

like image 798
Ergwun Avatar asked Mar 09 '11 05:03

Ergwun


3 Answers

I like the extension method approach. It can be made a bit nicer/robust, at least for callers:

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

Generic type constraints are used to avoid the virtual call and potential boxing of value types: the optimizer should deal with this well. At least in Visual Studio it generates warnings if you call the internal version via because of Obsolete. An explicit interface implementation is used to prevent accidentally calling the internal methods on the concrete types: marking them with [Obsolete] also works.

While this may not be the best idea for Trace stuff, there are some cases where this pattern is useful (I found my way here from an unrelated use case).

like image 114
CraigM Avatar answered Oct 23 '22 19:10

CraigM


A trace method shouldn't be appearing on an interface as it's an implementation detail.

But if you're stuck with the interface, and can't change it, then I'd use the #if ... #endif approach that you started with.

It is a rather savage syntax though, so I sympathise with why you might want to avoid it...

like image 34
sheikhjabootie Avatar answered Oct 23 '22 18:10

sheikhjabootie


What about this:

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
}
like image 32
Marius Bancila Avatar answered Oct 23 '22 18:10

Marius Bancila