内部类对象的工厂

时间:2017-11-25 17:05:18

标签: c# generics

我正在尝试创建一个抽象类(A),它返回一个自己的通用子项的实例(B)。

我将B的costructor设为内部,在没有我的工厂的情况下创建了一个新的实例,这导致了一个我无法找到解决方案的错误:

  

'A.B'必须是具有公共无参数的非抽象类型   构造函数,以便在泛型类型中使用它作为参数'T'   方法A.Create()。

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();
    }
}

3 个答案:

答案 0 :(得分:5)

public static T Create<T>() where T : A, new()定义了一个公共方法,声明T必须具有公共构造函数,因为泛型类型约束new()。因此,构造函数不能被保护或内部。

将静态工厂方法Create改为类B

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();
    }
}

更通用的方法是使用工厂代理字典,允许您将构造函数保持为私有。

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() { }
    }
}

在现实世界的示例中,您可能希望添加一些错误检查。如果构造函数internal足以为您提供保护,那么您不需要内部类中的工厂方法(使用类C显示)。

您也可以公开接口,并保持类(除了一些静态工厂类)完全私有。

还考虑使用依赖注入容器。他们的目标是解决这类问题(以及更多)。

答案 1 :(得分:1)

这是你的班级:

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

构造函数是protected;因此,只有那些继承B的类才能调用构造函数。没有其他人。

另一种方法

不确定您是否喜欢这个,但您可以采取的一种方法是创建一个公共抽象类,然后将您的具体类型设为私有。客户端将仅使用抽象接口,但要求您通过方法创建特定的具体类型。如下所示:

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());
    }

}

答案 2 :(得分:1)

结束与@Olivier Jacot-Descombes'非常相似的方法。不同之处仅在于A的孩子不在其中:

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());
    }
}