具有多个子类的抽象单例继承
Abstract singleton inheritance with multiple sub-classes
继续here给出的答案,有没有办法让MyChildSingleton抽象,然后让其子class定义自己的构造函数?我想做的一个例子:
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));
}
}
使用单例模式似乎是个好主意,因为对于每个特定的子class,数据只需要存在于一个地方,并且由于它是不可变的,因此避免了与单例相关的大多数问题(全局可变状态等)。在抽象基础 class 中提供单例功能将避免放置私有实例和 public 获取 属性 的样板,这就是我现在在每个子 [=27] 中所做的=].这真的不是太繁重的要求,我相信我可以接受。
我不想让 DefaultConfigData 和它的数据成为静态的,因为这样我就无法继承它并让我的库函数知道如何与它交互(不支持元classes C#)。另外,我不想使用接口,因为太多的功能是共享的,我无法在接口中定义它。
我也欢迎对替代方法发表评论,如果我正在尝试做的方法无法完成,或者其他方法更简单。我知道工厂模式也可以在这里工作,这是我最终打算尝试的。
最后,为什么这是一个问题?为什么不决定让抽象 classes 满足 new() 要求,前提是它们的任何子 classes 也满足 new()?
请注意,我的 "users" 是其他内部 developers/my 未来的自己。源代码通常是可交付成果,在这种环境下可以将检查推送到 运行 时间。
我能想到的最简单的解决方案是使用工厂模式。另一个解决方案是您需要保持 DefaultConfigData
class 通用,例如:
public abstract class DefaultConfigData<T>: SingletonBase<T>
where T : DefaultConfigData<T>, new()
{ }
问题在于,当您想在任何地方使用 DefaultConfigData
时,您必须使该方法或 class 通用,例如 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;
请注意,您的 class 中的 none 需要继承自 MyFactory
。只要你有一个无参数的构造函数,你就可以自由地创建 classes 来继承任何东西(或什么都不继承)。您可以使用 where T : new()
限制 MyFactory<T>
中的 T
,在这种情况下,您将获得 class 支持无参数 public 构造函数的编译时保证,并且可以通过仅使用 new T()
创建 class 来避免反射。没有 new()
限制,它可以使用私有构造函数创建单例,但是你会失去编译时检查是否存在无参数构造函数的功能。
其实还有另一种解决方法。它要复杂得多,但如果您充分利用它,它会非常强大:一个 IoC 容器,例如 Autofac, Unity,以及许多其他容器。这些可以帮助管理您的实例,以便您可以指定应该是单例的类型。我不会讨论如何使用它,因为那是一个完全不同的主题,并且需要多个段落来解释基础知识。
继续here给出的答案,有没有办法让MyChildSingleton抽象,然后让其子class定义自己的构造函数?我想做的一个例子:
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));
}
}
使用单例模式似乎是个好主意,因为对于每个特定的子class,数据只需要存在于一个地方,并且由于它是不可变的,因此避免了与单例相关的大多数问题(全局可变状态等)。在抽象基础 class 中提供单例功能将避免放置私有实例和 public 获取 属性 的样板,这就是我现在在每个子 [=27] 中所做的=].这真的不是太繁重的要求,我相信我可以接受。
我不想让 DefaultConfigData 和它的数据成为静态的,因为这样我就无法继承它并让我的库函数知道如何与它交互(不支持元classes C#)。另外,我不想使用接口,因为太多的功能是共享的,我无法在接口中定义它。
我也欢迎对替代方法发表评论,如果我正在尝试做的方法无法完成,或者其他方法更简单。我知道工厂模式也可以在这里工作,这是我最终打算尝试的。
最后,为什么这是一个问题?为什么不决定让抽象 classes 满足 new() 要求,前提是它们的任何子 classes 也满足 new()?
请注意,我的 "users" 是其他内部 developers/my 未来的自己。源代码通常是可交付成果,在这种环境下可以将检查推送到 运行 时间。
我能想到的最简单的解决方案是使用工厂模式。另一个解决方案是您需要保持 DefaultConfigData
class 通用,例如:
public abstract class DefaultConfigData<T>: SingletonBase<T>
where T : DefaultConfigData<T>, new()
{ }
问题在于,当您想在任何地方使用 DefaultConfigData
时,您必须使该方法或 class 通用,例如 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;
请注意,您的 class 中的 none 需要继承自 MyFactory
。只要你有一个无参数的构造函数,你就可以自由地创建 classes 来继承任何东西(或什么都不继承)。您可以使用 where T : new()
限制 MyFactory<T>
中的 T
,在这种情况下,您将获得 class 支持无参数 public 构造函数的编译时保证,并且可以通过仅使用 new T()
创建 class 来避免反射。没有 new()
限制,它可以使用私有构造函数创建单例,但是你会失去编译时检查是否存在无参数构造函数的功能。
其实还有另一种解决方法。它要复杂得多,但如果您充分利用它,它会非常强大:一个 IoC 容器,例如 Autofac, Unity,以及许多其他容器。这些可以帮助管理您的实例,以便您可以指定应该是单例的类型。我不会讨论如何使用它,因为那是一个完全不同的主题,并且需要多个段落来解释基础知识。