Как противоречивость работает с делегатом Func в ядре .net - PullRequest
0 голосов
/ 20 января 2019

У меня есть следующий фрагмент кода, в котором я пытаюсь написать общее правило проверки для моих доменных объектов.при этом у меня возникает проблема, связанная с разрешением дисперсии, поддерживаемой делегатом Func

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) ;
    }
}

При наличии приведенного выше кода компилятор выдает сообщение об ошибке, в котором говорится:

Ошибка компилятора в строке: base.AddValidationRule (empPredicate);Аргумент 1: невозможно преобразовать 'System.Func <> Employee, bool>' в 'System.Func <> Person, bool>

Я ссылался на это https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/dd465122%28v%3dvs.100%29, но яЯ все еще не мог заставить компилятор понять, что такое противоречие,

Ценю вашу помощь, поэтому я лучше понимаю это

Ответы [ 2 ]

0 голосов
/ 20 января 2019

Вы смешали ковариацию и контравариантность.

При ковариации аргумент универсального типа может быть «меньше», чем требуется. То есть если у нас есть:

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

Почему это работает? Func - ковариант во втором параметре. f2 ожидает делегата, который возвращает Animal. У него есть делегат, который возвращает меньший тип; количество Mammal с меньше Animal с, поэтому Mammal меньше .

Подумайте, почему это работает. Когда кто-то звонит f2, он ожидает вернуть животное. Но если они действительно называют f1, они все равно получают животное, потому что каждое млекопитающее является животным.

При ковариации «размер» универсального типа изменяется в том же направлении , что и размер аргумента типа. Mammal меньше Animal. Func<Mammal, Mammal> меньше Func<Mammal, Animal>. Вот почему это «co» дисперсия, co означает «вместе».

Контравариантность - это противоположность, следовательно, "против", что означает "против". В отличие от этого аргумент универсального типа может быть больше ожидаемого:

 Func<Giraffe, Mammal> f3 = f1;

f3 ожидает функцию, которая принимает жирафа; у нас есть функция, которая принимает более крупного типа, млекопитающее. Это больше, потому что там больше млекопитающих, чем жирафов. Contravariance говорит, что это хорошо, и это должно иметь смысл. Если кто-то вызывает f3 с жирафом, то это нормально, если это на самом деле вызов f2, потому что f2 может взять жирафа; это может принять любое млекопитающее.

Вы смешали ковариацию и контравариантность; вы ожидаете, что контравариантный параметр можно использовать ковариантным образом, что неверно. Функция, которая принимает сотрудников, не может быть преобразована в функцию, которая принимает людей, поскольку вы можете передать ей сотрудника, не являющегося сотрудником. Функция, которая принимает сотрудников, может быть преобразована в функцию, которая принимает менеджеров, потому что все менеджеры являются сотрудниками.

0 голосов
/ 20 января 2019

невозможно преобразовать из 'System.Func <> Employee, bool>' в 'System.Func <> Person, bool>

base.AddValidationRule требуется функция, которая может работатьлюбой Person.Ваша функция может работать только на Exployee, что является более ограничительным.Это неправильная форма отклонения.

Здесь не показано, но, скорее всего, BaseValidation реализовано IValidation<Person>.

Вероятно, лучшим решением будет гарантировать, что вы наследуете от IValidation<Employee>возможно, сделав BaseValidation универсальным.

Будет ли это работать?

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

internal class EmployeeValidation : BaseValidation<Employee>
{
 //...
}
...