views:

87

answers:

1

Have I stumbled upon implementation-defined behavior?

Here is the context:

public class GenericClass<T>
{
    public class NestedGenericClass<U>
    {
        public void GenericMethod<K>()
        {
        }
    }
}

Here's the behavior. This unit test passes as written. My actual questions are listed as the comment before the "wacky" (as it seems to me now) behavior.

[TestMethod]
public void TestNestedGenericMethod()
{
    Type openType = typeof(GenericClass<>.NestedGenericClass<>);
    Type closedType = typeof(GenericClass<bool>.NestedGenericClass<int>);
    /* Note there is absolutely no representation of the following as a [unique] type, via the
     * typeof operator or the Reflection API, even though the metadata TypeSpec signature
     * should in theory be able to reference it. This is the original reason I wrote these
     * tests.
     *   Type partiallyOpenType = typeof(GenericClass<bool>.NestedGenericClass<>);
     */

    MethodInfo openTypeOpenMethod = openType.GetMethod("GenericMethod");
    MethodInfo closedTypeOpenMethod = closedType.GetMethod("GenericMethod");
    MethodInfo closedTypeClosedMethod = closedTypeOpenMethod.MakeGenericMethod(typeof(long));

    Assert.IsNotNull(openTypeOpenMethod);
    Assert.IsNotNull(closedTypeOpenMethod);
    Assert.IsNotNull(closedTypeClosedMethod);

    Assert.AreNotSame(openTypeOpenMethod, closedTypeOpenMethod);
    Assert.AreNotSame(openTypeOpenMethod, closedTypeClosedMethod);
    Assert.AreNotSame(closedTypeOpenMethod, closedTypeClosedMethod);

    /* What on earth?!
     *  1. Is the following covered in the CLI spec and/or is it implementation-defined?
     *  2. Is there any potential use of this behavior (inside the runtime itself OR outside)?
     *  3. Will I ever hit a MethodDefSig (§23.2.1)/MethodRefSig (§23.2.2)/MethodSpecSig (§23.2.15) that resolves to this?
     */
    MethodInfo openTypeClosedMethod = openTypeOpenMethod.MakeGenericMethod(typeof(long));
    Assert.IsNotNull(openTypeClosedMethod);
    Assert.AreNotSame(openTypeClosedMethod, openTypeOpenMethod);
    Assert.AreNotSame(openTypeClosedMethod, closedTypeOpenMethod);
    Assert.AreNotSame(openTypeClosedMethod, closedTypeClosedMethod);

    Assert.AreSame(closedTypeOpenMethod, closedTypeClosedMethod.GetGenericMethodDefinition());
    Assert.AreSame(openTypeOpenMethod, openTypeClosedMethod.GetGenericMethodDefinition());
}
A: 

This is not so strange:

//void GenericClass<>.NestedGenericClass<>.GenericMethod<Int64>()
openTypeClosedMethod.ContainsGenericParameters = true
openTypeClosedMethod.IsGenericMethodDefinition = false

//void GenericClass<>.NestedGenericClass<>.GenericMethod<K>()
openTypeOpenMethod.ContainsGenericParameters = true
openTypeOpenMethod.IsGenericMethodDefinition = true

//void GenericClass<bool>.NestedGenericClass<int>.GenericMethod<K>()
closedTypeOpenMethod.ContainsGenericParameters = true
closedTypeOpenMethod.IsGenericMethodDefinition = true

//void GenericClass<bool>.NestedGenericClass<int>.GenericMethod<Int64>()
closedTypeClosedMethod.ContainsGenericParameters = false
closedTypeClosedMethod.IsGenericMethodDefinition = false

MethodInfo closedGeneratedMethod = closedTypeClosedMethod.GetGenericMethodDefinition();
MethodInfo openGeneratedMethod = openTypeClosedMethod.GetGenericMethodDefinition();

//void GenericClass<bool>.NestedGenericClass<int>.GenericMethod<K>()
closedGeneratedMethod.ContainsGenericParameters = true
closedGeneratedMethod.IsGenericMethodDefinition = true

//void GenericClass<>.NestedGenericClass<>.GenericMethod<K>()
openGeneratedMethod.ContainsGenericParameters = true
openGeneratedMethod.IsGenericMethodDefinition = true

Just compare all combinations from asserts.

P.S. You missed

Assert.AreNotSame(closedTypeOpenMethod, openTypeOpenMethod);
kek444
1) I didn't miss that case (it's parameters are reversed but I have it). 2) None of those checks are remarkable after the ones in my post, however I will be adding them plus more because they are important to the API. 3) You didn't answer any of my 3 questions, all of which refer to the fact that the .NET Framework implementation of ECMA-335 allows closed generic method within an open generic type. I'm working on my own implementation of the standard for fun, and I'm trying to figure out if I have to support that case or not. :)
280Z28
Okay, sorry for the 'unremarkable' post. The CLI nowhere explicitly forbids closed generic methods within an open generic type. It defines relations between a generic method and its declaring type, as well as nested generic types. The CLI only forbids partially constructed types, and instantiating open types, so I guess the method wouldn't be of much use, at least for instance methods. As for MethodDefSig and others, why don't you try emitting it to a dynamic assembly and analyzing from disk?
kek444