C# casting required to invoke Contains()
Posted: Sat Sep 22, 2012 7:42 pm
At viewtopic.php?f=4&t=987&start=10#p5105 it came up that a cast was needed in the Cobra code:
Without it, the error is:
BiDictionary.cobra(131): error: Type "System.Collections.Generic.Dictionary<TKey,TValue>" does not contain a definition for "Contains" and no extension method "Contains" of type "System.Collections.Generic.Dictionary<TKey,TValue>" could be found (are you missing a using directive or an assembly reference?) (C#)
This is actually a C# compilation error which I have reduced down to this:
Both Mono C# and Microsoft C# complain about this line:
Stating that there is no .Contains method. Typecasting to IDictionary<of TKey, TValue> resolves the problem (whether in C# or in the original Cobra code), though this seems silly since Dictionary<of TKey, TValue> is known at compile time to implement that interface.
@jaegs, how did you even guess to provide the type cast to IDictionary to resolve this?
When I googled for:
C# dictionary "does not contain a definition for" contains
I actually got a Cobra hit:
http://cobra-language.com/trac/cobra/changeset/1405
So apparently I have run into this before although for somewhat different classes (the nested KeyCollection and ValueCollection classes of Dictionary<of TKey, TValue>). But still don't know why.
Getting back to the situation at hand, I checked at run-time with reflection if the method Contains exists on the dictionary:
It does not as these are the only hits for methods with "Contains" in them:
Both Mono 2.10 and Linux and .NET 4 on Windows are in agreement on this.
But I am expecting Contains to be there because the docs say that Dictionary<> implements ICollection<>:
http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
And ICollection has a Contains method including .NET 2 and up:
http://msdn.microsoft.com/en-us/library ... 6(v=vs.100).aspx
I then added NonPublic to the binding flags and suddenly it appears:
I now recall that in C# that if you explicitly implement an interface member then putting "public" on it will yield an error. This following C# docs cover explicit implementation:
http://msdn.microsoft.com/en-us/library/ms173157.aspx
Note the part where they say "Both method implementations are separate, and neither is available directly on the class." (emphasis mine)
I guess I find it confusing that a .NET class might be declared as "Foo implements IBar" but any methods of IBar that were explicitly implemented will not be available through "foo.someMethod" and will require a cast. But maybe that's a requirement for the case they bring up.
But it's even more odd that the implementation of IDictionary<> does this for Contains. Based on what I see from reflection, and keeping in mind method overloading, there was no reason for the explicit implementation of Contains.
But there it is. So in the future, if a method cannot be found at compile-time, try typecasting to one of the interfaces it implements. @jaegs already knew this.
I'm not sure what, if anything, Cobra should do to help in this case:
One idea is that if the method can be bound unambiguously, as in this case, Cobra could insert the cast for you. Though I don't know if this situation comes up often enough to justify the work involved.
Another idea is that Cobra should detect that the method won't be accessible as is (currently it thinks it's fine and passes the code through to C#) and give a more helpful error message. Neither C# compiler I tested gave any hint Contains was reachable by casting to one of the known interfaces of the object.
Whew. Was this post long enough for you?
# .
def contains(kvp as KeyValuePair<of TKey, TValue>) as bool
return (_forward to IDictionary<of TKey, TValue>).contains(kvp)
Without it, the error is:
BiDictionary.cobra(131): error: Type "System.Collections.Generic.Dictionary<TKey,TValue>" does not contain a definition for "Contains" and no extension method "Contains" of type "System.Collections.Generic.Dictionary<TKey,TValue>" could be found (are you missing a using directive or an assembly reference?) (C#)
This is actually a C# compilation error which I have reduced down to this:
- Code: Select all
using System;
using System.Collections.Generic;
public class BiDictionary<TKey, TValue> : Object {
protected Dictionary<TKey, TValue> _forward = null;
public BiDictionary() {
_forward = new Dictionary<TKey, TValue>();
}
public bool Contains(KeyValuePair<TKey, TValue> kvp) {
// return ((IDictionary<TKey, TValue>)_forward).Contains(kvp); // works
// return ((Dictionary<TKey, TValue>)_forward).Contains(kvp); // does not work
return _forward.Contains(kvp); // does not work -- why?
}
public bool ContainsKey(TKey key) {
return _forward.ContainsKey(key); // works
}
}
public class Program : System.Object {
public static void Main() {
Console.WriteLine("done.");
}
}
Both Mono C# and Microsoft C# complain about this line:
return _forward.Contains(kvp); // does not work -- why?
Stating that there is no .Contains method. Typecasting to IDictionary<of TKey, TValue> resolves the problem (whether in C# or in the original Cobra code), though this seems silly since Dictionary<of TKey, TValue> is known at compile time to implement that interface.
@jaegs, how did you even guess to provide the type cast to IDictionary to resolve this?
When I googled for:
C# dictionary "does not contain a definition for" contains
I actually got a Cobra hit:
http://cobra-language.com/trac/cobra/changeset/1405
So apparently I have run into this before although for somewhat different classes (the nested KeyCollection and ValueCollection classes of Dictionary<of TKey, TValue>). But still don't know why.
Getting back to the situation at hand, I checked at run-time with reflection if the method Contains exists on the dictionary:
use System.Reflection
class P
def main
t = Dictionary<of String, int>
flags = BindingFlags(Public, FlattenHierarchy, Instance)
for i, methodInfo in t.getMethods(flags).toList.numbered
if 'Contains' in methodInfo.name
trace i, methodInfo
It does not as these are the only hits for methods with "Contains" in them:
- Code: Select all
trace : i=6
- methodInfo=Boolean ContainsKey(System.String) (MonoGenericMethod)
- at contains-reflection.cobra:11
- in P.main
trace : i=7
- methodInfo=Boolean ContainsValue(Int32) (MonoGenericMethod)
- at contains-reflection.cobra:11
- in P.main
Both Mono 2.10 and Linux and .NET 4 on Windows are in agreement on this.
But I am expecting Contains to be there because the docs say that Dictionary<> implements ICollection<>:
http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
And ICollection has a Contains method including .NET 2 and up:
http://msdn.microsoft.com/en-us/library ... 6(v=vs.100).aspx
I then added NonPublic to the binding flags and suddenly it appears:
- Code: Select all
trace : i=10
- methodInfo=Boolean System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValu
e>>.Contains(System.Collections.Generic.KeyValuePair`2[System.String,System.Int32]) (RuntimeMethodInfo)
- at contains-reflection.cobra:22
- in P.main
trace : i=13
- methodInfo=Boolean ContainsKey(System.String) (RuntimeMethodInfo)
- at contains-reflection.cobra:22
- in P.main
trace : i=14
- methodInfo=Boolean ContainsValue(Int32) (RuntimeMethodInfo)
- at contains-reflection.cobra:22
- in P.main
trace : i=39
- methodInfo=Boolean System.Collections.IDictionary.Contains(System.Object) (RuntimeMethodInfo)
- at contains-reflection.cobra:22
- in P.main
I now recall that in C# that if you explicitly implement an interface member then putting "public" on it will yield an error. This following C# docs cover explicit implementation:
http://msdn.microsoft.com/en-us/library/ms173157.aspx
Note the part where they say "Both method implementations are separate, and neither is available directly on the class." (emphasis mine)
I guess I find it confusing that a .NET class might be declared as "Foo implements IBar" but any methods of IBar that were explicitly implemented will not be available through "foo.someMethod" and will require a cast. But maybe that's a requirement for the case they bring up.
But it's even more odd that the implementation of IDictionary<> does this for Contains. Based on what I see from reflection, and keeping in mind method overloading, there was no reason for the explicit implementation of Contains.
But there it is. So in the future, if a method cannot be found at compile-time, try typecasting to one of the interfaces it implements. @jaegs already knew this.
I'm not sure what, if anything, Cobra should do to help in this case:
One idea is that if the method can be bound unambiguously, as in this case, Cobra could insert the cast for you. Though I don't know if this situation comes up often enough to justify the work involved.
Another idea is that Cobra should detect that the method won't be accessible as is (currently it thinks it's fine and passes the code through to C#) and give a more helpful error message. Neither C# compiler I tested gave any hint Contains was reachable by casting to one of the known interfaces of the object.
Whew. Was this post long enough for you?