Problem covariance and contravariance

Given the following snippet, I completely do not understand WHY what they will not be able to achieve:

Interface:

public interface IEntityRepository<out T> : IRepository<IEntity> { void RequeryDataBase(); IEnumerable<T> Search(string pattern); Task<IEnumerable<T>> SearchAsync(string pattern); SearchContext Context { get; } string BaseTableName { get; } } 

In IRepository<IEntity> are only simple common CRUDs .

I get an error on this line: Task<IEnumerable<T>> SearchAsync(string pattern);

Error:

The return type of the method must be safe. invalid variance: a parameter of type T must be invariant for the problem

Please help me understand why I cannot use <out T> with Task<T>

+6
source share
3 answers

Task<T> not covariant. Rejection can only apply to common interfaces (and delegates, but that doesn't matter here).

eg. Task<IEnumerable<Dog>> cannot be assigned Task<IEnumerable<Animal>> . Because of this, your interface cannot be marked as covariant.

You might want to see this related question .

+9
source

When deciding whether to change an argument of a general type in any common interface, you need to consider all the uses of the argument of a general type inside the interface. Each use may result in some dispersion restrictions. It includes:

  • Used as an input argument to a method - prohibits covariance
  • Used as a return value from a method - prohibits contravariance
  • Used as part of another generic derivation, such as Task<T> - such use may prohibit covariance, prohibit contravariance, or both

I used negative logic to emphasize that all of these cases basically introduce restrictions. After the analysis, you will find out if any element has forbidden covariance, and then your argument cannot be declared as out . Conversely, if any element rejects contravariance, your argument may not be declared as in .

In your particular case, the first Search method returns an IEnumerable<T> , while the contravariance of the rendering is not applicable. But then SearchAsync returns Task<IEnumerable<T>> , and this use introduces restrictions that exist in the Task type - it is invariant, which means that this time out also impossible.

As a result, your common interface must be invariant in its type of general arguments in order to satisfy the signatures of all its methods.

+5
source

You can cheat and use

 IObservable<out T> 

which is almost the same as Task<T> , but with a covariant type. It can always be converted into a task when you need it. You lose code readability a bit, because with Task<T> it is assumed (and forced) that you get only one result, but with IObservable<T> you can get a lot. However you can do

 IObservable<T> o = ... var foo = await o; 

just like

 Task<T> t = ... var foo = await t; 

Note that you will need to include the System.Reactive.Linq from the Rx library to make this work. It will add an extension method to IObservable<> , which will make it expected.

0
source

All Articles