在 c# 中从其接口的泛型类型存储和访问 class 的实现特定数据

storing and accessing implementation specific data of a class from generic type of its interface in c#

我正在尝试编写一个通用的缓存存储库,为每个正在缓存的数据类型添加一个基本键。

    public interface ICacheModel
    {
        private static readonly string _baseKey;
        public static string GetBaseCacheKey()
        {
            return _baseKey;
        }
    }
    public interface ICacheRepository<T> where T : class, ICacheModel
    {
        Task<T> GetAsync(string key);
    }
    public class CacheRepository<T> : ICacheRepository<T> where T : class, ICacheModel
    {
        private readonly IDistributedCache _distributedCache;

        public CacheRepository(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }

        public async Task<T> GetAsync(string key)
        {
            var res = await _distributedCache.GetAsync<T>(T.GetBaseCacheKey() + key );
            return res;
        }
    }

然后我从 ICacheModel 继承我计划缓存的任何对象,并在我的项目中需要缓存的任何地方注入 ICacheRepository。

但由于编译器错误 CS0119

,我无法访问静态方法 GetBaseCacheKey()

知道如何使用此功能吗?

更新:


    public class CacheSampleClass : ICacheModel
    {
        public Guid Id { get; set; }
        public string Prop1 { get; set; }
        public string Prop2 { get; set; }

        private static readonly string _baseKey = "CacheSampleClass-";
        public static string GetBaseCacheKey()
        {
            return _baseKey;
        }
    }

这是我要缓存的继承 classes 的示例。 我希望 GetBaseCacheKey 在由 CacheSampleClass 生成的所有对象中是唯一的,并且不同于由继承 ICacheModel

的其他 class 生成的所有对象

最简单的解决方案可能是仅使用反射来获取 class 名称作为“baseCacheKey”。

_distributedCache.GetAsync<T>(typeof(T).FullName  + key );

另一种解决方案是将基键定义为缓存对象的参数,而不是类型的参数:

public CacheRepository(IDistributedCache distributedCache, string baseKey) 
...

第三种解决方案是在接口中定义一个属性:

public interface ICacheModel
{
        public string BaseKey {get;}
}

所述接口的实现可以只提供 returns 常量或文字值的 get 方法的实现,因此没有任何针对此对象的字段。但是 属性 不能像发布的示例那样是静态的。

每种选择都有一些优点和缺点,所以这取决于你想做什么。

接口中的静态成员是在 C# 8.0 中引入的,我建议您避免使用它们,因为接口通常应该断开实现(字段)与契约(方法或 属性)的连接。

在你的情况下,最好转换为常规 属性

public interface ICacheModel
{
   string GetBaseCacheKey();
}

并将使用静态字段的实现移动到实现您的接口

的class

不能实现静态接口方法;静态成员仅属于它们的声明类型。

您可以改为这样做:

public interface ICacheModelKeyProvider<T> where T : class
{
    string BaseKey { get; }
}

然后在您的存储库中:

public class CacheRepository<T> : ICacheRepository<T> where T : class
{
    private readonly IDistributedCache distributedCache;
    private readonly ICacheModelKeyProvider<T> keyProvider;

    public CacheRepository(
        IDistributedCache distributedCache,
        ICacheModelKeyProvider<T> keyProvider)
    {
        this.distributedCache = distributedCache;
        this.keyProvider = keyProvider;
    }

    public async Task<T> GetAsync(string key)
    {
        var res = await _distributedCache.GetAsync<T>(keyProvider.BaseKey + key);
        return res;
    }
}

那么您创建的每个模型类型都需要 ICacheModelKeyProvider 才能拥有存储库。

例如

public class CacheSampleClass
{
    public Guid Id { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
}

public class CacheSampleClassKeyProvider : ICacheModelKeyProvider<CacheSampleClass>
{
    public string BaseKey { get; } = "CacheSampleClass-"; 
}

或者,如果您想要默认行为,您甚至可以使用通用提供程序:

public class DefaultKeyProvider<T> : ICacheModelKeyProvider<T>
{
    public string BaseKey { get; } = $"{typeof(T).Name}-"; 
}

如果感觉更整洁,您甚至可以嵌套 class:

public class CacheSampleClass
{
    public Guid Id { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }

    public class KeyProvider : DefaultKeyProvider<CacheSampleClass> { }
}