A contravariance conundrum

Suppose we have my usual hierarchy of types, Animal, Giraffe, etc, with the obvious type relationships. An IEqualityComparer<T> is contravariant in its type parameter; if we have a device which can compare two Animals for equality then it can compare two Giraffes for equality as well. So why does this code fail to compile?

IEqualityComparer<Animal> animalComparer = whatever;
IEnumerable<Giraffe> giraffes = whatever;
IEnumerable<Giraffe> distinct = giraffes.Distinct(animalComparer);

This illustrates a subtle and slightly unfortunate design choice in the method type inference algorithm, which of course was designed long before covariance and contravariance were added to the language.

Continue reading

Advertisements

What is the type of the null literal?

The C# 2.0 specification says

The null literal evaluates to the null value, which is used to denote a reference not pointing at any object or array, or the absence of a value. The null type has a single value, which is the null value.

But every version of the specification since then does not contain this language. So what then is the type of the null literal expression?

Continue reading

Why does a foreach loop silently insert an “explicit” conversion?

The C# specification defines

foreach (V v in x) 
  embedded-statement

as having the same semantics as:

{
  E e = ((C)(x)).GetEnumerator();
  try 
  {
    V v;  // Inside the while in C# 5.
    while (e.MoveNext()) 
    {
      v = (V)e.Current;
      embedded-statement
    }
  }
  finally 
  {
    // necessary code to dispose e
  }
}

(Actually this is not exactly what the spec says; I’ve made one small edit because I don’t want to get into the difference between the element type and the loop variable type in this episode.)

There are a lot of subtleties here that we’ve discussed before; what I want to talk about today is the explicit conversion from e.Current to V. On the face of it this seems very problematic; that’s an explicit conversion. The collection could be a list of longs and V could be int; normally C# would not allow a conversion from long to int without a cast operator appearing in the source code. (Or the long being a constant that fits into an int.) What justifies this odd design choice?

Continue reading

Why not allow double/decimal implicit conversions?

I’ve talked a lot about floating point math over the years in this blog, but a quick refresher is in order for this episode.

A double represents a number of the form +/- (1 + F / 252 ) x 2E-1023, where F is a 52 bit unsigned integer and E is an 11 bit unsigned integer; that makes 63 bits and the remaining bit is the sign, zero for positive, one for negative. You’ll note that there is no way to represent zero in this format, so by convention if F and E are both zero, the value is zero. (And similarly there are other reserved bit patterns for infinities, NaN and denormalized floats which we will not get into today.)

A decimal represents a number in the form +/- V / 10X where V is a 96 bit unsigned integer and X is an integer between 0 and 28.

Both are of course “floating point” because the number of bits of precision in each case is fixed, but the position of the decimal point can effectively vary as the exponent changes.
Continue reading

Ask the Bug Guys

Today on the Coverity Development Testing blog we’ve announced a new recurring feature: Ask the Bug Guys. If you’ve got a question about a strange behavior that caused a bug in a C#, Java, C or C++ program, or an interesting story to share about finding and fixing bugs, consider sending it to TheBugGuys@coverity.com. I’ll be reviewing the C# bugs, and my colleagues Jon and Tim will be looking at the C, C++ and Java submissions.

We of course cannot promise to write up an analysis of all submissions, but we will choose a selection of the most interesting and informative bugs and periodically post an analysis of them on the blog. Including a small, complete example that clearly demonstrates the bug would be helpful.

Why are generic constraints not inherited?

Today’s episode is presented as a dialogue:

Why are generic constraints not inherited?

Members are inherited. Generic constraints are not members of a type.

But generic constraints are inherited on generic methods, right?

That’s true, though what is inherited is the method and the constraint comes along with it. What is a bit odd is: generic constraints on methods are invisibly inherited when overriding, which has always vexed me. My preferred design would have been to require that the constraint be re-stated. That is, when you say:

class B
{
  public virtual void M<T>() where T : struct { }
}
class D : B
{
  public override void M<U>() { }
}

U in D.M<U> is still constrained to struct, but it is actually illegal in C# to re-state that fact redundantly! This is a bit of a misfeature in my opinion; it works against code clarity for the reader. Particularly since the base class might not even be in source code; it might be in a different assembly entirely.

(Note that I’m emphasizing here that there is no requirement that the type parameters be named the same. Similarly there is no requirement that the formal parameters be named the same either, but it is a good idea to do so. We’ll come back to this point in a moment.)

Continue reading