Search code examples
c#.netgenericsgeneric-variance

Co/contravariance with Func<in T1, out TResult> as parameter


Assume I have an interface such as

public interface IInterface<in TIn, out TOut> {
  IInterface<TIn, TOut> DoSomething(TIn input);
}

TIn being contra-variant, and TOut being co-variant.

Now, I want callers to be able to specify some function to be executed on the input's value, so naïvely I would add the following method to the interface:

IInterface<TIn, TOut> DoSomethingWithFunc(Func<TIn, TOut> func);

which … does not work. TIn is now required to be covariant, and TOut contravariant.

I understand, that I cannot use covariant generic types as input for methods, but I thought I could use them in a nested generic type which itself specifies the variance (Func<in T1, out TResult>).

I tried creating a new delegate type with co-/contravariant types and change the interface to accept an argument of this type, to no avail (same error).

public delegate TOut F<in TDlgIn, out TDlgOut>(TDlgIn input);

public interface IInterface<in TIn, out TOut> {
  IInterface<TIn, TOut> DoSomethingWithFunc(F<TIn, TOut> func);
}

I there a way I can make the compiler happy? Is this even possible (for instance with other nested types, or additional generic arguments)? If not, why not?


Solution

  • This would not be safe since you could then use it to do:

    public class Id<I, O> : IInterface<I, O>
    {
        private Func<I, O> f;
        public Id(Func<I, O> f) { this.f = f; }
        public IInterface<I, O> DoSomething(I i) { this.f(i); return this; }
        public IInterface<I, O> DoSomethingWithFunc(Func<I, O> newF) {
            this.f = newF;
            return this;
        }
    }
    

    and then

    Func<Animal, string> fa;
    IInterface<object, string> oi = new Id<object, string>(_ => "");
    Interface<Monkey, string> mi = oi;  //safe
    IInterface<Monkey, string> mi2 = mi.DoSomethingWithFunc(fa);
    oi.DoSomething("not an animal!");
    

    at this point you will have passed a string to an Func<Animal, string>.