Optional argument corner cases, part one

In C# 4.0 we added “optional arguments”; that is, you can state in the declaration of a method’s parameter that if certain arguments are omitted, then constants can be substituted for them:

void M(int x = 123, int y = 456) { }

can be called as M(), M(0) and M(0, 1). The first two cases are treated as though you’d said M(123, 456) and M(0, 456) respectively.

This was a controversial feature for the design team, which had resisted adding this feature for almost ten years despite numerous requests for it and the example of similar features in languages like C++ and Visual Basic. Though obviously convenient, the convenience comes at a pretty high price of bizarre corner cases that the compiler team definitely needs to deal with, and customers occasionally run into by accident. I thought I might talk a bit about a few of those bizarre corner cases.

Continue reading

Closing over the loop variable considered harmful, part two

(This is part two of a two-part series on the loop-variable-closure problem. Part one is here.)

UPDATE: We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The for loop will not be changed. We return you now to our original article.

Thanks to everyone who left thoughtful and insightful comments on last week’s post.

More countries really ought to implement Instant-Runoff Voting; it would certainly appeal to the geek crowd. Many people left complex opinions of the form “I’d prefer to make the change, but if you can’t do that then make it a warning”. Or “don’t make the change, do make it a warning”, and so on. But what I can deduce from reading the comments is that there is a general lack of consensus on what the right thing to do here is. In fact, I just did a quick tally:

  • Commenters who expressed support for a warning: 26
  • Commenters who expressed the sentiment “it’s better to not make the change”: 24
  • Commenters who expressed the sentiment “it’s better to make the change”: 25

Wow. I guess we’ll flip a coin. :-)[1. Mr. Smiley Face indicates that Eric is indulging in humourous japery.]

Continue reading

Locks and exceptions do not mix

A couple years ago I wrote a bit about how our codegen for the lock statement could sometimes lead to situations in which an unoptimized build had different potential deadlocks than an optimized build of the same source code. This is unfortunate, so we’ve fixed that for C# 4.0. However, all is still not rainbows, unicorns and Obama, as we’ll see.
Continue reading

Covariance and contravariance in C#, part 1

I have been wanting for a long time to do a series of articles about covariance and contravariance (which I will shorten to “variance” for the rest of this series.)

I’ll start by defining some terms, then describe what variance features C# 2.0 and 3.0 already support today, and then discuss some ideas we are thinking about for hypothetical nonexistent future versions of C#.

As always, keep in mind that we have not even shipped C# 3.0 yet. Any of my musings on possible future additions to the language should be treated as playful hypotheses, rather than announcements of a commitment to ship any product with any feature whatsoever.

Today: what do we mean by “covariance” and “contravariance”?

The first thing to understand is that for any two types T and U, exactly one of the following statements is true:

  • T is bigger than U.
  • T is smaller than U.
  • T is equal to U.
  • T is not related to U.

For example, consider a type hierarchy consisting of Animal, Mammal, Reptile, Giraffe, Tiger, Snake and Turtle, with the obvious relationships. (Mammal is a subclass of Animal, and so on.)

Mammal is a bigger type than Giraffe and smaller than Animal, and obviously equal to Mammal. But Mammal is neither bigger than, smaller than, nor equal to Reptile, it’s just different.

Why is this relevant? Suppose you have a variable, that is, a storage location. Storage locations in C# all have a type associated with them. At runtime you can store an object which is an instance of an equal or smaller type in that storage location. That is, a variable of type Mammal can have a reference to an instance of Giraffe stored in it, but not a Turtle.

This idea of storing an object in a typed location is a specific example of a more general principle called the “substitution principle”. That is, in many contexts you can often substitute an instance of a “smaller” type for a “larger” type.

Now we can talk about variance. Consider an “operation” which manipulates types. If the results of the operation applied to any T and U always results in two types T’ and U’ with the same relationship as T and U, then the operation is said to be “covariant”. If the operation reverses bigness and smallness on its results but keeps equality and unrelatedness the same then the operation is said to be “contravariant”.

That’s totally highfalutin and probably not very clear. Next time we’ll look at how C# 3 implements variance at present.

Commentary from 2020

At the time I wrote this series we were done with C# 3.0 but it had not shipped yet, and we were therefore well underway with the design and implementation of C# 4.0. The purpose of this series was to introduce the concept of generic variance, which we were planning to add in C# 4.0, and get early feedback on whether it would be useful and whether it would be understood.

Because we were gathering information to make a decision about how to design the feature and whether it was worth the effort at all, I was very concerned that my words not be construed as a promise to deliver any particular feature on any particular schedule. However, one of the nice things about my early days of blogging when I was a Microsoft employee was that the company pretty much gave us the guidance of “err on the side of whatever helps users most” and let the lawyers worry about the legal stuff.

There were a lot of confusing comments left on the original article. I realized later that I was insufficiently clear, even though I had boldfaced the key line to call attention to it. Covariance is about preserving a relationship across a mapping, and the thing I wanted to be very clear was: the relationship that is preserved is “a value of this type is assignable to a variable of that type“. This relationship is called assignment compatibility. In retrospect I also should have been more clear that I was specifically talking about assignment compatibility of reference types, and that the conversion from one type to another had to be a value-preserving reference conversion.

The most common confusion I’ve seen in the decade which followed, and I still see a lot, is that covariance is not “you can use a giraffe where a mammal is required“. That is assignment compatibility. Covariance is “because you can use a giraffe where a mammal is required, you are also allowed to use a sequence of giraffes where a sequence of mammals is required“. It is the preservation of assignment compatibility when the types are transformed from “giraffe” and “animal” to “sequence of giraffes” and “sequence of animals” that makes the transformation covariant.

As was true of most of this series, there were comments asking for return type covariance. It looks like that may finally be added to C# 9.