A default constructor is a constructor that doesn’t have parameters.
Therefore, to check if a type has a default constructor, you can use reflection to loop through the constructors and see if there are any with no parameters, like this:
private static bool HasDefaultConstructor(Type type)
{
return type.GetConstructors().Any(t => t.GetParameters().Count() == 0);
}
Code language: C# (cs)
In this article I’ll show an example of loading types that implement a specific interface and only create an instance of them if they have a default constructor.
Table of Contents
Loading the types with default constructors
For this example, I’ll be looking for all classes that implement the following interface called IPlugin:
public interface IPlugin
{
string HandlesDataFromSource { get; }
void ProcessData(string data);
}
Code language: C# (cs)
The following code loads all types implementing the IPlugin interface. Then it creates an instance of each type that has a default constructor and adds it to a dictionary:
static void Main(string[] args)
{
var candidateTypes = GetAllTypesThatImplementInterface<IPlugin>();
Dictionary<string, IPlugin> routingTable = new Dictionary<string, IPlugin>();
foreach(var type in candidateTypes)
{
if(HasDefaultConstructor(type))
{
var plugin = (IPlugin)Activator.CreateInstance(type);
routingTable.Add(plugin.HandlesDataFromSource, plugin);
Console.WriteLine($"Created type {type.Name}");
}
else
{
Console.WriteLine($"Not creating type {type.Name} - it doesn't have a default constructor");
}
}
}
private static bool HasDefaultConstructor(Type type)
{
return type.GetConstructors().Any(t => t.GetParameters().Count() == 0);
}
private static IEnumerable<Type> GetAllTypesThatImplementInterface<T>()
{
return System.Reflection.Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface);
}
Code language: C# (cs)
Note: It’s using the dictionary as a routing table based on what the plugin says it can handle.
Three default constructor scenarios
FileSystemDataHandler plugin – has compiler-generated default constructor
When you don’t declare a constructor, the compiler automatically creates a default constructor for you. When I run the code, it’ll load this plugin because it has a default constructor.
public class FileSystemDataHandler : IPlugin
{
public string HandlesDataFromSource => "FileSys";
public void ProcessData(string data)
{
//process data
}
}
Code language: C# (cs)
HttpDataHandler plugin – has user-defined declared default constructor
In this class I declared a constructor that has no parameters – therefore it’s a default constructor, and this plugin will be loaded when I run the code.
public class HttpDataHandler : IPlugin
{
public string HandlesDataFromSource => "Http";
public void ProcessData(string data)
{
//process data
}
private HashSet<string> wordFilterSet;
public HttpDataHandler()
{
var wordFilterList = ConfigurationManager.AppSettings["wordFilter"].Split(',');
wordFilterSet = new HashSet<string>(wordFilterList);
}
}
Code language: C# (cs)
This example is parsing a CSV string from a config file.
SqlDataHandler plugin – no default constructor
The following plugin has a constructor with parameters, which means it doesn’t have a default constructor. Because it doesn’t have a default constructor, it will not be loaded.
public class SqlDataHandler : IPlugin
{
private ConnectionStringSettings ConnectionStringSettings;
public SqlDataHandler(ConnectionStringSettings connectionStringSetting)
{
ConnectionStringSettings = connectionStringSetting;
}
public string HandlesDataFromSource => "Sql";
public void ProcessData(string data)
{
//process data
}
}
Code language: C# (cs)
Results
Running the code results in the following output. As you can see it doesn’t load SqlDataHandler (because it doesn’t have a default constructor, therefore it’s filtered out).
Not creating type SqlDataHandler - it doesn't have a default constructor
Created type FileSystemDataHandler
Created type HttpDataHandler
Code language: plaintext (plaintext)