I have these classes:
/* Data classes */
public class Data
{
public int Id { get; set; }
}
public class InfoData<TInfo> : Data
where TInfo: InfoBase
{
public TInfo Info { get; set; }
}
/* Info classes */
public abstract class InfoBase
{
public int Id { get; set; }
}
public interface IRelated
{
IList<InfoBase> Related {get; set;}
}
public class ExtraInfo : InfoBase, IRelated
{
public string Extras { get; set; }
public IList<InfoBase> Related { get; set; }
}
Then I have two generic methods with this signature:
public TData Add<TData>(TData data)
where TData: Data
public TData Add<TData, TInfo>(TData data)
where TData: InfoData<TInfo>
where TInfo: InfoBase, IRelated
Now when I create an instance of Data class and call Add method
// data is of type Data
Add(data);
The first generic method is used and generic type Data is correctly inferred.
But when I call the same method with a more implemented type object instance
// data is of type InfoData<ExtraInfo>
// ExtraInfo is of type InfoBase and implements IRelated
Add(data);
I would expect the second generic method to be invoked, but to my surprise it's not. If I check generic type constraints on the second one being:
where TData: InfoData<TInfo>
where TInfo: InfoBase, IRelated
The first one matches and the second one as well. And these types are more implemented than simple Data type if that makes any difference.
Working example
Here is a working .Net Fiddle for you to play with.
Questions
Why is second method not being called because both generic type constraints match and could be inferred?- How can I rewrite my second
Addmethod so type inference would work and won't have to explicitly provide these types just to make sure that correct overload is being used?
Edit
I've found the answer to my first question in MSDN documentation
The compiler can infer the type parameters based on the method arguments you pass in; it cannot infer the type parameters only from a constraint or return value.
In my case the first generic type can be inferred directly from parameter, but second one is more tricky. It can't be inferred from parameter only. Type constraint should be used, but compiler doesn't evaluate it.
I also have one possible solution to my second question that changes one type to concrete and keeps the other one generic.
public InfoData<TInfo> Add<TInfo>(InfoData<TInfo> data)
where TInfo: InfoBase, IRelated
But I'm wondering if there's a more general/generic way of mitigating around this problem so I can still keep both type parameters but somehow have both types as generic?
.Add((Data) new ExtraInfo())(contrived, but you get the issue) is supposed to call your "more specialized" method, rather than the one matching the argument type. If you are concerned about a simple implementation, either call the second methodAddInfoDataso callers are forced to pick the "right" one, or use double dispatch to benefit from virtual methods (Foo.Add(Data)invokesData.AddTo(Foo)whichInfoDataoverrides), where the correct method will be called at runtime. - Jeroen Mostert