There are a couple ways to handle your factorings. In my world, we used four primary techniques:
- compiler flags (
#if
)
- partial classes
- separate implementations
- Runtime decisions
So for example, we have build configurations for, C# with unmanaged code, C# with all managed code, C# for silverlight. In the C# unmanaged project we have a compile-time symbol UNMANAGED
, for C# we have MANAGED
and for the silverlight we have SILVERLIGHT
. This lets me inject small tasks into the code and share the same files across all projects. No big deal.
For partial classes, we have separate .cs files for each project that have implementations of the fringe code. This gets used in the cases where we couldn't make this work by having an abstract class as the parent class with most of the implementation and then the fringe code in concrete classes for each target. This works well enough.
For separate implementations, we acknowledge that there is little that can be shared between the code bases and we're better off with separate code. This is not ideal, but so be it.
For runtime checks, it's exactly that. Rather than check for DEBUG
in a #if
, you use a runtime check for a setting to make that choice. Unless you've got heinously huge debug scaffolding, this is not a bad choice as it also lets you do field debugging (but you may have delivery constraints that prevent it).
Personally, I try to avoid the compiler flags. They make the code harder to read. Honestly, though, there are times where they make sense. We've had classes that wouldn't compile in silverlight just because of the class declaration (I think it was ObservableCollection that wasn't available) and we had to inherit from something else. Everything else worked fine.