Activator.CreateInstance

One thing I love about using C# is reflection, it adds another paradigm to the toolkit. Recently, I revisited my old friend Activator.CreateInstance which is perfect for scenarios where you want to use generic code to create and initialize specific types of objects.

Basic Example

Some time ago, I wrote a simple application which plugs into our build pipeline. It does a few things such as backup and compress the existing solution, copy generated code over the top of existing code, applies any patches, and then rewrites parts of the project file.

With a command manager, using Activator.CreateInstance and leveraging the params object[] feature of C#, I was able to encapsulate actions into isolated commands, yet execute them as a generic set at the desired time. This is the command manager:

public class CommandManager
{
    public class Task
    {
        public string Description{ get; init; }
        public ICommand Command{ get; init; }
    }

    private readonly List<Task> _tasks=new();

    public void Register<T>(string description, params object[] args) 
        where T:ICommand
    {
        var command=(ICommand)Activator.CreateInstance(typeof(T), args);
        _tasks.Add(new Task { Description=description, Command=command });
    }

    public void Execute()
    {
        foreach(var task in _tasks)
        {   
            Console.WriteLine(task.Description);
            task.Command.Execute();
        }
    }
}

Next is the command registration and execution.

var manager=new CommandManager();

manager.Register<Archiver>(
    "Backing up previous code.",
    Settings.Default.BackupFolder,
    Settings.Default.ZipName,
    Settings.Default.Folder);

manager.Register<XCopy>(
    "Patching new code.",
    "/S /Y /R",
    Settings.Default.PatchCodeFolder
    Settings.Default.NewCodeFolder);

// more registrations....

manager.Execute();

Decoupled Commands

Another idea is to decouple the commands from the registration process by decorating them with a custom command attribute containing the specific arguments and an ordering priority. I usually place the commands in the same folder, similar to the startup files in a rails project. There is one limitation with this approach, the attribute arguments must be compile time constants. So, in this case, the constructor would need to initialize the command:

[Command(Desc="Backing up previous code.", Priority=100)]
public class BackupCommand:ICommand
{
    // the settings are injected
    public BackupCommand(Settings settings) 
    {
        BackupFolder = settings.BackupFolder;
        ZipName = settings.ZipName;
        Folder = settings.Folder;        
    }

    // ...
}

When executed, the command manager would search for commands, order them by priority, and run them accordingly. This allows you to add a command at any time, in any desired order, simply by creating the command and its decorating attribute.

System Initialization

I often use this approach to bootstrap an application. Rather than using large XML or code configuration files, I create small initialization classes focused on setting up one aspect of the system, such as a log file initializer, an IOC initializer, etc. I then call SystemInitializer.Execute to bootstrap the application. This is not always necessary, for example in an asp.net core project.

Exception Factory

There are many places you can leverage Activator.CreateInstance. For example it is useful to throw exceptions from a common location so you can log them and do any other desired processing. Such a class may have methods like this:

public static class ExceptionFactory
{
    public static void Throw<T>(params object[] args) 
        where T:Exception, new()
    {
        var exception = (T)Activator.CreateInstance(typeof(T), args);
        Raise(exception);        
    }

    // more methods to throw precondition failures etc.

    public static void Raise(Exception e)
    {
        // do any logging etc...
        throw e;
    }
}


// example usage:

ExceptionFactory.Throw<ArgumentNullException>("Username");

For more information about Activator.CreateInstance please visit the official page here.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s