具有多个子类的抽象单例继承

时间:2015-09-23 02:30:55

标签: c# inheritance singleton factory

继续给出here给出的答案,有没有办法允许MyChildSingleton是抽象的,然后让它的子类定义自己的构造函数?我想做的一个例子:

public abstract class SingletonBase<T> 
    where T : SingletonBase<T>, new()
{
    private static T _instance = new Lazy<T>(() => new T());
    public static T Instance
    {
        get
        {                
            return _instance;
        }   
    }
}

public abstract class DefaultConfigData: SingletonBase<DefaultConfigData>
{
    // This class won't compile since it's abstract, and SingletonBase<T>
    // has a new() restriction on T
    // Also, this class is immutable and doesn't change state
    public virtual string SomeData { get; } = "My Default Data String";

    public virtual double MoreData { get; } = 2.71;

    public virtual double SomeFunction(double num)
        { return num + 2*MoreData; }
    public DefaultConfigData() { ; /* nothing to do here */ }

    // Another 50 or so default values/functions...
    // enough to be tedious to redefine in multiple places,
    // and adding a constructor that takes every value would be ridiculous.
    // It would be possible to encapsulate this data, but I'm not
    // yet sure how this should be done, so I haven't gone there yet
}

public class SpecificConfigData1: DefaultConfigData
{
    public override string SomeData { get; } = "A Different String";
    public SpecificConfigData1() { ; /* nothing to do here */ }
}

public class SpecificConfigData2: DefaultConfigData
{
    public override double MoreData { get; } = 3.14;
    public SpecificConfigData2() { ; /* nothing to do here */ }
}

// Downstream developers may need to define additional ConfigData classes

public class Library
{
    public static double doSomething(DefaultConfigData data) { return data.MoreData + 2.0; }
}

public class Program
{
    private readonly DefaultConfigData data;
    public Program(bool choice)
    {
        data = (choice) ? SpecificConfigData1.Instance : SpecificConfigData2.Instance;
    }

    public static void Main()
    {
        Program prog = new Program(/*run-time user input here*/);
        Console.PrintLine(Library.doSomething(prog.data));
    }
}

使用单例模式似乎是一个好主意,因为对于每个特定的子类,数据只需要存在于一个地方,并且因为它是不可变的,这避免了与单例相关的大多数问题(全局可变状态,等等。)。在抽象基类中提供单例功能将避免放置私有实例和公共get属性的样板,这正是我现在在每个子类中所做的事情。这真的不是太繁重的要求,我确信我能忍受它。

我不想让DefaultConfigData及其数据保持静态,因为我无法继承它并让我的库函数知道如何与它进行交互(不支持元类中的元数据) C#)。另外,我不想使用界面,因为很多功能都是共享的,我无法在界面中定义它。

我也欢迎对替代方法的评论,如果我试图做的那个不能完成,或者另一种方式更容易。我知道工厂模式也可以在这里工作,这是我打算最终尝试的东西。

最后,为什么这是一个问题呢?为什么不让抽象类满足new()要求,只要它们的任何子类也满足new()?

请注意我的用户&#34;是其他内部开发者/我未来的自我。源代码通常是可交付的,在这种环境下推送检查到运行时间是可以的。

1 个答案:

答案 0 :(得分:1)

我能想到的最简单的解决方案是使用工厂模式。另一个解决方案是您需要保持DefaultConfigData类通用,例如:

public abstract class DefaultConfigData<T>: SingletonBase<T>
    where T : DefaultConfigData<T>, new()
{ }

问题在于,当您想在任何地方使用DefaultConfigData时,您必须使该方法或类具有通用性,例如doSomething方法:

public static double doSomething<T>(DefaultConfigData<T> data)
    where T : DefaultConfigData<T>, new()
{
    return data.MoreData + 2.0;
}

正如你可以猜到的那样,真的令人讨厌。所以,回到工厂模式:

public static class MyFactory<T>
{
    private static Lazy<T> _instance = new Lazy<T>(CreateUsingReflection);
    public static T Instance
    {
        get
        {
            return _instance.Value;
        }
    }

    private static T CreateUsingReflection()
    {
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
        ConstructorInfo ctor = typeof(T).GetConstructor(flags, null, Type.EmptyTypes, null);
        return (T)ctor.Invoke(null);
    }
}

而且,您可以像这样使用它:

SpecificConfigData1 scd1 = MyFactory<SpecificConfigData1>.Instance;

请注意,您的所有类都不需要从MyFactory继承。只要你有一个无参数构造函数,你就可以自由地创建从任何东西(或什么都没有)继承的类。您可以使用T限制MyFactory<T>中的where T : new(),在这种情况下,您将获得编译时保证该类支持无参数的公共构造函数,并且可以通过使用创建类来避免反射只是new T()。如果没有new()限制,它可以使用私有构造函数创建单例,但是您将失去无参数构造函数存在的编译时检查。

实际上,还有另一种解决方案。它要复杂得多,但如果你最充分地使用它会非常强大:一个IoC容器,如AutofacUnity和许多其他容器。这些可以帮助管理您的实例,以便您可以指定应该是单例的类型。我不会再讨论如何使用一个,因为这是一个完全不同的主题,并且需要多个段落来解释基础知识。