Search code examples
c#contravariance

Difference in ability to use contravariance when using a Func<T> parameter


I am curious as to why the following logic is allowed in C#

private static void Foo(Func<Exception, string> func)
{
    try
    {
        // ...  
    }
    catch (ArgumentException ex)
    {
        func(ex);
    }
}

whereas this is not

private static void Foo<T>(Func<T, string> func) where T : Exception
{
    try
    {
        // ...  
    }
    catch (ArgumentException ex)
    {
        func(ex);
    }
}

From what I understand, since the Func<in TArg1, out TResult> delegate has a contravariant parameter since .NET 4.0, the first scenario is not problem, but why does switching to a constrained generic type change the ability of the compiler to apply this contravariance ?


Solution

  • Your first function will compile, however due to contravariance of Func parameters, you can only pass a Func with a parameter which is a supertype of Exception e.g.

    Func<object, string> f = o => o.ToString();
    Foo(f);
    

    this is not the case in the second example, which would require you to pass a Func with a parameter which is a subtype of Exception e.g

    Func<InvalidOperationException> f = o => ...
    

    which would not be safe.