C# Activator.CreateInstance Abstract class can't find constructor

860 Views Asked by At

I'm trying to get every child of Commands in multiple assembly's to store them in a List but in order to do that i need to create an instance of that child in order to store it but so i am trying to use Activator.CreateInstance the goal is to have a ctor public for outside usage and a ctor for the Activator so it can create instances to store but, problem is that Activator just can't find the ctor for some reason, i even tagged the ctor as public but no luck

public abstract class Command
{
    public static List<Command> List { get; set; }
    public static Dictionary<Type, int> Lookup { get; set; }

    public Command(int id, FieldInfo[] field) 
    {
        Id = id;
        Fields = field;
    }

    public Command()
    {
        Command command = List[Lookup[GetType()]];
        Id = command.Id;
        Fields = command.Fields;
    }

    public static void Initialize()
    {
        Lookup = new Dictionary<Type, int>();
        List = new List<Command>();

        foreach (Type type in
                AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
                .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(Command))))
        {
            Command command = (Command)Activator.CreateInstance(type, List.Count, type.GetFields());
            Lookup.Add(type, command.Id);
            List.Add(command);
        }
    }
}

public class PlayerMove : Command
{

}

[TestClass()]
public class PacketTests
{
    [TestMethod()]
    public void PackTest()
    {
        Command.Initialize();

        Packet packet = new Packet();

        var cmd = new PlayerMove()
        {

        };

        cmd.Send(Method.Unreliable);

        var g = Command.List;
    }

What am i doing wrong ?

1

There are 1 best solutions below

4
Sean Reid On

You are trying to construct an instance of PlayerMove. This only has a default constructor, i.e. PlayerMove(), which will call the base constructor Command(). Therefore your call to Activator.CreateInstance should look like

Command command = (Command)Activator.CreateInstance(type);

Alternatively you should add an additional constructor to the PlayerMove class:

public class PlayerMove : Command
{
    public PlayerMove(int id, FieldInfo[] field) : base(id, field){}
    public PlayerMove() : base(){}
}

But I think that probably what you actually want is something like this:

public abstract class Command
{
    public static List<Command> List { get; private set; }
    public static Dictionary<Type, int> Lookup { get; private set; }

    public int Id { get; }
    public FieldInfo[] Fields { get; }

    protected Command(int id, FieldInfo[] field)
    {
        Id = id;
        Fields = field;
    }

    protected Command()
    {
        Command command = List[Lookup[GetType()]];
        Id = command.Id;
        Fields = command.Fields;
    }

    public static void Initialize()
    {
        Lookup = new Dictionary<Type, int>();
        List = new List<Command>();

        foreach (Type type in
                AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
                .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(Command))))
        {
            Command command = (Command) Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.NonPublic, null,
                new object[] {List.Count, type.GetFields()}, null);
            Lookup.Add(type, command.Id);
            List.Add(command);
        }
    }
}

public class PlayerMove : Command
{
    private PlayerMove(int id, FieldInfo[] field) : base(id, field)
    {

    }

    public PlayerMove()
    {

    }
}

This adds a private constructor to the PlayerMove command that is called through reflection and populates the Id and Fields properties on the base class, and a paramaterless constructor that can then be used by other clients of the class.