为什么不能从通用约束中隐含地推断出“this”的类型转换?

时间:2017-10-19 16:45:33

标签: c# generics types constraints implicit

我有以下课程:

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

此处TItem item = this;生成编译器错误“无法将Item<TItem>隐式转换为TItem”。

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

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

3 个答案:

答案 0 :(得分:6)

因为它不安全。考虑:

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>

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

答案 1 :(得分:3)

那是因为您的班级类型为var result = (from a in db.Persons join b in db.Person_IDs on a.PersonId equals b.PersonId where b.FaceId == faceId select a).FirstOrDefault(); 而不是Item<TItem>。你可以;

TItem

样本有点令人费解。将此问题置于上下文中Item<TItem> item = this; 解决了尝试

的问题
TItem item = this;

答案 2 :(得分:2)

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

TItem可能比Item<TItem>更有派生,因此thisTItem可能是Apple,而this可能是橙子。因此编译器会阻止分配。

目前在c#中没有办法声明类型参数必须与继承类本身的类型匹配。

有两种常见的解决方法。首先使用显式强制转换

TItem GetReference() => (TItem) this;

确保继承类使用正确的类型参数是你的工作,否则你可能会尝试使用这种方法获得运行时异常。

第二种方法是使用类本身的返回类型。这是安全的(没有运行时异常),但没有派生类的任何合同。即你应该确保为每个派生类编写这个方法。

Item<TItem> GetReference() => this;

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

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

请注意,此功能已在GitHub c#repo,https://github.com/dotnet/csharplang/issues/252

中请求

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