Designing a .NET application with removable features

Normally, if you remove a class file from an application, it will just break, but let’s imagine, you want to allow that, so that, a sysops engineer can delete sensitive code from your application that may not be required in a given circumstance.
So, to design an application that will allow you to just delete a file without breaking the application, takes alot of forethought, and here’s one possible way of doing it.
TL;DR; here’s the github repo: https://github.com/infiniteloopltd/PluginDemo
So, First off, I’ll describe this proof of concept application. It has two features, Addition and Subtraction, which are named Addition.cs and Subtraction.cs in the Features folder. Either of these files can be deleted, and it won’t break the application, but evidently the ability to perform that operation will be removed.
So, First, I define an Interface, which features must adhere to; as follows;
namespace PluginDemo
{
public interface IFeature
{
string Description { get; }
int Execute(int a, int b);
}
}
So, each feature has a Description, and it can perform some numerical operation on two numbers. Here is how Addition.cs implements this interface;
namespace PluginDemo
{
class Addition : IFeature
{
public string Description
{
get
{
return "Addition";
}
}
public int Execute(int a, int b)
{
return a + b;
}
}
}
Now, the magic is in the reflection, where we generate a dynamic list at runtime of all classes in the assembly that implement IFeature. (except IFeature itself), which is defined in a class named FeatureManager
using System;
using System.Collections.Generic;
using System.Reflection;
namespace PluginDemo
{
public static class FeatureManager
{
private static readonly List<IFeature> _features = new List<IFeature>();
static FeatureManager()
{
var dll = Assembly.GetExecutingAssembly();
foreach (var type in dll.GetTypes())
{
if (typeof(IFeature).IsAssignableFrom(type) && !type.IsInterface)
{
_features.Add(Activator.CreateInstance(type) as IFeature);
}
}
}
public static List<IFeature> Features
{
get
{
return _features;
}
}
}
}
Now, we can list all available features by calling;
foreach (var feature in FeatureManager.Features)
{
Console.WriteLine(feature.Description);
}
And, we can use this feature list to check for the availability of a feature, and call it, if it is available.
var addition = FeatureManager.Features.FirstOrDefault(f => f.Description == "Addition");
if (addition != null)
{
Console.WriteLine("1 + 2 = " + addition.Execute(1,2) );
}
Here, as you can see in the above code. If the file, Addition.cs is deleted, then the addition object is null, but no exception is thrown.