[英]Issue with using Static Constructors in Derived and Base Class
我在重构代码时遇到了一些麻烦。 我有一个 appcache 类,它有一个静态构造函数,它将初始化一些信息并创建缓存。 它还具有其他方法,如 getobject、saveobject...
我正在创建一个新的缓存类,它应该继承 appcache 的所有有用方法。 我还希望这个新的缓存类有一个静态构造函数,但不是作为连接部分的“clusterclient”部分,而是不同的初始化。
public class AppCache
{
internal static AWSCache _internalCache;
static AppCache()
{
TimeSpan awsExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["AppCacheMinutes"], out var cacheMinutes) ? cacheMinutes : 60);
TimeSpan localExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["LocalCacheMinutes"], out cacheMinutes) ? cacheMinutes : 2);
_internalCache = new AWSCache("clusterclient", awsExpiration, localExpiration, null);
}
// OTHER USEFUL METHODS ...
public class OtherCache : AppCache
{
static OtherCache()
{
TimeSpan awsExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["AppCacheMinutes"], out var cacheMinutes) ? cacheMinutes : 60);
TimeSpan localExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["LocalCacheMinutes"], out cacheMinutes) ? cacheMinutes : 2);
_internalCache = new AWSCache("otherclusterclient", awsExpiration, localExpiration, null);
}
}
// 程序其他部分的代码 AppCache.GetObject(key) 调用 -> _internalCache.GetObject(...,...,...);
我的主要问题基本上是如何设置和重构 AppCache 类,这样我就不必完全复制它并创建一个名为 OtherCache 的新类。 相反,我想继承“其他有用的方法”并以不同的方式静态初始化 _internalCache。
我厌倦了各种各样的事情,无法让它工作。 它总是调用基类静态方法并以这种方式初始化 _internalcache,即使我使用如下所示。
OtherCache.GetObject(key) 调用 => _internalCache.SaveObject(...,...,...);
这是一个(我认为)满足您对语法的需求的实现。 它使用单例模式、一个基类来隐藏/重用实现细节,并使用带有反射的泛型来注入唯一的初始化信息。
你提到你可以重构AppCache
类,这种方法需要修改它。
单例:单例模式提供您想要的静态访问(同时避免使用静态构造函数)。 此模式还确保每个派生类型都有单独的缓存,其中每个缓存都可以根据需要使用唯一的字符串进行初始化。 我使用 Lazy 实现了 Singleton,但还有其他选择。
基类:拥有基类允许您在基类中定义所有静态“其他有用的方法”,以实现良好的代码重用。 我为你开始了一个。 它假设每个派生缓存始终是AWSCache
类型。
泛型/反射:更值得注意的是 IMO,它使用带有where
子句和最终反射(通过 Activator.CreateInstance)的泛型来查找和调用派生构造函数并以您想要的方式初始化缓存。 这意味着每个派生类只需要定义一行:一个调用基本构造函数并为其提供在缓存初始化期间使用的唯一字符串的构造函数。 反射通常可能很慢,但在这种情况下,它只在初始化单例时调用一次。
作为编译控制台应用程序呈现:
using System;
using System.Configuration;
namespace SomeNamespace
{
public class Program
{
static void Main()
{
// example of desired syntax - no initialization required.
_ = AppCache.GetObject("key");
_ = DerivedCache.GetObject("key");
}
}
// note the 'where' clause.
// we want to restrict T to always be derived from this class type, and nothing else.
public abstract class CacheBase<T> where T : CacheBase<T>
{
private readonly AWSCache _internalCache;
protected CacheBase(string cacheTypeName)
{
TimeSpan awsExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["AppCacheMinutes"], out var cacheMinutes) ? cacheMinutes : 60);
TimeSpan localExpiration = TimeSpan.FromMinutes(int.TryParse(ConfigurationManager.AppSettings["LocalCacheMinutes"], out cacheMinutes) ? cacheMinutes : 2);
_internalCache = new AWSCache(cacheTypeName, awsExpiration, localExpiration, null);
}
// very, VERY simplistic implementation of the Singleton pattern
// sets the 'nonPublic' bool to 'true' to find the hidden constructors
// you may want better error handling of instance creation; modify as needed
protected static readonly Lazy<T> _lazy = new Lazy<T>(() => (T)Activator.CreateInstance(typeof(T), true));
// implement all the "USEFUL METHODS" here, in the base class.
// This works because all derived classes create a cache of the same type.
private static AWSCache Cache => _lazy.Value._internalCache;
public static object GetObject(string key)
{
return Cache.GetObject(key);
}
// etc.
}
// for derived classes, remember to keep the constructors private to maintain Singleton pattern.
public class AppCache : CacheBase<AppCache>
{
private AppCache() : base("clusterCache") { }
}
public class DerivedCache : CacheBase<DerivedCache>
{
private DerivedCache() : base("otherClusterCache") { }
}
// implemented as a shell so things compile for simple console app with no references
public class AWSCache
{
public AWSCache(string name, TimeSpan awsExpiration, TimeSpan localExpiration, object dunno) { }
public object GetObject(string key) { return null; }
}
}
用法看起来很干净,正如我认为的那样。 您可以随时调用DerivedClass.USEFULMETHOD()
。
每个派生类都应始终将自身用作泛型类型T
。 您可以(并且应该)添加一些错误处理来捕捉开发人员将错误类型放入T
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.