1
votes

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

2
Yes. I mean, you have all the information you need already, and you interpreted it correctly. It even makes sense. What else are you looking for?Luaan
Well, that´s what the errors say to me, but I do not fully understand the intense of this. What exactly makes sense from your point of view?HimBromBeere
Just think about it - you're returning a function that takes an argument of type T. If you were allowed to cast the interface to IGenericOrder<object>, it would return a function that takes an argument of type object. Not all Ts are also objects - you broke type safety. On the other hand, you can make T contra-variant - it's valid to pass a descendant instead of a parent class as an argument.Luaan

2 Answers

2
votes

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.

1
votes

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.