c# - 从通用抽象类派生类和转换错误?
问题描述
我有抽象基类,例如:
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
方法。当我尝试投射它时,我收到一个异常。'无法转换类型的对象......'。Slider_CacheValueProvider 继承自基础,滑块模型实现自 ICacheItem。问题是什么?为什么我不能投?
更新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# 允许在以下情况下进行协变转换,例如您想要的转换:
协变转换中涉及的泛型类型参数——即
<>
--- 中的东西必须都是引用类型。即使可以转换List<int>
为,您也不能转换为。不是引用类型。IEnumerable<object>
int
object
int
您要转换为的“外部”泛型类型必须是接口或委托类型,而不是类或结构。
接口或委托必须声明为支持协变,并且编译器必须能够检查声明是否有效,并且永远不会产生您可以将老虎放入只能容纳长颈鹿的盒子的情况。
我不知道如何重做复杂的逻辑以使其按您想要的方式工作。您可能想要使用不太依赖泛型的不太复杂的解决方案。
推荐阅读
- python-3.x - 使用 Python 模块 xml.etree.ElementTree 解析有点复杂的 XML 并将值存储在列表中
- mysql - 2个表列之间的SQL差异
- javascript - Math.random() 结果似乎颠倒了
- javascript - Js fetch api post undefined as value not string
- python - 如何在 selenium 中正确设置 firefox 配置文件?
- flutter - 来自flutter的小部件ios14
- angular - 当我尝试将数组添加到我的角度同步融合网格时出现此错误
- android-studio - 单击单选按钮时如何显示图像
- c# - 如何提取第一个字符和之后的数字,直到在字符串中找到一个字符(az) - C# 2.0
- javascript - 如何从 React Native FlatList 中的异步函数返回值?