【问题标题】:How can I make the C# compiler infer these type parameters automatically?如何让 C# 编译器自动推断这些类型参数?
【发布时间】:2011-02-20 16:39:31
【问题描述】:

我有一些如下所示的代码。首先,我有一些领域类和一些特殊的比较器。

public class Fruit {
  public int Calories { get; set; }
  public string Name { get; set; }
}

public class FruitEqualityComparer : IEqualityComparer<Fruit> {
  // ...
}

// A basket is just a group of Fruits.
public class BasketEqualityComparer : IEqualityComparer<IEnumerable<Fruit>> {
  // ...
}

接下来,我有一个名为 ConstraintChecker 的辅助类。它有一个简单的BaseEquals 方法,可以确保考虑一些简单的基本情况:

public static class ConstraintChecker {
  public static bool BaseEquals(T lhs, T rhs) {
    bool sameObject = l == r;
    bool leftNull = l == null;
    bool rightNull = r == null;

    return sameObject && !leftNull && !rightNull;
  }

还有一个 SemanticEquals 方法,它只是一个 BaseEquals 检查和您指定的比较器函数。

  public static bool SemanticEquals<T>(
    T lhs, T rhs, Func<T, T, bool> f) {
    return BaseEquals(lhs, rhs) && f(lhs, rhs);
  }

最后还有一个SemanticSequenceEquals 方法,它接受两个IEnumerable&lt;T&gt; 实例进行比较,以及一个IEqualityComparer 实例,它将通过Enumerable.SequenceEquals 对列表中的每对元素进行调用。

  public static bool SemanticSequenceEquals<T, U, V>(U lhs,
                                                     U rhs,
                                                     V comparator)
    where U : IEnumerable<T>
    where V : IEqualityComparer<T> {
    return SemanticEquals(lhs, rhs, (l, r) => lhs.SequenceEqual(rhs, comparator));
  }
} // end of ConstraintChecker

SemanticSequenceEquals 的要点是,当您想要比较 IEnumerable&lt;T&gt;T 实例时,您不必定义两个比较器;现在您可以只指定一个IEqualityComparer&lt;T&gt;,它还会在您调用SemanticSequenceEquals 时处理列表。所以我可以摆脱 BasketEqualityComparer 类,这很好。

但是有一个问题。当您调用 SemanticSequenceEquals 时,C# 编译器无法确定涉及的类型:

// Error! Compiler can't infer the type parameters.
return ConstraintChecker.SemanticSequenceEquals(lhs, rhs,
  new FruitEqualityComparer());

如果我明确指定它们,它会起作用:

return ConstraintChecker.SemanticSequenceEquals<Fruit, IEnumerable<Fruit>,
  IEqualityComparer<Fruit>> (lhs, rhs, new FruitEqualityComparer());

显然,这很麻烦,而且也不是很干燥。我可以在这里更改什么,以便我不必显式编写类型参数?

【问题讨论】:

    标签: c# generics compiler-construction semantics


    【解决方案1】:

    尝试只指定 T 而没有像这样的 U 和 V。

    public static bool SemanticSequenceEquals<T>(
        IEnumerable<T> lhs, 
        IEnumerable<T> rhs, 
        IEqualityComparer<T> comparator)
    {
        return SemanticEquals(lhs, rhs, (l, r) => lhs.SequenceEqual(rhs, comparator));
    }
    

    【讨论】:

    • 我怎么没想到呢?这是最明显的解决方案,尽管我仍然必须明确指定 T 的类型参数。当它是两个方法参数的共享类型参数时,编译器似乎无法推断 T。
    【解决方案2】:

    您可以将参数显式键入到 SemanticSequenceEquals。这对我来说似乎编译得很好:

    public static bool SemanticSequenceEquals<T>(IEnumerable<T> lhs, IEnumerable<T> rhs, IEqualityComparer<T> comparator)
    {
        return SemanticEquals(lhs, rhs, (l, r) => lhs.SequenceEqual(rhs, comparator));
    }
    
    List<Fruit> a, b;
    return ConstraintChecker.SemanticSequenceEquals(a, b, new FruitEqualityComparer());
    

    【讨论】:

    • 确实如此。如果您不使用泛型,请不要使用泛型。您不需要具有 U 和 V 类型,因此不应包含它们。在此示例中,SemanticSequenceEquals 不需要知道它的参数实际上是 List,因为它只使用 IEnumerable 功能。
    • 你说得对,UV 可以被取消;我错过了各种明显的重构。不过,我似乎仍然必须明确指定T。您的示例(在 SemanticSequenceEquals 调用中省略了 T 规范)是否为您编译?
    • 它在 VS2010 中对我来说编译得很好,是的。不过,我不得不添加一些 where T : class 说明符。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 2012-11-30
    相关资源
    最近更新 更多