c# Generics "in" keyword

5.1k views Asked by At

I've recently been assigned to some maintenance work on an existing application. I've come across the following code:

public interface IEntityService<T, in TKey>
{
    T GetEntityById(TKey id);    
    IEnumerable<T> GetAll();    
    void Update(T entity);    
    void Delete(TKey key);
}

I'm not sure what the in keyword does for the second generic argument, TKey.

I came across the following MSDN Article which (should) explain it to me perfectly:
in (Generic Modifier) (C# Reference)

However, I don't truly understand it. Here's what it says:

For generic type parameters, the in keyword specifies that the type parameter is contravariant. You can use the in keyword in generic interfaces and delegates.

Contravariance enables you to use a less derived type than that specified by the generic parameter. This allows for implicit conversion of classes that implement variant interfaces and implicit conversion of delegate types. Covariance and contravariance in generic type parameters are supported for reference types, but they are not supported for value types.

A type can be declared contravariant in a generic interface or delegate if it is used only as a type of method arguments and not used as a method return type. Ref and out parameters cannot be variant.

An interface that has a contravariant type parameter allows its methods to accept arguments of less derived types than those specified by the interface type parameter. For example, because in .NET Framework 4, in the IComparer interface, type T is contravariant, you can assign an object of the IComparer(Of Person) type to an object of the IComparer(Of Employee) type without using any special conversion methods if Employee inherits Person.

A contravariant delegate can be assigned another delegate of the same type, but with a less derived generic type parameter.

I suppose this makes sense, but particularly citing

Contravariance enables you to use a less derived type than that specified by the generic parameter.

How can it be of any use to an int? Is there any "less derived type" I would ever pass in?

2

There are 2 answers

0
xanatos On BEST ANSWER

I note that the only reference of int is in the last line of your question. Are you sure that IEntityService<> is to be used only with int keys? Probably it was built for Compound Key (primary keys composed of multiple columns)

Now, in NHibernate for example, for compound keys you use a whole class to represent them, so you could have for a table MyTable a

class MyTableKey 
{ 
    public int Code;
    public int SubCode;
}

If you have a secondary table MuSubtable connected to that table you could then have

class MySubtableKey : MyTableKey 
{
    public int SubSubCode; 
}

where MySubtable is a table that has as the primary key the full primary key of MyTable (Code + SubCode) plus another field (SubSubCode).

0
Jay Dubal On

Suppose you have two different entities, one has primary key column with data type as "integer" and another one is using a "string" type.

Then you could use IEntityService as:

public class PersonService : IEntityService<Person, int>
{
   int _id;
   ...
}

public class LogService : IEntityService<Log, string>
{
   string _id;        
   ...
}