General covariance with interfaces - a strange behavioral contradiction between the "is" and "=" operators

I had a full wtf moment when working 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 not inherited from FruitBasket .
  • IBasket covariant .

Later 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 expected, the conclusion:

true
true

HOWEVER , consider the following code:

AppleBasket appleBasket = new AppleBasket();

Log(appleBasket is IBasket<Fruit>);    

You expect him to output true, right? Well, you're wrong - at least with my compiler:

false    

It's weird, but maybe it does an implicit conversion , similar to converting intto long. intis not a view long, but an int can be assigned to a long one implicitly.

HOWEVER, :

IBasket<Fruit> basket = new AppleBasket(); // implicit conversion?

Log(basket is IBasket<Fruit>);

- - , AppleBasket IBasket<Fruit>. , .

basket - IBasket<Fruit> - IBasket<Fruit>... , . ?

, is, ! :

false

... IBasket<Fruit> fruit IBasket<Fruit>... Huh?

... :

IBasket<Fruit> FruitBasket { get { ... } }

-, null, IBasket<Fruit>.


FURTHER, ReSharper , appleBasket is IBasket<Fruit> , AppleBasket appleBasket != null... ReSharper ?

, ? # (Unity 5.3.1p4 - It Unity Mono, .NET 2.0), ?

+4
2

, , ... ​​ # 4.

IS , , , , .NET 2.0

+1

:

IBasket<Fruit> basket = new AppleBasket();

, <T>.

interface IBasket<out T> { }

, . . :

interface IBasket<out T>
{ 
    void Add(T item);
}

, , , (out) . , :

IBasket<Fruit> basket = new AppleBasket()

Apple IBasket<Apple>.

0

All Articles