MakePeaceGreatAgain
MakePeaceGreatAgain

Reputation: 37113

cast to base-type not supported for generic method

I have an interface IGenericOrder as follows:

using System;
using System.Linq.Expressions;

namespace MyNamespace
{
    interface IGenericOrder<T> where T : new()
    {
        Func<T, int> GetStatement(string name);
    }


    public class GenericOrder<T> : IGenericOrder<T> where T : new()
    {
        public Func<T, int> GetStatement(string name)
        {
            return (T) => 4;
        }    
    }
}

Now when I create an instance of that class and cast it back to IGenericOrder<object>

var sorter = (IGenericOrder<object>) new GenericOrder<Foo>();

I get Exception:

InvalidCastException: The instance of type GenericOrder<Foo> cannot be cast to IGenericOrder<object>

Which seems clear as the Interface IGenericOrder is not covariant. However if I make the interface covariant (interface IGenericOrder<out T>) I get a compiler-error:

Invalid variance: The type parameter 'T' must be contravariantly valid on IGenericOrder<T>.GetStatement(string). 'T' is covariant.

As I read from here covariance can only used when

[the generic type parameter] is used only as a method return type and is not used as a type of formal method parameters.

Does this mean I cannot use co-/contravariance for methods that themselfes return generic instances?

I am using .Net 4.5

Upvotes: 1

Views: 651

Answers (2)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 727077

To understand what is going on look at the definition of Func<T,TResult> delegate:

public delegate TResult Func<in T, out TResult>(
    T arg
)

As you can see, its first type parameter T is an in parameter. Therefore, you cannot use an out parameter as Func's first type argument. The second type parameter TResult is out, so using T as TResult would work:

interface IGenericOrder<out T> where T : new() {
    Func<int,T> GetStatement(string name);
}

public class GenericOrder<T> : IGenericOrder<T> where T : new() {
    public Func<int,T> GetStatement(string name) {
        return (x) => default(T);
    }    
}

Demo.

Upvotes: 2

Aasmund Eldhuset
Aasmund Eldhuset

Reputation: 38010

You are using T as the first type parameter of Func, so the only valid mode is contravariance because the T represents the type of the function's parameter. What you are trying to achieve with your cast is unsafe: you are saying "Here's a function which takes a Foo as a parameter. Please treat this as a function which takes any object as a parameter." If instead you had a Func<int, T>, you could use covariance, because a function that returns a Foo can also be viewed as a function that returns an object.

Upvotes: 1

Related Questions