3

I have the following spec to help illustrate the problem:

class when_getting_type_of_generic_argument_using_subtype_instance
{
    static GenericTypeTester _genericTypeTester;
    static IPet _dog;
    static Type _result;

    Establish context =
        () =>
        {
            _genericTypeTester = new GenericTypeTester();

            _dog = new Dog();
        };

    Because of =
        () => _result = _genericTypeTester.Test(_dog);

    It should_return_the_subtype =
        () => _result.ShouldEqual(_dog.GetType());
}

class Dog : IPet
{
}

interface IPet
{
}

class GenericTypeTester
{
    public Type Test<T>(T dog) where T : IPet
    {
        return typeof (T);
    }
}

The spec above fails with the following message:

Expected: System.RuntimeType:[Pepino.ScenarioRunner.Selenium.Specs.Dog] But was: System.RuntimeType:[Pepino.ScenarioRunner.Selenium.Specs.IPet]

I need the result to be of type Dog. Is there something I can do without using reflection?

2 Answers 2

3

The issue here is the type being used at runtime versus compile time.

Because you declared _dog as an IPet, the variable passed into the generic method is an IPet at compile time despite being a Dog at runtime. Thus the compiler uses IPet for the generic parameter, even though the object at runtime is a Dog. Since you used typeof(T), you get the exact type given to the generic method by the compiler.

This can be seen by changing the type of _dog to Dog instead of IPet, which will then cause the compiler to infer the correct type.

This can also be avoided by explicitly casting the object as dynamic:

() => _result = _genericTypeTester.Test(_dog as dynamic);

Which will force the compiler to hold off the type inference until runtime, at which point it will determine that the object is type Dog. This is generally not a good idea for production code, however, since dynamic types are rather slow.

Sign up to request clarification or add additional context in comments.

4 Comments

Change static IPet _dog; to static Dog _dog; and you will see the type change to Dog
@Grax technically true, so I'll integrate that into my answer
@Grax is correct, but at runtime, I don't have that luxury. The type has to remain IPet. BUT, your "trick" of casting as dynamic made the spec pass immediately.
Perhaps your GenericTypeTester should return dog.GetType() instead of typeof(T) if you are wanting the runtime type of that variable.
0

Just add the constrait "class" to your method :

 class GenericTypeTester
 {
   public Type Test<T>(T dog) where T : IPet, class
   {
     return typeof (T);
   }
 }

1 Comment

This would prevent the rest of the code as-is from compiling, since object _dog is an IPet and thus stops meeting the given constraints. It also doesn't solve the given problem asked

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.