2

我有以下代码,我正在尝试为我的域对象编写通用验证规则。在这样做的同时,我有一个问题来处理 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但我仍然无法使编译器了解这里的逆变,

感谢您的帮助,以便我更好地理解这一点

4

2 回答 2

8

你混淆了协变和逆变。

使用协方差,泛型类型参数可以“小于”所需的参数。也就是说,如果我们有:

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

为什么这行得通? 在其第二个参数中Func协变的。f2期待一个返回的委托Animal。它有一个返回较小类型的委托;Mammals 比s少Animal,所以Mammal

想想为什么这有效。当有人打电话给 f2 时,他们期望得到一只动物。但如果他们真的叫 f1,他们还是会得到动物,因为每个哺乳动物都是动物。

使用协方差,泛型类型的“大小”与类型参数的大小方向相同。Mammal小于AnimalFunc<Mammal, Mammal>小于Func<Mammal, Animal>。这就是为什么它是“co”变体,co 意思是“在一起”。

逆变是相反的,因此是“对”,意思是“反对”。通过逆变,泛型类型参数可能比预期的要大:

 Func<Giraffe, Mammal> f3 = f1;

f3 需要一个带长颈鹿的函数;我们有一个函数需要更大的类型,哺乳动物。它更大是因为哺乳动物比长颈鹿还多。逆变说这很好,这应该是有道理的。如果有人用长颈鹿调用f3,那如果实际上是调用f2也没关系,因为f2可以带长颈鹿;它可以带走任何哺乳动物。

你混淆了协变和逆变;您期望可以以协变方式使用逆变参数,这是错误的。带员工的函数不能转换为带人员的函数,因为您可以将非员工人员传递给它。一个接受员工的函数可以转换为一个接受经理的函数,因为所有的经理都是员工。

于 2019-01-20T15:29:54.250 回答
1

无法从 '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>
{
 //...
}
于 2019-01-20T15:07:10.083 回答