Reputation: 8541
I have the following class:
public class Item<TItem>
where TItem : Item<TItem>
{
void GetReference()
{
TItem item = this;
}
}
Here TItem item = this;
generates a compiler error "can't convert Item<TItem>
implicitly to TItem
".
But why do we need a conversion here? We have defined the constraint where TItem : Item<TItem>
, so one can think that no conversion is needed at all since the two types are the same, aren't they?
Btw an explicit conversion is available. This also is stated in the compiler error.
Upvotes: 3
Views: 214
Reputation: 19149
because every TItem
is Item<TItem>
(as declared by where constraint), but not vice versa.
TItem
could be more derived than Item<TItem>
, so do this
, TItem
could be Apple and this
could be oranges. so assignment is prevented by compiler.
currently in c# there is no way to declare that type parameter must match type of inheriting class itself.
there are two common ways to fix this. first use explicit cast
TItem GetReference() => (TItem) this;
Its your job to make sure inheriting class is using right type parameter, other wise you may get runtime exception if you ever try to use this method.
second way is to use return type of class itself. which is safe (no runtime exception) but does not have any sort of contract for derived classes. i.e you should make sure you write this method for every derived class.
Item<TItem> GetReference() => this;
now you can hide this method in derived classes.
new Derived GetReference() => this; // public class Derived : Item<Derived>
Note that this feature is already requested in GitHub c# repo, https://github.com/dotnet/csharplang/issues/252
you just have to wait for c# team to add this feature :)
Upvotes: 2
Reputation: 4971
That's because your class is type Item<TItem>
not TItem
. You could have;
Item<TItem> item = this;
The sample is a bit of a mind bender. To put this into context TItem item = this;
resolves to trying to do
Item<TItem> item = new Item<Item<TItem>>();
Upvotes: 3
Reputation: 1500385
Because it wouldn't be safe. Consider:
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
satisfies the constraint for TItem
with no problems - GoodItem
is indeed derived from Item<GoodItem>
.
There's no way of expressing a relationship between the class being declared and a type parameter, which is what you really want.
Upvotes: 5