Search code examples
c#.net-coreasp.net-core-2.1generic-variance

How does contravariance work with Func delegate in .net core


I have the following piece of code where I am trying to write a generic validation rule for my domain objects. while doing so I have an issue to deal Func delegate supporting variance

public class Person { }
public class Employee : Person { }

internal interface IValidation<T> where T: Person  
{
     void AddValidationRule(Func<T,bool> predicateFunction);
}

internal class BaseValidation : IValidation<Person>
{
    void IValidation<Person>.RegisterValidationRules(Person person)
    {

    }
}

internal class EmployeeValidation : BaseValidation
{
    void RegisterValidation()
    {
        Func<Employee,bool> empPredicate = CheckJoiningDate;
        base.AddValidationRule(empPredicate);
    }

    bool CheckJoiningDate(Employee employee)
    {
        return employee.JoiningDate > DateTime.Now.AddDays(-1) ;
    }
}

With the above code in place, compiler gives an error message saying

Compiler Error on line : base.AddValidationRule(empPredicate); Argument 1: cannot convert from 'System.Func<>Employee, bool>' to 'System.Func<>Person, bool>

I had referred to this https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/dd465122%28v%3dvs.100%29 but I still couldn't make the compiler to understand about the contravariance here,

Appreciate your help so I understand this better


Solution

  • cannot convert from 'System.Func<>Employee, bool>' to 'System.Func<>Person, bool>

    base.AddValidationRule requires a function that can operate on any Person. Your function can only operate on Exployee which is more restrictive. This is the wrong form of variance.

    It is not shown here but likely BaseValidation implemented IValidation<Person>.

    Likely, the best fix is to ensure that you inherit from IValidation<Employee>, possibly by making BaseValidation generic.

    Would this work?

    internal class BaseValidation<T> : IValidation<T>
    {
        void IValidation<T>.RegisterValidationRules(T entity)
        {
        }
    }
    
    internal class EmployeeValidation : BaseValidation<Employee>
    {
     //...
    }