Reputation: 54117
This question is about when you do and don't need to include generic type specification arguments. It's a bit lengthy, so please bear with it.
If you have the following (contrived) classes...
public abstract class UserBase
{
public void DoSomethingWithUser() { }
}
public class FirstTimeUser : UserBase
{
/* TODO: some implementation */
}
The following method...
private static void DoThingsWithUser<TUser>(TUser user) where TUser : UserBase
{
user.DoSomethingWithUser();
}
Can be called with or without having to specify the type argument TUser
...
var user = new FirstTimeUser();
DoThingsWithUser<FirstTimeUser>(user);
DoThingsWithUser(user); // also valid, and less typing required!
So far, so good.
But if you add a couple more (again, contrived) classes...
public abstract class UserDisplayBase<T> where T : UserBase
{
public T User { get; protected set; }
}
public class FirstTimeUserDisplay : UserDisplayBase<FirstTimeUser>
{
public FirstTimeUserDisplay()
{
User = new FirstTimeUser();
}
}
And a method...
private static void DoThingsWithUserDisplay<TDisplay, TUser>(TDisplay userDisplay)
where TDisplay : UserDisplayBase<TUser>
where TUser : UserBase
{
userDisplay.User.DoSomethingWithUser();
}
When calling this method, it is mandatory to include the type arguments...
var userDisplay = new FirstTimeUserDisplay();
DoThingsWithUserDisplay<FirstTimeUserDisplay, FirstTimeUser>(userDisplay); // Type arguments required!
If you don't specify the type arguments, you will get a compiler error of
The type arguments for method 'DoThingsWithUserDisplay(TDisplay)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
I think that the compiler should/could be smart enough to figure this out...or is there a subtle reason why not?
Upvotes: 6
Views: 475
Reputation: 4059
As the compiler is analyzing the types to infer, it reaches a dead end when looking at DoThingsWithUserDisplay(userDisplay);
It finds that TDisplay
must be of type FirstTimeUserDisplay
, which is good. Then the constraint says that TDisplay
must inherit from UserDisplayBase<TUser>
.
Since it does not know the type for TUser
it cannot determine if FirstTimeUserDisplay
inherits from UserDisplayBase<TUser>
and it also can't infer that the TUser
parameter should whatever type is in the generic UserDisplayBase<TUser>
.
Edit: Incidentally, you can get the type inference you seek using an interface with a variant. In this case, the interface definition provides sufficient inheritance information so that the constraints are met.
public abstract class UserDisplayBase<T> : IUserDisplayBase<T>
where T : UserBase
{
public T User { get; protected set; }
}
public interface IUserDisplayBase<out T>
where T : UserBase
{
T User { get; }
}
private static void DoThingsWithUserDisplay<TDisplay>(TDisplay userDisplay)
where TDisplay : IUserDisplayBase<UserBase>
{
userDisplay.User.DoSomethingWithUser();
}
can be called with
var userDisplay = new FirstTimeUserDisplay();
DoThingsWithUserDisplay(userDisplay);
Upvotes: 2