通用类型规范参数 - 有时是可选的,但并非总是如此

Ric*_*ett 6 c# generics

这个问题是关于何时做,不需要包含泛型类型规范参数.它有点冗长,所以请耐心等待.

如果您有以下(人为)课程......

public abstract class UserBase
{
    public void DoSomethingWithUser() { }
}

public class FirstTimeUser : UserBase
{
     /* TODO: some implementation */
}
Run Code Online (Sandbox Code Playgroud)

以下方法......

private static void DoThingsWithUser<TUser>(TUser user) where TUser : UserBase
{
    user.DoSomethingWithUser();
}
Run Code Online (Sandbox Code Playgroud)

无论是否必须指定类型参数都可以调用TUser...

var user = new FirstTimeUser();

DoThingsWithUser<FirstTimeUser>(user);
DoThingsWithUser(user); // also valid, and less typing required!
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.

但是,如果你再增加一些(再次,人为的)课程......

public abstract class UserDisplayBase<T> where T : UserBase
{
    public T User { get; protected set; }
}

public class FirstTimeUserDisplay : UserDisplayBase<FirstTimeUser>
{
    public FirstTimeUserDisplay()
    {
        User = new FirstTimeUser();
    }
}
Run Code Online (Sandbox Code Playgroud)

一种方法......

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

调用此方法时,必须包含类型参数...

var userDisplay = new FirstTimeUserDisplay();
DoThingsWithUserDisplay<FirstTimeUserDisplay, FirstTimeUser>(userDisplay); // Type arguments required!
Run Code Online (Sandbox Code Playgroud)

如果未指定类型参数,则会出现编译器错误

无法从用法推断出方法'DoThingsWithUserDisplay(TDisplay)'的类型参数.尝试显式指定类型参数.

认为编译器应该/可能足够智能来解决这个问题......或者有一个微妙的原因,为什么不呢?

Gra*_*x32 2

当编译器正在分析要推断的类型时,它在查看时遇到了死胡同DoThingsWithUserDisplay(userDisplay);

它发现TDisplay必须是 类型FirstTimeUserDisplay,这很好。然后约束表示TDisplay必须继承自UserDisplayBase<TUser>

由于它不知道类型,因此TUser无法确定是否FirstTimeUserDisplay继承UserDisplayBase<TUser>,也无法推断参数TUser应该是 generic 中的任何类型UserDisplayBase<TUser>

编辑:顺便说一句,您可以使用带有变体的接口来获得您寻求的类型推断。在这种情况下,接口定义提供了足够的继承信息,以便满足约束。

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();
}
Run Code Online (Sandbox Code Playgroud)

可以调用

var userDisplay = new FirstTimeUserDisplay();
DoThingsWithUserDisplay(userDisplay);
Run Code Online (Sandbox Code Playgroud)