在C#中使用IComparer<T>.Compare(T,T)

4
我一直在尝试制作一个通用的反向优先队列,但是在EnQueue中仍然无法处理使用IComparer所带来的错误。
错误信息:Error 1 对于非静态字段、方法或属性“System.Collections.Generic.IComparer.Compare(T, T)”,需要对象引用。
public void inQ(T dat)//adding element in place, increasing order
    {
        if (start == null)//that means that the RPQ is empty
        {
            start = new node(dat);
            return;
        }
        if (IComparer<T>.Compare(start.data, dat) > 0)//Doesn't work
        {
            start = new node(dat, start);
            return;
        }
        //Default Case
        //no need for an else, actually
        node q = start;
        while (q.next != null && Comparer<T>.Default.Compare(q.next.data, dat) < 0)//Works Perfectly
            q++;
        q.next = new node(dat, q.next);
    }
1个回答

14

发生了什么

您需要一个IComparer<T>实例才能调用其上的Compare方法...请记住,这是一个接口,它没有静态方法。

这就是为什么这样不起作用的原因:

IComparer<T>.Compare(start.data, dat) > 0

另一方面,Comparer<T> 是一个类,它提供了一个静态属性 Default,可以为您提供 IComparer<T> 的实例...。
这就是为什么这个代码可以工作的原因:
Comparer<T>.Default.Compare(q.next.data, dat) < 0

获取 IComparer<T> 实例

你可以考虑存储一个字段来保存你的 IComparer<T> 实例,并使用它代替直接创建。这将最小化混淆,并且允许你在构造函数中获取一个 IComparer<T> 实例,以便存储在该字段上 - 这在客户端需要自定义行为时非常有用。


默认实例

如果您想使用默认比较器,可以从中获取它:

var comparer = Comparer<T>.Default;

这个比较器将为你使用的任何T提供默认行为。此行为相当于在T的实例上调用CompareTo,并额外处理null值。由于你不能在实例为null时使用方法CompareTo,你可能希望在调用CompareTo之前检查null……使用比较器可以解决这个问题。

实现 IComparer<T>

由于IComparer<T>是一个接口,您可以编写一个类来实现它,该类必须具有您想要的任何逻辑的Compare方法。然后创建该类的实例,并在系统需要IComparer<T>时使用它。

请参见MSDN上关于实现IComparer<T>的示例

// This class is not demonstrated in the Main method 
// and is provided only to show how to implement 
// the interface. It is recommended to derive 
// from Comparer<T> instead of implementing IComparer<T>. 
public class BoxComp : IComparer<Box>
{
    // Compares by Height, Length, and Width. 
    public int Compare(Box x, Box y)
    {
        if (x.Height.CompareTo(y.Height) != 0)
        {
            return x.Height.CompareTo(y.Height);
        }
        else if (x.Length.CompareTo(y.Length) != 0)
        {
            return x.Length.CompareTo(y.Length);
        }
        else if (x.Width.CompareTo(y.Width) != 0)
        {
            return x.Width.CompareTo(y.Width);
        }
        else
        {
            return 0;
        }
    }
}

您的比较器将是该类的实例:

var comparer = new BoxComp();

注意: 实际上文档建议从Comparer<T>继承而不是直接实现IComparer<T>,实用的原因是Comparer<T>除了实现IComparer<T>之外,还实现了IComparer


使用委托创建一个IComparer<T>

如果你不熟悉委托是什么,可以把它看作是对方法的引用,因此你可以有一个变量来引用这个方法并传递它。

如果你有一个指向比较方法的委托,那么你可以创建一个IComparer<T>... 这可以通过调用方法Comparer<T>.Create实现,该方法将接受你想要的方法的委托。例如,你可以使用lambda表达式如下:

var comparer = Comparer<string>.Create
(
    (str1, str2) => str1.Length.CompareTo(str2.Length)
);

上面的代码是这个的简写形式:
Comparison<string> comparison = (str1, str2) => str1.Length.CompareTo(str2.Length);
var comparer = Comparer<string>.Create(comparison);

这句话的意思是:“这又是一个缩写,代表着这个。”
Comparison<string> comparison = delegate(string str1, string str2)
{
   return str1.Length.CompareTo(str2.Length);
};
var comparer = Comparer<string>.Create(comparison);

除了上面的代码中方法是匿名的之外,这就像是某些东西的糖。
Comparison<string> comparison = StringComparison;
var comparer = Comparer<string>.Create(comparison);

// ...

private static int StringComparison(string str1, string str2)
{
    return str1.Length.CompareTo(str2.Length);
}

额外阅读材料:


Comparer<T> 能够使用我自己定义的任何 Compare<T> 方法吗? - Sherbi7y
由于IComparer<T>是一个接口,您可以编写一个类来实现它,该类必须具有您想要的任何逻辑的Compare方法。然后,创建该类的实例并在系统需要IComparer<T>的地方使用它。请参阅MSDN上实现IComparer<T>的示例 - Theraot
@Sherbi7y Comparer<T>.Default 不会使用您创建的自定义 IComparer<T>Comparer<T> 不会发现自定义实现。如果要使用自定义比较器,则需要获取其实例并使用它[请参见答案中如何获取一个]。通常做法是-如果您的类需要-则添加一个接受 IComparer<T> 的构造函数重载,如果没有提供回退到 Comparer<T>.Default。可以像这样 _comparer = comparer ?? Comparer<T>.Default; - Theraot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接