C # универсальный метод сравнения [аналог шаблона C ++] - PullRequest
0 голосов
/ 07 июня 2018

И C ++, и C # поддерживают дженерики.Однако я не вижу способа переписать простой шаблон функции C ++, который сравнивает любые два аргумента (arg1> arg2?), В единый универсальный метод C #:

C ++

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

работает как с int, std::string, std::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> '

Является ли единственный способ заставить его работать как с целочисленными типами, так и с коллекциями в 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;
}

Ответы [ 3 ]

0 голосов
/ 07 июня 2018

Вы определили T для типа IComparable (или его потомка), когда написали:

   where T : IComparable<T>

и в своем примере вы использовали int в качестве типа для списка, который не относится к типуIComparable, поэтому выдается ошибка преобразования.

0 голосов
/ 07 июня 2018

Непосредственной причиной ошибки является то, что 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>());
0 голосов
/ 07 июня 2018

Если вы хотите сравнить на равенство, вы можете использовать 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);
}

Возможно, вам придется немного подробнее рассказать о том, что вы хотите сравнить, потому что некоторые концепции (например, больше , меньше , количество элементов и т. Д.)Не имеет смысла для любого произвольного T.


РЕДАКТИРОВАТЬ:

Похоже, вы хотите сравнить элементы коллекций.Не имеет смысла, не ограничивайте T, чтобы быть IEnumerable тогда.

Вы можете использовать Enumerable.SequenceEqual, чтобы сравнить, содержат ли коллекции те же элементы,и вы даже можете указать компаратор для каждого элемента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...