Why calling ISet<dynamic>.Contains() compiles, but throws an exception at runtime?
The answers you have received so far do not explain the behaviour you are seeing. The DLR should find the method ICollection<object>.Contains(object)
and call it with the boxed integer as a parameter, even if the static type of the variable is ISet<dynamic>
instead of ICollection<dynamic>
(because the former derives from the latter).
Therefore, I believe this is a bug and I have reported it to Microsoft Connect. If it turns out that the behaviour is somehow desirable, they will post a comment to that effect there.
Why it compiles: the entire expression is evaluated as dynamic (hover your mouse over it inside your IDE to confirm), which means that it is a runtime check.
Why it bombs: My (completely wrong, see below) guess is that it is because you cannot implement a dynamic interface in such a manner. For example, the compiler does not allow you to create a class that implements ISet<dynamic>
, IEnumerable<dynamic>
, IList<dynamic>
, etc. You get a compile-time error stating "cannot implement a dynamic interface". See Chris Burrows' blog post on this subject.
http://blogs.msdn.com/b/cburrows/archive/2009/02/04/c-dynamic-part-vii.aspx
However, since it's hitting the DLR anyway, you can make s
completely dynamic.
dynamic s = new HashSet<dynamic>;
s.Contains(d);
Compiles and runs.
Edit: the second part of this answer is completely wrong. Well, it is correct in that you can't implement such an interface as ISet<dynamic>
, but that's not why this blows up.
See Julian's answer below. You can get the following code to compile and run:
ICollection<dynamic> s = new HashSet<dynamic>();
s.Contains(d);
The Contains
method is defined on ICollection<T>
, not ISet<T>
. The CLR doesn't allow an interface base method to be called from a derived interface. You usually doesn't see this with static resolution because the C# compiler is smart enough to emit a call to ICollection<T>.Contains
, not the non-existing ISet<T>.Contains
.
Edit: The DLR mimics the CLR behavior, that's why you get the exception. Your dynamic call is done on an ISet<T>
, not an HashSet<T>
the DLR will mimics the CLR: for an interface, only interfaces methods are searched for, not base interfaces (contrary to classes where this behavior is present).
For an in-depth explanation, see a previous response of mine to a similar question:
Strange behaviour when using dynamic types as method parameters