我为我的存储库创建了这个界面。
public interface IRepository<T, in TKey> where T: class
{
IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
IEnumerable<T> FindAll();
T FindSingle(TKey id);
void Create(T entity);
void Delete(T entity);
void Update(T entity);
}
FindSingle
方法接受一个ID,该ID将用于搜索主键。使用in
我希望我只能将引用类型作为TKey
传递。出于好奇,我决定创建一个具体的类并将其指定为int,所以我可以看到异常。
我查了MSDN并指出这不起作用
参考类型支持泛型类型参数的协方差和逆变,但值类型不支持它们。
我创建的课程看起来像这样
public class ProjectRepository : IRepository<Project,int>
{
public IEnumerable<Project> Find(Expression<Func<Project, bool>> predicate)
{
throw new NotImplementedException();
}
public IEnumerable<Project> FindAll()
{
throw new NotImplementedException();
}
public Project FindSingle(int id)
{
throw new NotImplementedException();
}
public void Create(Project entity)
{
throw new NotImplementedException();
}
public void Delete(Project entity)
{
throw new NotImplementedException();
}
public void Update(Project entity)
{
throw new NotImplementedException();
}
}
为什么我没有在指定TKey
作为值类型的构建中获得异常?另外,如果我从参数中删除in
我丢失了什么? MSDN文档说逆向允许使用较少派生的类型,但肯定通过删除in
我可以传递任何类型,因为它仍然是通用的。
这可能表现出对逆变和协方差的理解不足,但它让我有些困惑。
答案 0 :(得分:6)
Covariance and contravariance对价值类型没有多大意义,因为它们都是密封的。虽然从文档中不清楚,但使用struct
作为共变/逆变类型是有效的,但它并不总是有用。您引用的文档很可能是指以下内容无效:
public struct MyStruct<in T>
逆变法意味着您可以执行以下示例:
IRepository<string, Base> b = //something
IRepository<string, Derived> d = b;
由于没有任何内容来自int
,您可以使用IRepository<string, int>
,但只能使用IRepository<string, int>
。
协方差意味着您可以反过来,例如IEnumerable<T>
是out T
,是协变的。您可以执行以下操作:
IEnumerable<Derived> d = //something
IEnumerable<Base> b = d;
如果您尝试将TKey
和T
限制为class
es(引用类型),则应包含第二个限制:
public interface IRepository<T, in TKey>
where T : class
where TKey : class
答案 1 :(得分:2)
事实上,你错过了共同和逆转的全部观点:-)它是关于能够将泛型类型的变量分配给同一泛型类型的另一个变量,但具有不同的泛型类型参数与源中使用的相关 根据泛型类型参数是共变量还是逆变量,允许不同的赋值。
假设以下界面:
public interface IRepository<in T>
{
void Save(T value);
}
此外,假设以下接口以及实现它的值类型和引用类型:
public interface IBar
{
}
public struct BarValueType : IBar
{
}
public class BarReferenceType : IBar
{
}
最后,假设两个变量:
IRepository<BarReferenceType> referenceTypeRepository;
IRepository<BarValueType> valueTypeRepository;
现在,逆向变换意味着您可以将IRepository<IBar>
的实例分配给变量referenceTypeRepository
,因为BarReferenceType
实现了IBar
。
您引用的MSDN中的部分仅表示将IRepository<IBar>
的实例分配给valueTypeRepository
是不合法的,尽管BarValueType
也实现了IBar
。
答案 2 :(得分:1)
使用值类型实现接口没有问题。例如,尝试将IRepository<Project, object>
分配给IRepository<Project, int>
时,您只会收到错误消息。在以下代码中,最后一个赋值将无法编译:
public interface IContravariant<T, in TKey> where T : class
{
T FindSingle(TKey id);
}
public class objCV : IContravariant<Project, object>
{
public Project FindSingle(object id)
{
return null;
}
public static void test()
{
objCV objcv = new objCV();
IContravariant<Project, Project> projcv;
IContravariant<Project, int> intcv;
projcv = objcv;
intcv = objcv;
}
}
答案 3 :(得分:0)
在本文中,他们告诉我们编译器将type参数视为不变:
差异仅适用于参考类型;如果指定值类型 对于变体类型参数,该类型参数为不变 结果构造类型。