C# 逆变值类型

C# 逆变值类型,c#,.net,contravariance,C#,.net,Contravariance,我已经为我的存储库创建了这个接口 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);

我已经为我的存储库创建了这个接口

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);
}
为什么在将
TKey
指定为值类型的生成中没有得到异常?此外,如果我从参数中删除了中的
,我丢失了什么?MSDN文档说,相反的方法允许使用派生较少的类型,但是通过在
中删除
,我可以传递任何类型,因为它仍然是泛型的

这可能表明我对逆变和协方差缺乏理解,但这让我有点困惑。

对于值类型没有太多意义,因为它们都是密封的。虽然文档中没有明确说明,但将
struct
用作协变/逆变类型是有效的,但它并不总是有用的。您引用的文档很可能是指以下内容无效:

public struct MyStruct<in T>
如果您试图将
TKey
T
限制为
class
es(参考类型),则应包括第二个限制:

public interface IRepository<T, in TKey>
    where T : class
    where TKey : class
公共接口IRepository
T:在哪里上课
TKey在哪里:类

事实上,您缺少协变和逆变的全部要点:-)这是关于能够将一个泛型类型的变量分配给另一个相同泛型类型但具有与源中使用的变量相关的不同泛型类型参数的变量。
根据泛型类型参数是共变还是逆变,允许不同的赋值

假设以下接口:

public interface IRepository<in T>
{
    void Save(T value);
}
最后,假设两个变量:

IRepository<BarReferenceType> referenceTypeRepository;
IRepository<BarValueType> valueTypeRepository;
i存储引用类型存储库;
IRepository valueTypeRepository;
逆变现在意味着您可以将
IRepository
的实例分配给变量
referenceTypeRepository
,因为
BarReferenceType
实现了
IBar


您引用的MSDN中的部分仅表示将
IRepository
的实例分配给
valueTypeRepository
是不合法的,尽管
BarValueType
也实现了
IBar

,但在本文中,他们告诉我们类型参数被编译器视为不变量:

差异仅适用于引用类型;如果指定值类型 对于变量类型参数,该类型参数对于 生成的构造类型


From:

使用值类型实现接口没有问题。例如,您仅在尝试将
IRepository
分配给
IRepository
时才会出现错误。在以下代码中,不会编译最后一个赋值:

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;
    }
}
公共接口IContravariant,其中T:class
{
T FindSingle(TKey id);
}
公共类objCV:IContravant
{
公共项目FindSingle(对象id)
{
返回null;
}
公共静态无效测试()
{
objCV objCV=新的objCV();
i创新项目;
IContravant intcv;
projcv=objcv;
intcv=objcv;
}
}

我怀疑编译器没有抱怨,因为
int
密封的
,因此永远不会被用于协变或反变。方差是指当用变量类型的派生替换基类型时,编译器如何处理泛型类型类的用法。例如,列出动物=新列表()是协变的。更新(动物)
是反变的。@KeithPayne:
List
是一个类。类不支持协变和逆变,因此该代码无法编译。分配的目标必须是协变接口。@DanielHilgarth感谢Daniel。示例应该是
IEnumerable animals=new List()第二个例子也不是很好<代码>((IUpdateOnlyRepository)回购)。更新(动物)
更好,因为使用普通的旧存储库意味着还会返回变量类型的方法。@KeithPayne:正确。
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;
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;
    }
}