Search code examples
c#genericslambdacastingcontravariance

C# how to workaround contravariance in Func<T, bool>?


Suppose I have following code:

class Program
{
    static IList<Func<object, bool>> _exclusions = new List<Func<object, bool>>();

    static void Main(string[] args)
    {       
        SetExclusions<PropertyInfo>(x => typeof(ValueType).IsAssignableFrom(x.PropertyType));           
    }

    public static void SetExclusions<T>(Func<T, bool> lambda)
        where T : class
    {
        Func<object, bool> l = (Func<object, bool>)lambda; // <= InvalidCastException
        _exclusions.Add(l);
    }
}

Of course it doesn't work because T type parameter in Func is contravariant - I can't pass PropertyInfo as more derived than object.

Is there a way to get around it? From my point of view it would be perfectly correct to do something like this afterwards:

foreach (var e in GetExclusions<PropertyInfo>())
{
    var a = members.Where(e);    
}

Solution

  • Before suggesting anything, the current design is most probably not appropriated to the problem you need to solve. So I'd really recommend revisiting it whenever there is a chance

    Now to your question, one option could be casting the object to the expected type inside the lambda; Something like this:

    Func<object, bool> l = o => lambda((T) o); 
    

    Then you could keep track in either a Dictionary<Type, Func<object, bool>> of what type of expressions work for each different Type. There could be other alternatives if you don't want to keep a dictionary, but they would involve using a class that holds the same information