yBother
yBother

Reputation: 718

C# Reflection dynamically create concrete class instance of T

I want to do something at runtime with objects based on there type. Depending on the type I need to do different operations so I thought I would create an abstract class like this:

internal abstract class DataOperator<T>
        where T : class
    {
        public abstract void DoSomething(IList<T> data);
    }

with multiple concrete implementations like:

class MyClassOperator : DataOperator<MyClass>
     
    {
        public override void DoSomething(IList<MyClass> data)
        {
            throw new NotImplementedException();
        }
    }

But how would I actually create an instance of the concrete class at runtime?

Upvotes: 0

Views: 211

Answers (2)

yBother
yBother

Reputation: 718

I resolved this issue using a factory as suggested by Matthew Watson. DataOperator is actually AggregateRootMigrator

 public static class AggregateRootMigratorFactoryProvider
    {
        public static AggregateRootMigrator<T> GetAggregateRootMigrator<T>()
            where T : AggregateRoot
        {
            var type = typeof(AggregateRootMigrator<T>).Assembly.GetTypes().Where(
                t =>
                    {
                        if (t.IsAbstract == false && t.IsSubclassOf(typeof(AggregateRootMigrator<T>)))
                        {
                            return true;
                        }

                        return false;
                    }).SingleOrDefault();

            if (type == null)
            {
                throw new InvalidOperationException($"No Factory found for eventType: {typeof(T).Name}");
            }

            return (AggregateRootMigrator<T>)Activator.CreateInstance(type);
        }
    }

now I can use it like this: var dataMigrator = AggregateRootMigratorFactoryProvider.GetAggregateRootMigrator(); var result = dataMigrator.Migrate(data);

Upvotes: 0

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112259

The problem is that DataOperator<A> and DataOperator<B> are not assignment compatible. Not even if A and B are. So, you cannot assign your classes to, say DataOperator<object>. Therefore, you will have to treat them individually as non related types.

A way out is to have a non generic base type or interface.

interface IDataOperator
{
    public void DoSomething(IList data);
}

We create an abstract class that implements it explicitly in order to hide this weakly typed DoSomething when not called through the interface. We also introduce the generic type parameter.

abstract class DataOperator<T> : IDataOperator
    where T : class
{
    void IDataOperator.DoSomething(IList data)
    {
        if (data is IList<T> listT) {
            DoSomething(listT);
        } else {
            throw new ArgumentException("The List is not compatible.", nameof(data));
        }
    }

    abstract public void DoSomething(IList<T> data);
}

We implement concrete types like this:

class DataOperatorA : DataOperator<MyClassA>
{
    public override void DoSomething(IList<MyClassA> data)
    {
        throw new NotImplementedException();
    }
}

class DataOperatorB : DataOperator<MyClassB>
{
    public override void DoSomething(IList<MyClassB> data)
    {
        throw new NotImplementedException();
    }
}

These implementations are assignment compatible to our interface. Example:

IDataOperator[] operators = { new DataOperatorA(), new DataOperatorB() };

If you want to create different operators depending on other data, you can create a factory class. In this example, we use a string as discriminator, but it could be anything else, like a System.Type or an enum, etc.

static class DataOperator
{
    public static IDataOperator Create(string op)
    {
        return op switch {
            "A" => new DataOperatorA(),
            "B" => new DataOperatorB(),
            _ => throw new ArgumentException("Unknown operator.", "op")
        };
    }
}

Same example as above but with the factory:

IDataOperator[] operators = { DataOperator.Create("A"), DataOperator.Create("B") };

Upvotes: 2

Related Questions