I had a complete wtf moment when dealing with covariant interfaces.
Consider the following:
class Fruit { }
class Apple : Fruit { }
interface IBasket<out T> { }
class FruitBasket : IBasket<Fruit> { }
class AppleBasket : IBasket<Apple> { }
Note:
AppleBasket
does not inherit from FruitBasket
.IBasket
is covariant.Later on in the script, you write:
FruitBasket fruitBasket = new FruitBasket();
AppleBasket appleBasket = new AppleBasket();
Log(fruitBasket is IBasket<Fruit>);
Log(appleBasket is IBasket<Apple>);
...and as you'd expect, the output is:
true
true
HOWEVER, consider the following code:
AppleBasket appleBasket = new AppleBasket();
Log(appleBasket is IBasket<Fruit>);
You'd expect it to output true
, right? Well, you are wrong -- at least with my compiler anyway:
false
That's strange, but perhaps it's performing an implicit conversion, similar to converting an int
to a long
. An int
is not a kind of long
, but a long can be assigned the value of an int implicitly.
HOWEVER, consider this code:
IBasket<Fruit> basket = new AppleBasket(); // implicit conversion?
Log(basket is IBasket<Fruit>);
This code runs just fine - no compiler errors or exceptions - even though we previously learned that an AppleBasket
is NOT a kind of IBasket<Fruit>
. Unless there is some third option, it must be doing an implicit conversion in the assignment.
Surely basket
- declared as an IBasket<Fruit>
- MUST be an instance of IBasket<Fruit>
... I mean, that's what it was declared as. Right?
But no, according to the is
operator, you're wrong again! It outputs:
false
...Meaning IBasket<Fruit> fruit
is NOT an instance of IBasket<Fruit>
... Huh?
...Meaning the following property:
IBasket<Fruit> FruitBasket { get { ... } }
Can sometimes return something that both isn't null, and isn't an instance of IBasket<Fruit>
.
FURTHER, ReSharper tells me that appleBasket is IBasket<Fruit>
is redundant in that appleBasket
is always of the provided type, and can be safely replaced with appleBasket != null
... ReSharper got it wrong too?
So, what's going on here? Is this just my version of C# (Unity 5.3.1p4 - It's Unity's own branch of Mono, based off of .NET 2.0) being a nut?
Based on your comments, it is no small surprise that covariance isn't properly being supported...it was added in C#4.
What IS incredibly surprising is that it is even compiling at all if it is targeting a port based off of .NET 2.0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With