Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

There is no implicit reference conversion from 'System.Collections.Generic.List<T>' to 'T'

Tags:

class Class1<T> {     public virtual void Update(T entity)     {         Update(new List<T>() { entity }); //It's failed     }      public virtual void Update(IEnumerable<T> entities)     {     }      public virtual void Update<TSub>(TSub entity) where TSub : T     {     }      public virtual void Update<TSub>(IEnumerable<TSub> entities) where TSub : T     {     } } 

I have a piece of code. But it always failed.

If I replaced Update(new List<T>() { entity }) by Update((new List<T>() { entity }).AsEnumerable()), it will be ok.

It will be ok too when you delete the third method Update<TSub>(TSub entity) where TSub : T.

Can anybody tell me why?

like image 446
Jailu Lee Avatar asked Mar 22 '13 10:03

Jailu Lee


1 Answers

OK, let's go through this carefully. We have

Update(new List<T>());  

And three candidates -- note that we care only about the signatures of those candidates, so we'll strip away the return types and constraints, which are not part of the signature:

Update(IEnumerable<T> entities) Update<U>(U entity)  Update<V>(IEnumerable<V> entities)  

Our first task is to do type inference on those last two candidates. If inference fails then they are not applicable candidates.

Consider the second method

Update<U>(U entity)  

We have an argument of type List<T> and a formal parameter U. Therefore we infer that U is List<T>.

Consider the third method:

Update<V>(IEnumerable<V> entities) 

We have an argument of type List<T> and a formal parameter of type IEnumerable<V>. List<T> implements IEnumerable<T> so we deduce that V is T.

OK, so our candidate list now consists of:

Update(IEnumerable<T> entities) Update<List<T>>(List<T> entity)  Update<T>(IEnumerable<T> entities)  

Are all of these candidates applicable? Yes. In each case List<T> is convertible to the formal parameter type. We cannot eliminate any of them yet.

Now that we have only applicable candidates we must determine which one is the unique best.

We can immediately eliminate the third one. The third one and the first one are identical in their formal parameter lists. The rule of C# is that when you have two methods that are identical in their formal parameter lists, and one of them got there "naturally" and one of them got there via type substitution, the substituted one loses.

We can also eliminate the first one. Clearly the exact match in the second one is better than the inexact match in the first one.

That leaves the second one as the last man standing. It wins the overload resolution fight. Then during final validation we discover that the constraint is violated: List<T> is not guaranteed to be a derived class of T.

Therefore overload resolution fails. Your arguments caused the best method chosen to be invalid.

If I call Update((new List<T>() { entity }).AsEnumerable()), it will be ok.

Correct. Go through it again. Three candidates:

Update(IEnumerable<T> entities) Update<U>(U entity)  Update<V>(IEnumerable<V> entities)  

We have an argument of type IEnumerable<T>, so we infer the second and third to be:

Update(IEnumerable<T> entities) Update<IEnumerable<T>>(IEnumerable<T> entity)  Update<T>(IEnumerable<T> entities)  

Now we have three applicable candidates with identical parameter lists. The ones that got there under construction are automatically worse than the natural ones, so we eliminate the second and third, leaving only the first. It wins, and it has no constraints to be violated.

It will be ok too when you delete the third method

Your statement is false; this will produce the same error as the first scenario. Taking away the third candidate does not cause the first candidate to suddenly start beating the second candidate.

like image 130
Eric Lippert Avatar answered Sep 28 '22 09:09

Eric Lippert