Search code examples
c#genericsactivator

Create Instance of type using base class with generics


I've created a plugin system within my code, which loads types from DLLS. I grab the type I want from the loaded DLL using this code;

var type = Assembly.LoadFrom(filePath).GetTypes()
                    .FirstOrDefault(t =>
                        t.IsClass && t.IsSubclassOfRawGeneric(typeof(DespatchBasePlugin<>)));

IsSubClassOfRawGeneric hunts down the base type as it is buried several classes down, the code works and the correct type is returned.

I then create an instance of this class using Activator;

DespatchBasePlugin<XMLSettingBase> obj = Activator.CreateInstance(type, new object[] { logger }) as DespatchBasePlugin<XMLSettingBase>;

Unfortunately the cast on this line creates a null reference. Removing the cast returns an instance of the class in question, but I need to store is as its base type.

This is the class being loaded(Shortened for brevity);

public class DHLPlugin : DespatchBasePlugin<UserSetting>
{
    public DHLPlugin(BaseForm logger) : base("DHL", logger)
    {
        this.order = 10;
    }
}

And this is the base class I want it to use(Note the class itself has a base class, it goes several layers deep);

public abstract class DespatchBasePlugin<TSettings> : DespatchBase<TSettings> where TSettings : XMLSettingBase, new()

The previous code used a base class with no generic assigned to it and worked absolutely fine. It looked like this;

DespatchBasePlugin obj = Activator.CreateInstance(type, new object[] { logger }) as DespatchBasePlugin;

I'm sure I'm doing something dumb, please tell me what it is.

Edit - Not marked this as duplicate as I believe this is a better question/answer than the other which consists of a generic link to MSDN as the answer. If this is not a correct way to use the duplicate system please let me know.


Solution

  • You can use contravariance to define your plugin:

    public class Program
    {
        public static void Main()
        {
            var settings = new DerivedSettings()
            {Name = "John"};
            DerivedPlugin a = new DerivedPlugin(settings);
            IPlugin<BaseSettings> sample = (IPlugin<BaseSettings>)a;
            Console.WriteLine(sample.GetName());
        }
    }
    
    public abstract class BaseSettings
    {
        public abstract string Name
        {
            get;
            set;
        }
    }
    
    public interface IPlugin<out TSettings>
        where TSettings : BaseSettings
    {
        string GetName();
    }
    
    public abstract class BasePlugin<TSettings> : IPlugin<TSettings> where TSettings : BaseSettings
    {
        protected readonly TSettings _settings;
        public BasePlugin(TSettings settings)
        {
            _settings = settings;
        }
    
        public virtual string GetName()
        {
            return _settings.Name;
        }
    }
    
    public class DerivedSettings : BaseSettings
    {
        public override string Name
        {
            get;
            set;
        }
    }
    
    public class DerivedPlugin : BasePlugin<DerivedSettings>
    {
        public DerivedPlugin(DerivedSettings settings): base (settings)
        {
        }
    }
    

    I've included a BasePlugin class, but this is optional and you can just directly use the interface.