2013-08-07 29 views
4

我为我的存储库创建了此接口。逆向值类型

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方法接受一个I​​D,它将用于在主键上搜索。通过使用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

我怀疑是编译器并没有抱怨,因为'int'是'sealed'等都将永不进入共同或禁忌变种使用。 * Variance *是关于当* variant *类型的派生类型替换基类型时,编译器如何处理一般类型类的​​用法。例如。 'List animals = new List ();'是协变的。和'((IRepository )repo).Update(animal);'是逆变的。 –

+0

@KeithPayne:'列表'是一类。类和协变不支持,因此代码不会编译。分配的目标需要是协变接口。 –

+0

@DanielHilgarth谢谢丹尼尔。 这个例子应该是IEnumerable animals = new List ();' 而第二个例子也不是很好。 '((IUpdateOnlyRepository )repo).Update(animal);'更好,因为使用普通的旧版本库意味着也会返回变体类型的方法。 –

回答

6

Covariance and contravariance对价值类型没有多少意义,因为它们都是封闭的。虽然从文档中不清楚,但使用struct作为co/contravariant类型是有效的,但它并不总是有用。您引用的文档是最有可能指的是以下是无效的:

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; 

如果你想限制都TKeyTclass ES(引用类型),你应该包括第二限制:

public interface IRepository<T, in TKey> 
    where T : class 
    where TKey : class 
+0

谢谢你的解释,你说得很清楚。 – James

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; 

逆变现在意味着您可以分配一个实例o f IRepository<IBar>与变量referenceTypeRepository相关联,因为BarReferenceType实现了IBar
您引用的MSDN部分仅仅意味着IRepository<IBar>valueTypeRepository的实例的分配不合法,但BarValueType也实现了IBar

0

在这篇文章中,他们告诉我们,类型参数是由编译器为不变处理:

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

来源:http://msdn.microsoft.com/en-us/library/dd799517.aspx

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