[英]Derived class from generic abstract class and casting error?
我有像这样的抽象基类:
public abstract class CacheValueProviderBase<T> where T : ICacheItem
{
protected ConcurrentDictionary<int,T> dataList = new ConcurrentDictionary<int, T>();
public virtual void Add(T model){ // add code }
public virtual bool Remove(int id){ //remove code }
public abstract string getName();
public abstract void UpdateForceFromDataBase();
public abstract void UpdateForceFromCacheServer();
public virtual bool allowForUpdater
{
get
{
return true;
}
}
public virtual bool beforeUpdate()
{
return true;
}
}
我有多个从该抽象类派生的类。 下面的Slider_CacheValueProvider
类用作示例。
public class Slider_CacheValueProvider : CacheValueProviderBase<Cache_Home_Slider_Model>
{
public override string getName()
{
return "Slider_Cache";
}
public override void UpdateForceFromCacheServer()
{ // updating from cache server
}
public override void UpdateForceFromDataBase()
{ // updating from database
}
}
滑块缓存模型:
public class Cache_Home_Slider_Model : ICacheItemID
{
public int ID { get; set; }
public string SlideImage { get; set; }
public string Link { get; set; }
}
所有缓存模型都依赖于ID属性,并仅出于简单的crup操作实现此接口:
public interface ICacheItemID
{
int ID { get; set; }
}
信息:我的缓存机制有2个步骤。 第一步是内部缓存。 第二步是外部缓存服务器。
我有缓存更新程序。 它会定期更新缓存,具体取决于抽象类的“ allowForUpdater”属性。 首先,我发现了所有派生类:
public static List<Type> CacheTypeList()
{
var type = typeof(CacheValueProviderBase<>);
return Assembly.GetExecutingAssembly().GetTypes().Where(i => !i.IsAbstract && !i.IsInterface &&
i.BaseType != null && i.BaseType.IsGenericType && i.BaseType.GetGenericTypeDefinition() == type
).ToList();
}
像这样迭代:
foreach (var item in CacheTypeList())
{
var cache= getCache(item);
if(cache.allowForUpdater && cache.beforeUpdate())
{
cache.UpdateForceFromCacheServer();
}
}
和getCache
方法:
public static CacheValueProviderBase<ICacheItem> getCache(Type type)
{
var val = storeList.Find(i => i.Key == type).Value;
return (CacheValueProviderBase<ICacheItem>)val;
}
storeList是静态列表,并且在应用程序中全局包含Slider_CacheValueProvider。
问题是getCache
方法。 当我尝试投射时,会收到异常。 '无法转换类型为...的对象。 从ICacheItem的基本和滑块模型实现继承的Slider_CacheValueProvider。 问题是什么? 我为什么不能投射?
更新1:
使用'out'关键字抽象类,得到以下错误:'无效的方差修饰符。 只能将接口和委托类型参数指定为“变体”。
所以我用接口更改了抽象类。 接口是:
public interface ICacheProvider<T> where T : ICacheItemID
{
DateTime LastModifiedTime { get; set; }
void Add(T model);
bool Remove(int id);
bool Update(T model);
T Where(Func<T, bool> expression);
void Clear();
int Count(int id);
IEnumerable<T> GetList();
void AddList(IEnumerable<T> model);
void RemoveList(IEnumerable<int> model);
void RemoveByFunc(Func<KeyValuePair<int, T>, bool> expression);
IEnumerable<T> WhereList(Func<T, bool> expression);
string getName();
void UpdateForceFromDataBase(bool updateCache = true);
void UpdateForceFromCacheServer();
bool allowForUpdater { get; }
bool beforeUpdate();
}
当前的抽象类是这样的:
public abstract class CacheValueProviderBase<T> : ICacheProvider<T> where T : ICacheItemID
如果我将接口更改为“ out T”,则会在Add,Update,AddList,RemoveByFunc上出错。 错误是:
“无效的方差,类型参数'T'必须在ICacheProvider上相反地有效。Add(T)(或其他方法名称)'T'是协变量。”
更新2:
我更改了代码。 我为更新程序创建了新的界面,如下所示:
public interface ICacheUpdaterImplements
{
string getName();
void UpdateForceFromDataBase();
void UpdateForceFromCacheServer();
bool allowForUpdater();
}
我得到这样的界面:
public static ICacheUpdaterImplements getCacheUpdaterImplements(Type type)
{
return (ICacheUpdaterImplements)storeList.Single(i => i.Key == type).Value;
}
然后更改更新程序代码,如下所示:
foreach (var item in CacheTypeList())
{
var updater= getCacheUpdaterImplements(item);
if(updater.allowForUpdater())
{
updater.UpdateForceFromCacheServer();
}
}
所以,我知道我的设计有误。 我更改了代码并解决了问题。
到目前为止给出的答案均不正确。 正确的是,问题在于您的类型不是协变的,但是在建议的解决方案中是错误的,因为这是非法的并且无法编译。
您的示例非常复杂,因此让我们看一个简单的示例。 如果你有:
class Animal {}
class Giraffe : Animal {}
class Tiger : Animal {}
那么此转换是合法的:
IEnumerable<Giraffe> giraffes = new List<Giraffe>() { new Giraffe() };
IEnumerable<Animal> animals = giraffes;
这是协变转换。 协变转换是一种转换,其中转换的理由是“长颈鹿可转换为动物,因此长颈鹿序列可转换为动物序列”。 也就是说,协变转换是现有转换证明更复杂的泛型转换合理的转换 。
但是,这种转换是不合法的:
IList<Giraffe> giraffes = new List<Giraffe>() { new Giraffe() };
IList<Animal> animals = giraffes;
为什么不允许这种转换? 因为它可以被滥用! 我们现在可以说
animals.Add(new Tiger());
动物的清单仍然是长颈鹿的清单 。 您不能将老虎添加到长颈鹿列表中。 您可以将老虎添加到动物列表中。 因此,即使长颈鹿是动物的亚型,“长颈鹿的列表”也不是“动物列表”的子类型。 IEnumerable<T>
允许协方差, 因为无法将老虎插入到长颈鹿序列中 。 IList<T>
不允许协方差,因为有一种方法可以滥用它。
在以下情况下,C#允许像您希望的那样进行协变转换:
协变转换中涉及的通用类型参数(即<>
-中的内容必须全部是引用类型)。 即使int
可转换为object
,也不能将List<int>
转换为IEnumerable<object>
。 int
不是引用类型。
您要转换为的“外部”泛型类型必须是接口或委托类型,而不是类或结构。
必须将接口或委托声明为支持协方差,并且编译器必须能够检查该声明是否有效,并且永远不会出现您可以将老虎放入只能容纳长颈鹿的盒子的情况。
我不知道如何立即重做复杂的逻辑以使其按您希望的方式工作。 您可能想要使用一个不太复杂的解决方案,该解决方案不那么依赖泛型。
尝试此实现,它应该可以满足您的需求...既然您广泛使用泛型,这就是要走的路-但是,如果我是您,我会重新考虑您的整个结构,因为我认为您将无法调用此方法(仅通过使用反射)
public ICacheProvider<T> getCache<T>() where T : ICacheItem
{
var val = storeList.Single(i => i.Key == typeof(T)).Value;
return (ICacheProvider<T>)val;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.