How does contravariance work with Func delegate in .net core

心已入冬 提交于 2021-02-05 07:00:52

问题


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://docs.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


回答1:


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>
{
 //...
}



回答2:


You've mixed up covariance and contravariance.

With covariance, the generic type argument can be "smaller" than what is required. That is, if we have:

Func<Mammal, Mammal> f1 = whatever;
Func<Mammal, Animal> f2 = f1;

Why does that work? Func is covariant in its second parameter. f2 is expecting a delegate that returns Animal. It got a delegate that returns a smaller type; there are fewer Mammals than Animals, so Mammal is smaller.

Think about why this works. When someone calls f2, they are expecting to get an animal back. But if they are really calling f1, they still get an animal, because every mammal is an animal.

With covariance, the "size" of the generic type varies in the same direction as the size of the type argument. Mammal is smaller than Animal. Func<Mammal, Mammal> is smaller than Func<Mammal, Animal>. That's why it is "co" variance, co meaning "together".

Contravariance is the opposite, hence "contra", meaning "against". With contravariance, the generic type argument can be bigger than expected:

 Func<Giraffe, Mammal> f3 = f1;

f3 expects a function that takes a giraffe; we have a function that takes a bigger type, mammal. It's bigger because there are more mammals than giraffes. Contravariance says this is good, and that should make sense. If someone calls f3 with a giraffe, it's OK if that is actually a call to f2, because f2 can take a giraffe; it can take any mammal.

You've mixed up covariance and contravariance; you're expecting that a contravariant parameter can be used in a covariant manner, which is wrong. A function that takes employees cannot be converted to a function that takes persons, because you could pass a non-employee person to it. A function that takes employees can be converted to a function that takes managers, because all managers are employees.



来源:https://stackoverflow.com/questions/54276476/how-does-contravariance-work-with-func-delegate-in-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!