Ser*_*kan 1 c# generics abstract-class casting
我有抽象基类,如:
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;
        }
    }
Run Code Online (Sandbox Code Playgroud)
我有来自这个抽象类的多个派生类.以下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
        }
    }
Run Code Online (Sandbox Code Playgroud)
滑块缓存模型:
public class Cache_Home_Slider_Model : ICacheItemID
    {
        public int ID { get; set; }
        public string SlideImage { get; set; }
        public string Link { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)
所有缓存模型都依赖于ID属性并实现此接口只是为了简化crup操作:
public interface ICacheItemID
    {
        int ID { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)
信息:我的缓存机制有两个步骤.第一步是内部缓存.第二步是外部缓存服务器.
我有缓存更新程序.它定期更新缓存,这取决于抽象类'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();
        }
Run Code Online (Sandbox Code Playgroud)
并迭代如下:
foreach (var item in CacheTypeList())
{
    var cache= getCache(item);
    if(cache.allowForUpdater && cache.beforeUpdate())
    {
        cache.UpdateForceFromCacheServer();
    }
}
Run Code Online (Sandbox Code Playgroud)
和getCache方法:
public static CacheValueProviderBase<ICacheItem> getCache(Type type)
{
    var val = storeList.Find(i => i.Key == type).Value;
    return (CacheValueProviderBase<ICacheItem>)val;
}
Run Code Online (Sandbox Code Playgroud)
storeList是静态列表,在app上包含Slider_CacheValueProvider全局.
问题是getCache方法.当我尝试施放它时,我收到一个例外.'无法投射类型对象......'.Slider_CacheValueProvider继承自ICacheItem的base和slider模型实现.问题是什么?我为什么不能演员?
更新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();
    }
Run Code Online (Sandbox Code Playgroud)
而目前的抽象类是这样的:
public abstract class CacheValueProviderBase<T> : ICacheProvider<T> where T : ICacheItemID
Run Code Online (Sandbox Code Playgroud)
如果我将接口更改为"out T",则会在Add,Update,AddList,RemoveByFunc上出现错误.错误是:
"无效方差,Type参数'T'必须在ICacheProvider.Add(T)(或其他方法名称)上有效,"T'是协变量."
更新2:
我改变了我的代码.我为updater创建了这样的新界面:
public interface ICacheUpdaterImplements
{
        string getName();
        void UpdateForceFromDataBase();
        void UpdateForceFromCacheServer();
        bool allowForUpdater();
}
Run Code Online (Sandbox Code Playgroud)
我得到这样的界面:
public static ICacheUpdaterImplements getCacheUpdaterImplements(Type type)
        {
            return (ICacheUpdaterImplements)storeList.Single(i => i.Key == type).Value;
        }
Run Code Online (Sandbox Code Playgroud)
并更改更新程序代码,如下所示:
foreach (var item in CacheTypeList())
{
    var updater= getCacheUpdaterImplements(item);
    if(updater.allowForUpdater())
    {
        updater.UpdateForceFromCacheServer();
    }
}
Run Code Online (Sandbox Code Playgroud)
所以,我知道,我的设计错了.我改变了代码并解决了问题.
到目前为止给出的答案都不正确.他们是正确的,问题是你的类型不是协变的,但在提出的解决方案中是错误的,这是非法的,不会编译.
你的例子非常复杂,让我们看一个更简单的例子.如果你有:
class Animal {}
class Giraffe : Animal {}
class Tiger : Animal {}
Run Code Online (Sandbox Code Playgroud)
然后这种转换是合法的:
IEnumerable<Giraffe> giraffes = new List<Giraffe>() { new Giraffe() };
IEnumerable<Animal> animals = giraffes;
Run Code Online (Sandbox Code Playgroud)
这是一种协变转换.协变转换是一种转换,其中转换的理由是"长颈鹿可转换为动物,因此一系列长颈鹿可转换为动物序列".也就是说,协变转换是指现有转换证明更复杂的通用转换的转换.
但是,此转换不合法:
IList<Giraffe> giraffes = new List<Giraffe>() { new Giraffe() };
IList<Animal> animals = giraffes;
Run Code Online (Sandbox Code Playgroud)
为什么不允许这种转换?因为它可以被滥用!我们现在可以说
animals.Add(new Tiger());
Run Code Online (Sandbox Code Playgroud)
动物名单仍然是长颈鹿的名单.你不能将老虎添加到长颈鹿列表中.您可以将老虎添加到动物列表中.因此,"长颈鹿名单"不是"动物名单"的子类型,即使长颈鹿是动物的亚型.IEnumerable<T>允许协方差,因为没有办法将虎插入一系列长颈鹿.IList<T>不允许协方差,因为有一种方法可以滥用它.
C#允许在下列情况下进行协变转换,例如您想要的转换:
协变转换中涉及的泛型类型参数 - 即<>---中的东西- 必须都是引用类型.你不能,比如说转换List<int>为IEnumerable<object>即使int可转换为object.int不是参考类型.
要转换为的"外部"泛型类型必须是接口或委托类型,而不是类或结构.
接口或委托必须声明为支持协方差,并且编译器必须能够检查声明是否有效并且永远不会产生这样的情况,即您可以将老虎放入只能容纳长颈鹿的盒子中.
我不知道如何重做你复杂的逻辑,让它按照你想要的方式工作.您可能希望采用不那么复杂的解决方案,而不依赖于泛型.