C# 通用比较方法 [C++ 模板的模拟]

C# generic compare method [analogue of C++ template]

C++ 和 C# 都支持泛型。但是,我看不到任何方法可以将比较任意两个参数(arg1 > arg2?)的简单 C++ 函数模板重写为单个 C# 泛型方法:

C++

template<typename T>
int compare(const T & st1, const T & st2) {
    if (std::less<T>() (st1, st2)) 
        return -1;
    return 1;
}

适用于 intstd::stringstd::vector

compare(33, 4);         // 1

std::vector<int> v1{ 1,0 }, v2{ 1,0,0 };
compare(v1, v2);        // -1

std::vector<std::string> vs1{ "hi", "bob" }, vs2{ "hi", "ben" };
compare(vs1, vs2);      // 1

C#

   class Demo
    {
        public static int Compare<T>(T v1, T v2) where T : IComparable<T>
        {
            if (v1.CompareTo(v2) < 0)
                return -1;
            return 1;
        }
    }

不适用于 C# Lists<>:

List<int> v1 = new List<int> { 1, 2 };
List<int> v2 = new List<int> { 3, 4 };
Console.WriteLine($"Compare(v1, v2): {Compare(v1, v2)}");

错误:没有从'System.Collections.Generic.List'到'System.IComparable>'
[=的隐式引用转换33=] 使它在 C# 中同时使用整数类型和集合的唯一方法是每次都重载吗?

public static int Compare<T>(List<T> v1, List<T> v2) where T : IComparable<T>
{
    for (int i = 0; i < v1.Count; i++)
    {
        if (v1[i].CompareTo(v2[i]) < 0)
            return -1;
    }
    return 1;
}

如果要比较相等性,可以使用 EqualityComparer<T>.Default,因为不能将泛型与 == 进行比较(== null 除外)。

/// <returns>
///     <see langword="true" /> if <paramref name="v1" /> is equal to <paramref name="v2" />; otherwise, <see langword="false" />.
/// </returns>
public static bool Compare<T>(T v1, T v2)
{
    return EqualityComparer<T>.Default.Equals(v1 , v2);
}

您可能需要详细说明您想要比较的内容,因为有些概念(例如 大于小于 , items count 等)对任意 T.

没有意义

编辑:

您似乎想比较 collections 的项目。没有意义不要将 T 限制为 IEnumerable 那么。

您可以使用Enumerable.SequenceEqual来比较collections是否包含相同的项目,您甚至可以为每个项目指定一个比较器。

您已将 T 定义为 IComparable 类型(或它的后代):

   where T : IComparable<T>

并且在您的示例中,您使用 int 作为列表的类型,它不是 IComparable 类型,因此会引发转换错误。

错误的直接原因是 List<int> 没有实现 IComparer<List<int>> 并且这个事实不符合方法的规范:

public static int Compare<T>(T v1, T v2) where T : IComparable<T>

因为 T 必须实施 IComparable<T>

我建议这样(快速但在某些情况下是肮脏的解决方案):

public static int Compare<T>(T v1, T v2, IComparer<T> comparer = null) {
  if (null == comparer)              // If we don't have tailored comparer
    comparer = Comparer<T>.Default;  // Try default one

  // If we don't know how to compare - throw exception
  if (null == comparer)
    throw new ArgumentNullException("comparer", 
      $"Type {typeof(T).Name} doesn't have default comparer; comparer must not be null.");

  // Taken from the question: 
  // if (v1.CompareTo(v2) < 0)
  //          return -1;
  //      return 1;
  // You, probably, may want just  
  // return comparer.Compare(v1, v2);
  return comparer.Compare(v1, v2) < 0 ? -1 : 1;
}

所以你可以把,在一个简单的例子中

int result = Compare(15, 25); // Comparer<int>.Default will be used

在没有默认比较器的复杂情况下,您必须实现它:

public class MyComparer<T> : IComparer<IEnumerable<T>> {
  public int Compare(IEnumerable<T> x, IEnumerable<T> y) {
    if (Object.ReferenceEquals(x, y))
      return 0;
    else if (null == x)
      return -1;
    else if (null == y)
      return 1;

    Comparer<T> comparer = Comparer<T>.Default;

    using (var en_x = x.GetEnumerator()) {
      using (var en_y = y.GetEnumerator()) {
        if (!en_x.MoveNext()) 
          if (!en_y.MoveNext())
            return 0;
          else
            return 1;
        else if (en_y.MoveNext())
          return -1;

        if (comparer != null) {
          int result = comparer.Compare(en_x.Current, en_y.Current);

          if (result != 0)
            return result;
        }
      }
    }

    return 0;
  }
}

并提供比较器

List<int> v1 = new List<int> { 1, 2 };
List<int> v2 = new List<int> { 3, 4 };

int another result = Compare(v1, v2, new MyComparer<int>());