[英]Abstract singleton inheritance with multiple sub-classes
與給出的答案繼續在這里 ,有什么辦法可以讓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()要求的決定呢?
請注意,我的“用戶”是其他內部開發人員/我將來的自己。 源代碼通常是可交付的,並且在這種環境下將檢查推送到運行時是可以的。
我能想到的最簡單的解決方案是使用工廠模式。 另一個解決方案是您需要保持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容器,例如Autofac , Unity和許多其他容器。 這些可以幫助管理您的實例,以便您可以指定應該為單例的類型。 我不會討論如何使用它,因為那是一個完全不同的主題,並且將需要多個段落來解釋基礎知識。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.