我有一个泛型类,为具体类型的谓词提供常用方法(我使用PredicateBuilder
扩展方法)
public class GenericPredicate<TItem>
{
protected GenericPredicate(Expression<Func<TItem, bool>> predicate)
{
Predicate = predicate;
}
protected GenericPredicate()
{
}
public Expression<Func<TItem, bool>> Predicate { get; protected set; }
//Combines two predicates
public TPred And<TPred>(TPred second) where TPred : GenericPredicate<TItem>, new()
{
return new TPred { Predicate = Predicate.And(second.Predicate) };
}
//Negates the predicate
public TPred Not<TPred>() where TPred : GenericPredicate<TItem>, new()
{
return new TPred { Predicate = Predicate.Not() };
}
}
具体的谓词:
public class PersonPredicate : GenericPredicate<Person>
{
protected PersonPredicate(Expression<Func<Person, bool>> predicate)
: base(predicate)
{
}
public PersonPredicate()
{
}
public static PersonPredicate IsAdult()
{
return new PersonPredicate(p => p.Age >= 18);
}
public static PersonPredicate IsFemale()
{
return new PersonPredicate(p => Equals(p.Gender, Gender.Female));
}
}
在我实例化具体谓词的工厂中,我得到了错误
方法Not的类型参数无法从用法
中推断出来
调用通用Not()
PersonPredicate isFemale = PersonPredicate.IsFemale();
PersonPredicate isAdult = PersonPredicate.IsAdult();
PersonPredicate femaleAndAdult = isFemale.And(isAdult);
PersonPredicate notFemale = isFemale.Not(); //Error as described
PersonPredicate notFemaleWorkaround = isFemale.Not<PersonPredicate>(); // Works as suggested by compiler
因此,编译器不知道TItem
是什么。令我困惑的是,通用And()
方法无需明确指定类型参数
答案 0 :(得分:1)
基本上,泛型类型参数的类型推断基于传递给方法的参数而起作用。
所以And()
有效,因为你传递的是isAdult
。编译器推断TPred
为PersonPredict
,因为它是isAdult
的类型,并且没有其他约束。
对于Not()
,您没有传递任何参数,因此编译器没有关于您希望TPred
的信息。这是它无法推断的 - 不是TItem
。
如果你真的想要这个,你可以用两个类型参数声明GenericPredicate
,其中一个参数应该是子类本身
public class GenericPredicate<TPred, TItem>
where TPred : GenericPredicate<TPred, TItem>, new()
和
public class PersonPredicate : GenericPredicate<PersonPredicate, Person>
您可能希望将GenericPredicate<TPred,TItem>
作为GenericPredicate<TItem>
的子类,以便其他代码仍然接受只是GenericPredicate<TItem>
。但是,在这一点上它完全是错综复杂的 - 你最好只指定类型参数。
答案 1 :(得分:1)
在两个示例中,编译器都会尝试确定应该用于解析函数的谓词TPred
的类型。
在这个例子中:
isFemale.And(isAdult);
编译器发现已将PersonPredicate
isAdult
传递给函数。因此,它可以确定在函数调用中,TPred
实际上是PersonPredicate
。
在另一个例子中,
isFemale.Not();
编译器没有任何提示可以理解适合TPred
的内容。因此,它需要具体的指示来解决冲突。
isFemale.Not<PersonPredicate>();
现在编译器可以正确解析调用。
最后,您可能认为因为您将函数调用的结果赋给PersonPredicate
,编译器应该已经选择了它。不幸的是,功能目标分辨率不考虑指定的值。这是因为当您使用=
分配值时,还有其他因素可以发挥作用。可能存在定义的隐式类型转换,运算符可能已经重载,或者可能存在隐式构造函数。因此,无法保证受让人与实际价值的类型相同。