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?
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).
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...
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With