何时在通用C#方法调用中显式指定类型参数?

时间:2014-07-03 05:50:31

标签: c# generics

我有一个泛型类,为具体类型的谓词提供常用方法(我使用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()方法无需明确指定类型参数

2 个答案:

答案 0 :(得分:1)

基本上,泛型类型参数的类型推断基于传递给方法的参数而起作用。

所以And()有效,因为你传递的是isAdult。编译器推断TPredPersonPredict,因为它是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,编译器应该已经选择了它。不幸的是,功能目标分辨率不考虑指定的值。这是因为当您使用=分配值时,还有其他因素可以发挥作用。可能存在定义的隐式类型转换,运算符可能已经重载,或者可能存在隐式构造函数。因此,无法保证受让人与实际价值的类型相同。