Turtles are Reptiles, right?

While writing an article about co- and contravariance in .NET I stumbled over some funny C# behaviors.

Given a simple domain model of animals (Reptile is base of Snake and Turtle), will this actually work?

And if not, what will happen? Will it even compile?

Reptile[] reptiles = new[]
  {
    new Snake("Leo"), 
    new Snake("Lucy")
  };

reptiles[0] = new Turtle("Platschi");

Quite obvious, still odd. 🙂

GREAT! Thanks for the answers!

I think @thinkbeforecoding had the best answer. Justin Chase, Björn Rochel and Stefan Lieser also got it right.

Here is what happens:

  1. It compiles!
  2. The type inference will infer a Snake[] from new[] because all elements are Snake.
  3. The Snake array will then be implicitly casted to the Reptile array reptiles because C# supports covariance for Array element types.
  4. Line 7 also compiles fine. But the covariance support is unsafe, hence read-only. The Snake-Array behind the scenes can’t handle Turtles.
  5. A ArrayTypeMismatchException is thrown at runtime.

In C# 4.0 there will not be safe variance for Arrays either. There can never be safe co- and contra-variance for read-write-interfaces or types, because you need co-variance for reading (out) and contra-variance for writing (in).

11 thoughts on “Turtles are Reptiles, right?

  1. Duh, I’m such a biology sucker. Did I mention that I ditched that on 11th grade 😉

    So, probably the correct answer is a) it compiles and b) it gets an exception at runtime. That happens because the array of reptiles is still an array of snakes behind the scenes . . .

  2. Maybe Platschi is not a turtle… and C#’s Tierleben prohibits naming turtles Platschi causing the constructor new Turtle(“Platschi”) to fail.

  3. I didn’t run it, but I would expect 3.5 compiler to say that an array of Reptiles can’t be assigned a value of an array of Snakes (which is what the new[] will create). 4.0 was about to introduce co-variance, so you sample should work there.

  4. The new[] {} will instanciate a Snake[] array because all elements are Snakes.
    The odd array variance will accept to cast it as Reptile[] even if it causes problems at runtime with array mutability.

    The indexer setter will throw an exception when it will check the type of argument : Turtule cannot be cast as Snake.

    To avoid problems, user new Reptile[] { } or new [] { (Reptile)new Snake(..) }.

    It’s a bit scarry anyway..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s