The general arguments of a type specification are sometimes optional, but not always

This question is about when you do this, and you don't need to include type argument spec arguments. It is a little long, so please feel free to.

If you have the following (far-fetched) classes ...

public abstract class UserBase { public void DoSomethingWithUser() { } } public class FirstTimeUser : UserBase { /* TODO: some implementation */ } 

Next method ...

 private static void DoThingsWithUser<TUser>(TUser user) where TUser : UserBase { user.DoSomethingWithUser(); } 

It can be called with or without an argument of type 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, far-fetched) 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 the method ...

 private static void DoThingsWithUserDisplay<TDisplay, TUser>(TDisplay userDisplay) where TDisplay : UserDisplayBase<TUser> where TUser : UserBase { userDisplay.User.DoSomethingWithUser(); } 

When calling this method, you must include arguments of the type ...

 var userDisplay = new FirstTimeUserDisplay(); DoThingsWithUserDisplay<FirstTimeUserDisplay, FirstTimeUser>(userDisplay); // Type arguments required! 

If you do not specify type arguments, you will get a compiler error

Type arguments for the DoThingsWithUserDisplay (TDisplay) method cannot be taken out of use. Try explicitly specifying type arguments.

I think the compiler should / could be smart enough to figure this out ... or is there a subtle reason why not?

+6
source share
1 answer

Because the compiler parses the types for output, it reaches a dead end when viewing DoThingsWithUserDisplay(userDisplay);

He found that TDisplay should be of type FirstTimeUserDisplay , which is good. Then the restriction says that TDisplay should inherit from UserDisplayBase<TUser> .

Since he does not know the type for TUser , he cannot determine whether FirstTimeUserDisplay from UserDisplayBase<TUser> , and also cannot deduce that the TUser parameter must have a type that is in the general UserDisplayBase<TUser> .

Edit: By the way, you can get a query of the type you are looking for using the interface with the option. In this case, the definition of the interface provides sufficient information for inheritance, so that the restrictions 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); 
+2
source

Source: https://habr.com/ru/post/982440/


All Articles