Factory of inner class objects

Viewed 743

I am trying to create an abstract class (A) that returns an instance of a generic child of itself (B).

I made B's costructor internal, making the creation of a new instance without my factory impossible, this caused an error I cannot seem to find a solution for:

'A.B' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method A.Create().

Is there a solution for such a problem in C#?

namespace C
{
    class P
    {
        static void Main() => Console.WriteLine(A.Create<A.B>().GetType());
    }
    public abstract class A
    {
        public class B : A { protected B() { } }
        public static T Create<T>() where T : A, new() => new T();
    }
}
4 Answers

public static T Create<T>() where T : A, new() defines a public method stating that T must have a public constructor because of the generic type constraint new(). Therefore the constructor cannot be protected or internal.

Move the static factory method Create to the class B instead.

class P
{
    static void Main() => Console.WriteLine(A.B.Create().GetType());
}

public abstract class A
{
    public class B : A
    {
        private B() { }
        public static B Create() => new B();
    }
}

A more generic approach is to use a dictionary of factory delegates allowing you to keep the constructors private.

class P
{
    static void Main() => Console.WriteLine(A.Create<A.B>().GetType());
}

public abstract class A
{
    private static Dictionary<Type, Func<A>> _factories = new Dictionary<Type, Func<A>> {
        [typeof(B)] = () => B.Create(), // Example using factory method.
        [typeof(C)] = () => new C()     // Example using constructor.
    };

    public static T Create<T>() where T : A => (T)_factories[typeof(T)]();

    public class B : A
    {
        private B() { }
        internal static B Create() => new B();
    }

    public class C : A
    {
        internal C() { }
    }
}

In a real world example you might want to add some error checking. If making the constructors internal is enough protection for you, you don't need the factory methods in the internal classes (shown with class C).

You could also expose interfaces instead and keep the classes (except some static factory class) completely private.

Consider also using a dependency injection container. Their aim is to solve this type of problem (and much more).

This is your class:

public class B : A { protected B() { } }

The constructor is protected; therefore, only those classes that inherit B will be able to call the constructor. No one else.

Another Approach

Not sure if you like this but one approach you can take is to create a public abstract class and then make your concrete types private. The clients will only use the abstract interface but ask you to create a specific concrete type through a method. Something like below:

public abstract class SomeClass
{
    public abstract string DoSomething();
}

public abstract class A
{
    private class B : SomeClass
    {
        public override string DoSomething()
        {
            return "Something is done in B";
        }
    }
    public static SomeClass CreateB()
    {
        SomeClass sc = new B();
        return sc;
    }
}
public  static class Program
{
   public static void Main()
    {
        var b = A.CreateB();
        Console.WriteLine(b.DoSomething());
    }

}

Ended up with approach very similar to @Olivier Jacot-Descombes'. The difference is only in that children of A are outside of it:

internal class Program
{
    private static void Main()
    {
        var ab = A.Create<B>();
        ab.Foo();

        var ac = A.Create<C>();
        ac.Foo();
    }
}

public abstract class A
{
    public virtual void Foo() => Console.WriteLine("A");

    public static A Create<T>()
        where T : A
    {
        return Factories[typeof(T)]();
    }

    private static readonly Dictionary<Type, Func<A>> Factories;
    static A()
    {
        Factories = new Dictionary<Type, Func<A>>();
        B.AppendFactory(Factories);
        C.AppendFactory(Factories);
    }
}

public class B : A
{
    private B() => Console.WriteLine("B.ctor()");
    public override void Foo() => Console.WriteLine("B");

    public static void AppendFactory(Dictionary<Type, Func<A>> factories)
    {
        factories.TryAdd(typeof(B), () => new B());
    }
}

public class C : A
{
    private C() => Console.WriteLine("C.ctor()");
    public override void Foo() => Console.WriteLine("C");

    public static void AppendFactory(Dictionary<Type, Func<A>> factories)
    {
        factories.TryAdd(typeof(C), () => new C());
    }
}

Here's another possible solution where the only accessible way to create an inner type of A is to use Create<T> with the inner type as the type argument, or by reflection on the IFactory<T> implementations.

public abstract class A
{
    private A() { }

    private static IDictionary<Type, IFactory> factories = new Dictionary<Type, IFactory>
    {
        {typeof(B), new B.BFactory() },
        {typeof(C), new C.CFactory() }
    };

    public static T Create<T>() where T : A 
        => factories.TryGetValue(typeof(T), out var factory) ? ((IFactory<T>)factory).Create() : throw new InvalidOperationException();

    private interface IFactory { }
    private interface IFactory<T> : IFactory
    {
        T Create();
    }

    public class B : A
    {
        private B() { }

        public class BFactory : IFactory<B>
        {
            B IFactory<B>.Create() => new B();
        }
    }

    public class C : A
    {
        private C() { }

        public class CFactory : IFactory<C>
        {
            C IFactory<C>.Create() => new C();
        }
    }
}
Related