我们有一个接口可以用非常简单的定义处理DAL:
interface IRepository<T> : IQueriable<T> // so we can read data from database
{
Save(T document); // dozen of methods here
}
通常,我们使用两种实现方式:用于单元测试的真实版本和内存版本。这是类之一的声明:
public RealRepository : IRepository<AccountEntity> { ... }
// typical IOC usage
services.AddSingleton<IRepository<AccountEntity>, RealRepository<AccountEntity>>();
现在,我们正在努力将主要代码库剥离到项目的自定义版本,并且我们需要数据中的自定义字段和存储库中的偶尔自定义行为。大多数类都可以使用基本实现,但其他一些则需要特定的实现。所以我的目标是获得以下服务:
var repository = new RealRepository<CustomAccountEntity>();
services.AddSingleton(IRepository<AccountEntity>, repository);
// for new classes
services.AddSingleton(IRepository<CustomAccountEntity>, repository);
我试图将
out T
添加到IRepository,但是我在输入参数中使用了T,这给了编译时间“无效的方差”错误。我可以看到一个解决方案,方法是在接口中添加第二个类型的参数,如下所示:
IRepository<TBase, out TChild> : IQueriable<TChild> {
Save (T document);
}
最后,问题:如何使变更100%向后兼容?
我试过的
添加
IRepository<T>: IRepository<T,T>
->符合,但是RealRepository
不再实现IRepository
。在实现中添加2个接口:
public class RealRepository<TBase, TChild>: IRepository<TBase, TChild>, IRepository<TChild>
,但这会产生编译错误“无法同时实现...和...,因为它们可能会为某些类型参数替换统一” 最佳答案
Save(T document)
在T
上处于反位置。这表示in T
,而不是out T
。
让我们回顾一下矛盾的含义。假设您有以下代码:
using System;
public class Entity {}
public class AccountEntity : Entity {}
public class CustomAccountEntity : AccountEntity {}
public interface IQueryable<in T>
where T : Entity
{}
public interface IRepository<in T>
where T : Entity
{
void Save(T record);
}
public class EntityRepository<T> : IRepository<T>
where T : Entity
{
public void Save(T record) {}
}
public class Program
{
public static void Main()
{
// This is ***VALID***:
IRepository<CustomAccountEntity> repo = new EntityRepository<AccountEntity>();
Console.WriteLine(repo == null ? "cast is invalid" : "cast is valid");
}
}
https://dotnetfiddle.net/cnEdcm
因此,只要需要
IRepository<CustomAccountEntity>
,就可以使用具体的EntityRepository<AccountEntity>
实例。似乎违反直觉,但实际上是完全正确的:如果具体方法是Save(AccountEntity)
,则显然也可以处理CustomAccountEntity
实例; OTOH如果具体方法是Save(CustomAccountEntity)
,则将无法处理简单的AccountEntity
实例。话虽如此,那我认为你应该
改用相反性;
使用最专业的类型声明所有依赖项,例如
IRepository<CustomWhateverEntity>
;在IoC注册代码中,对于每个特定实体,如果需要额外的行为,请设置
Repository<CustomeWhateverEntity>
,否则设置Repository<WhateverEntity>
。关于c# - 使协变接口(interface)向后兼容,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55150924/