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