tags:

views:

59

answers:

3

I'm a bit boggled by something, I hope the CLR gearheads can help. Apparently my gears aren't big enough.

I have a reflector utility that generates assembly stubs for Cola for .NET, and I find classes have methods that only differ by a modifier, such as virtual. Example below, from Oracle.DataAccess.dll, method GetType():

class OracleTypeException : System.SystemException {
    virtual  string ToString ();
    virtual  System.Exception GetBaseException ();
    virtual  void set_Source (string value);
    virtual  void GetObjectData (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context);
    virtual  System.Type GetType (); // DeclaringType Exception
    virtual  bool Equals (object obj);
    virtual  int32 GetHashCode ();
    System.Type GetType (); // DeclaringType Object
}

What is this?

I have not been able to reproduce this with C# and it causes trouble for Cola as it thinks GetType() is a redefinition, since the signature is identical.

My method reflector starts like this:

static void DisplayMethod(MethodInfo m)
{
    if (
       // Filter out things Cola cannot yet import, like generics, pointers, etc.
       m.IsGenericMethodDefinition || m.ContainsGenericParameters || m.ReturnType.IsGenericType
       || !m.ReturnType.IsPublic
       || m.ReturnType.IsPointer || m.ReturnType.IsByRef
       || m.ReturnType.IsMarshalByRef
       || m.ReturnType.IsImport
       )
   return;

   // generate stub signature
   // [snipped]
}

SOLVED: non-virtual GetType() comes from System.Object. The deriving class shadowed System.Object.GetType() with a virtual method.

+3  A: 

I would check:

  • is it static?
  • is it an implicit interface implementation?

Note that you can also re-declare methods, which could be a factor; but that would be crazy; however, this shows this:

class Bar {
    new Type GetType() { return null; }
}
static class Program {
    static void Main() {
        var methods = typeof(Bar).GetMethods(
              BindingFlags.Instance | BindingFlags.Static
            | BindingFlags.Public | BindingFlags.NonPublic);
        foreach (var method in methods) {
            Console.WriteLine(method.Name);
        }
    }
}
Marc Gravell
It isn't static, I'm handling that modifier. I'll check the interface suggestion.
mrjoltcola
@mrjoltcola - added another, with example that shows it. I didn't bother writing out all the args etc, but this *is* another `GetType()` method with the same signature and different accessor. It could actually even have the *same* accessor if we felt especially evil.
Marc Gravell
Yes, I'm feeling the evil, because I have to decide how to import these. This is essentially overloading based on "DeclaringType", which complicates things.
mrjoltcola
+1 to both of you, I accepted Darin's due to his initial code sample, but I appreciate that you added one too.
mrjoltcola
+3  A: 

This is possible. Here's a code snippet that compiles just fine:

public class OracleTypeException : SystemException, _Exception
{
    public virtual Type GetType()
    {
        throw new NotImplementedException();
    }

    Type _Exception.GetType()
    {
        throw new NotImplementedException();
    }
}

It relies on the fact that SystemException implements _Exception which itself defines the GetType method. So here we have:

  1. an explicit implementation of this interface
  2. a GetType method that hides the one coming from System.Object.

By the way the compiler would generate a warning about it.

Darin Dimitrov
Ok, that explains it. Now to figure out how to handle this case. For the purpose of importing the assembly into Cola at compile time, this is an ambiguity. Any suggestions?
mrjoltcola
To disambiguate you have two cases: a variable defined of type `SystemException` would invoke the first method, while a variable of type `_Exception` would invoke the second method.
Darin Dimitrov
So a variable of type "OracleTypeException" would invoke which? Is there a reference on MSDN regarding the rule for this case?
mrjoltcola
A variable of type `OracleTypeException` would invoke the first. Only a variable of type `_Exception` would invoke the second. I guess that's why it's called explicit interface implementation. As for the rule, look at the link I provided in my answer (http://msdn.microsoft.com/en-us/library/aa288461(VS.71).aspx).
Darin Dimitrov
mrjoltcola
From a quick test, the interface is unrelated; the v4 compiler names this method "`System.Runtime.InteropServices._Exception.GetType`". The real problem is re-declaring `GetType()`.
Marc Gravell
@mrjoltcola, `Exception` implements the `_Exception` interface.
Darin Dimitrov
MSDN confused me. In one place GetType() is listed as inherited from System.Object, but it seems this is only for the XNA platform, in another, it says implemented from _Exception.
mrjoltcola
Object does implement GetType() and that's generally the implementation you want. The _Exception implementation is specifically for interoperability, for usage by unmanaged clients that know about the _Exception interface, but don't know about System.Object.
Dan Bryant
+1  A: 

This class should exhibit the same behavior:

  class Something {
    public virtual Type GetType() {
      throw new NotImplementedException();
    }
  }

Since every class inherits from System.Object, it also inherits the non-virtual Object.GetType() method. Redeclaring the method as virtual (note that not even the "new" keyword is needed) hides the original inherited GetType method.

Not sure what this tool requires, but I suppose you'll need to rename the hidden inherited method with something like "Object_GetType".

Hans Passant
@Hans: You are exactly right, I tested your simple case above. Thanks for shedding more light on this. I've not determined how I will handle it yet. Until today I had always assumed that when a method was shadowed, it was not part of the signature of the class that shadowed it, so I have more research to do. Thanks! +1
mrjoltcola