我有以下课程:
public class Item<TItem>
where TItem : Item<TItem>
{
void GetReference()
{
TItem item = this;
}
}
此处TItem item = this;
生成编译器错误“无法将Item<TItem>
隐式转换为TItem
”。
但为什么我们需要转换呢?我们已经定义了约束where TItem : Item<TItem>
,因此可以认为根本不需要转换,因为这两种类型是相同的,不是吗?
顺便说一下,显式转换是可用的。这也在编译器错误中说明。
答案 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>
更有派生,因此this
,TItem
可能是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#团队添加此功能:)