C # универсальный метод сравнения [аналог шаблона C ++]

И 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;
}

0

Решение

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

Другие решения

Если вы хотите сравнить на равенство, вы можете использовать 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 чтобы сравнить, содержат ли коллекции одни и те же элементы, и вы даже можете указать компаратор для каждого элемента.

1

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

   where T : IComparable<T>

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

0
По вопросам рекламы [email protected]