You can resort to plugin-based architecture, where all (or most) features are implemented as plugins that extend core app functionality. This way, your editions will differ only in what assemblies get installed/shipped/etc.
Granted, with this approach you can always make a "Starter" edition to turn into "Professional" by just copying missing assemblies. To solve this, you'll still have to resort to conditional compilation, but you'll have to conditionally compile blocks which are responsible for loading those plugins.
For example, suppose for your Professional edition you want to be able to add, say, export functionality. To that end, you create a separate IExporter
plugin interface. Here's how you handle this:
public IExporter GetExporter(FormatType format)
{
#if PROFESSIONAL_EDITION
return ExporterRegistry.GetExporter(format);
#else
return NullExporter();
#endif
}
Thus, your Professional edition will have an ability to be extended with custom IExporter
s, whereas non-Professional editions, even with all "Professional" assemblies in place, won't be able to make use of this functionality.