views:

266

answers:

2

I'm interesting in some design choices of C# language. There is a rule in C# spec that allows to use method groups as the expressions of is operator:

class Foo {
  static void Main() { if (Main is Foo) Main(); }
}

Condition above is always false, as the specification says:

7.10.10 The is operator

If E is a method group or the null literal, of if the type of E is a reference type or a nullable type and the value of E is null, the result is false.

My questions: what is the purpose/point/reason of allowing to use the C# language element with no runtime representation in CLR like method groups inside the such "runtime" operator like is?

+3  A: 

First of all a method is not a type, msdn clearly states the following:

The is operator is used to check whether the run-time type of an object is compatible with a given type

Example

public static void Test (object o) 
{
   Class1 a;

   if (o is Class1) {}
}

From MSDN:

An is expression evaluates to true if both of the following conditions are met:

  • expression is not null.
  • expression can be cast to type. That is, a cast expression of the form (type)(expression) will complete without throwing an exception. For more information, see 7.6.6 Cast expressions.

So the reason for your example being false lands on the second point, it has to be castable to a specific type.

I hope I did not missunderstand the question.

Filip Ekberg
The question is, why isn't `Main is Class1` a compiler error?
SLaks
@SLacks: Maybe we should use Reflector to further investigate this.
Andreas
@SLaks, using ReShaper I get a warning with this message: "The given expression is never provided a type". Checking with Reflector now..
Filip Ekberg
@Filip: Agree. I get the warning at command line, cf:http://msdn.microsoft.com/en-us/library/230kb9yt%28VS.90%29.aspx
Andreas
@Andreas, right. It takes a "reference type", a method can be a delegate, so therefore this doesn't give you a compile time error. That's my guess anyways.
Filip Ekberg
@Filip: Wrong. Method groups are untyped.
SLaks
Right. Does this answer add any value to the Question?
Filip Ekberg
@SLaks, Yeah thats because methods are not types. :)
Filip Ekberg
@Filip: Wrong. Method groups, anonymous delegates, and lambda expressions are untyped expressions, meaning that they represent a value that has no intrinsic type. (Unless you use it in delegate context, in which case it becomes the type of the delegate) This is why `var a = 1` is valid, whereas `var a = Console.WriteLine` is invalid.
SLaks
@SLaks, that's what I just said, Methods are **not** types.
Filip Ekberg
@Filip: Normal expressions are also not types, but `"abc"` is a typed expression, of type `String`.
SLaks
@SLaks, I don't understand where you are getting at?
Filip Ekberg
A method is not a type, but any other expression is also not a type. I think you meant to say, "a method _doesn't have_ a type".
SLaks
@SLaks, I might be lost in translation here. :)
Filip Ekberg
+13  A: 

what is the purpose/point/reason of allowing to use the C# language element with no runtime representation in CLR like method groups inside the such "runtime" operator like is?

The language design notes archive make no mention of why this decision was made, so any guess at an answer will be a conjecture. They do mention that if the result of the "is" can be statically determined to always be true or false, that it be so determined and produce a warning. It seems plausible that this could simply be an error.

The most common reason for turning what could rightly be an error into a warning (or simply allowing it) is because that lessens the burden upon producers of programs that automatically generate code. However, I don't see a really compelling scenario here.

UPDATE:

I just checked the C# 1.0 specification. It does not have this language in it. It does not say anything about nulls or method group arguments. And of course it says nothing about method group conversions because in C# 1.0 there were no implicit method group conversions; you had to explicitly call "new D(M)" if you wanted to convert method M to delegate type D.

This latter point is justification for "M is D" returning false rather than true; You couldn't legally say "D d = M;" so why should "M is D" be true?

Of course in C# 2.0 this makes less sense, since you can say "D d = M;" in C# 2.0.

I also just asked one of the people present when the "is" operator was designed and he had no memory of ever deciding this question one way or the other. His suspicion was that the original design of the "is" operator was to not give any errors, only warnings, and that all the text in the spec about what to do with method groups and nulls and whatnot was added post-hoc, for the C# 2.0 version of the spec, and based on what the compiler actually did.

In short, it looks like this was a design hole in C# 1.0 that was papered over when the spec was updated for C# 2.0. It does not look like this specific behaviour was desired and deliberately implemented.

This theory is reinforced by the fact that anonymous methods do produce an error when used as an argument to "is" in C# 2.0. It would not be a breaking change to do so, but it would be a breaking change to make "M is D" suddenly start returning true or being an error.

FURTHER UPDATE:

While investigating this I learned something interesting. (To me.) When the feature was originally designed, the design was to allow either the name of a type, or a Type object as the right-hand argument to "is". That idea was abandoned well before C# 1.0 shipped though.

Eric Lippert
Thanks, Eric! Very glad to see you answering my questions, waiting for old-timers story :)
ControlFlow