c# - 从通用抽象类派生类和转换错误?

标签 c# generics casting abstract-class

我有抽象基类,如:

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
        }
    }

slider 缓存模型:

public class Cache_Home_Slider_Model : ICacheItemID
    {
        public int ID { get; set; }

        public string SlideImage { get; set; }

        public string Link { get; set; }
    }

所有的缓存模型都依赖于 ID 属性,实现这个接口(interface)只是为了方便 crup 操作:

public interface ICacheItemID
    {
        int ID { get; set; }
    }

信息:我的缓存机制有两个步骤。第一步是内部缓存。第二步是外部缓存服务器。

我有缓存更新程序。它定期更新缓存,这取决于抽象类的“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 继承自 base 和 slider model implement from ICacheItem。问题是什么?为什么我不能转换?

更新 1:

使用'out'关键字来抽象类,得到这个错误: '无效的方差修饰符。只有接口(interface)和委托(delegate)类型参数可以指定为变体。

所以我用接口(interface)改变了抽象类。界面是:

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不是引用类型。

  • 您要转换为的“外部”泛型类型必须是接口(interface)或委托(delegate)类型,而不是类或结构。

  • 接口(interface)或委托(delegate)必须声明为支持协变,并且编译器必须能够检查声明是否有效并且绝不会产生可以将老虎放入只能装长颈鹿的盒子。

我不知道如何重做复杂的逻辑以使其按您想要的方式工作。您可能希望采用一种不那么依赖泛型的不太复杂的解决方案。

关于c# - 从通用抽象类派生类和转换错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53969986/

相关文章:

c# - 如何在我们的网页中添加区域语言?

c# - 与 url 不同的操作名称

c# - GC.Collect() 和 GC.Collect(GC.MaxGeneration) 有什么区别?

c# - ASP.NET MVC 项目层

generics - 我是否需要多个 impl block 来实现多个为类型参数设置具体类型的方法?

java - 使用泛型类和静态方法的 Eclipse 自动完成

c - C 中的文字字符 : is it an int or a char?

java - 类型不匹配 : cannot convert from HashMap<Integer, Item> 到 HashMap<Integer,Item>

delphi - delphi中有这个VB代码的相关语法吗?

swift - 如何在 Swift 中转换 @Binding