为什么不能从泛型约束中隐式推断出 this 的类型转换?

Why can't the type conversion of `this` be implicitly inferred from the generic contraint?

我有以下 class:

public class Item<TItem>
    where TItem : Item<TItem>
{
    void GetReference()
    {
        TItem item = this;
    }
}

此处 TItem item = this; 生成编译器错误 "can't convert Item<TItem> implicitly to TItem"。

但是为什么我们需要在这里进行转换呢?我们已经定义了约束where TItem : Item<TItem>,所以可以认为根本不需要转换,因为这两种类型是相同的,不是吗?

顺便说一句,显式转换可用。这也在编译器错误中说明。

那是因为你的 class 是类型 Item<TItem> 而不是 TItem。你可以;

Item<TItem> item = this;

示例有点令人费解。将其放入上下文 TItem item = this; 解决尝试做

Item<TItem> item = new Item<Item<TItem>>();

因为这样不安全。考虑:

public class GoodItem : Item<GoodItem>
{
    // No problem
}

public class EvilItem : Item<GoodItem>
{
    // GetReference body would be equivalent to 
    // GoodItem item = this;
    // ... but this *isn't* a GoodItem, it's an EvilItem!
}

EvilItem 毫无问题地满足 TItem 的约束 - GoodItem 确实源自 Item<GoodItem>.

无法表达声明的 class 和类型参数之间的关系,而这正是您真正想要的。

因为每个 TItem 都是 Item<TItem>(由 where 约束声明),但反之则不然。

TItem 可能比 Item<TItem> 更衍生,this 也是如此,TItem 可能是苹果,this 可能是橙子。所以编译器阻止了赋值。

目前在 c# 中无法声明类型参数必须匹配继承 class 本身的类型。

有两种常见的方法可以解决这个问题。首先使用显式转换

TItem GetReference() => (TItem) this;

你的工作是确保继承 class 使用正确的类型参数,否则如果你尝试使用此方法,你可能会遇到运行时异常。

第二种方法是使用 class 本身的 return 类型。这是安全的(没有运行时异常)但没有任何类型的派生 classes 合同。也就是说,您应该确保为每个派生的 class.

编写此方法
Item<TItem> GetReference() => this;

现在您可以在派生的 classes 中隐藏此方法。

new Derived GetReference() => this; // public class Derived : Item<Derived>

请注意,此功能已在 GitHub c# 存储库中请求,https://github.com/dotnet/csharplang/issues/252

你只需要等待 c# 团队添加此功能:)