#c# #linq #union #extension-methods
#c# #linq #объединение #методы расширения
Вопрос:
Я использую Enumerable.Union<TSource>
метод для получения объединения пользовательского List1 с пользовательским List2. Но почему-то в моем случае это работает не так, как должно. Я получаю все элементы, также дублирующие один раз.
Я перешел по ссылке MSDN, чтобы выполнить работу, но все же я не могу добиться того же.
Ниже приведен код пользовательского класса:-
public class CustomFormat : IEqualityComparer<CustomFormat>
{
private string mask;
public string Mask
{
get { return mask; }
set { mask = value; }
}
private int type;//0 for Default 1 for userdefined
public int Type
{
get { return type; }
set { type = value; }
}
public CustomFormat(string c_maskin, int c_type)
{
mask = c_maskin;
type = c_type;
}
public bool Equals(CustomFormat x, CustomFormat y)
{
if (ReferenceEquals(x, y)) return true;
//Check whether the products' properties are equal.
return x != null amp;amp; y != null amp;amp; x.Mask.Equals(y.Mask) amp;amp; x.Type.Equals(y.Type);
}
public int GetHashCode(CustomFormat obj)
{
//Get hash code for the Name field if it is not null.
int hashProductName = obj.Mask == null ? 0 : obj.Mask.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = obj.Type.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
Это я вызываю следующим образом:-
List<CustomFormat> l1 = new List<CustomFormat>();
l1.Add(new CustomFormat("#",1));
l1.Add(new CustomFormat("##",1));
l1.Add(new CustomFormat("###",1));
l1.Add(new CustomFormat("####",1));
List<CustomFormat> l2 = new List<CustomFormat>();
l2.Add(new CustomFormat("#",1));
l2.Add(new CustomFormat("##",1));
l2.Add(new CustomFormat("###",1));
l2.Add(new CustomFormat("####",1));
l2.Add(new CustomFormat("## ###.0",1));
l1 = l1.Union(l2).ToList();
foreach(var l3 in l1)
{
Console.WriteLine(l3.Mask " " l3.Type);
}
Пожалуйста, предложите подходящий способ добиться того же!
Комментарии:
1. Это кажется странным, но ваш код работает, если вы а) предоставляете конструктор без параметров для CustomFormat и передаете экземпляр этого класса методу объединения — см. dotnetfiddle.net/YTVwTI . Тогда возникает вопрос, почему объединение игнорирует реализацию IEqualityComparer<CustomFormat> внутри класса.
Ответ №1:
Странность здесь в том, что ваш класс реализует IEqualityComparer<CustomClass>
вместо IEquatable<CustomClass>
. Вы могли бы передать другой экземпляр CustomClass
, который будет использоваться в качестве средства сравнения, но было бы более идиоматично просто сделать CustomClass
реализацию IEquatable<CustomClass>
, а также переопределить Equals(object)
.
Разница между IEquatable<T>
и IEqualityComparer<T>
заключается в том, что IEquatable<T>
говорится: «Я знаю, как сравнить себя с другим экземпляром T
«, тогда IEqualityComparer<T>
как говорится: «Я знаю, как сравнить два экземпляра T
«. Последнее обычно предоставляется отдельно — так же, как оно может быть предоставлено с Union
помощью другого параметра. Очень редко тип реализуется IEqualityComparer<T>
для своего собственного типа, тогда IEquatable<T>
как в значительной степени должен использоваться только для сравнения значений одного и того же типа.
Вот реализация, использующая автоматически реализованные свойства для простоты и более идиоматические имена параметров. Я бы, вероятно, сам изменил реализацию хэш-кода и использовал члены с выражением, но это другое дело.
public class CustomFormat : IEquatable<CustomFormat>
{
public string Mask { get; set; }
public int Type { get; set; }
public CustomFormat(string mask, int type)
{
Mask = mask;
Type = type;
}
public bool Equals(CustomFormat other)
{
if (ReferenceEquals(this, other))
{
return true;
}
return other != null amp;amp; other.Mask == Mask amp;amp; other.Type == Type;
}
public override bool Equals(object obj)
{
return Equals(obj as CustomFormat);
}
public override int GetHashCode()
{
// Get hash code for the Name field if it is not null.
int hashProductName = Mask == null ? 0 : Mask.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = Type.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
Теперь это не помогает, что (как отмечено в комментариях) документация для Enumerable.Union
неверна. В настоящее время в нем говорится:
Средство сравнения равенства по умолчанию,
Default
, используется для сравнения значений типов, реализующихIEqualityComparer<T>
универсальный интерфейс.
В нем должно быть что-то вроде:
Средство сравнения равенства по умолчанию,
Default
, используется для сравнения значений, когда конкретноеIEqualityComparer<T>
значение не указано. ЕслиT
реализованоIEquatable<T>
, средство сравнения по умолчанию будет использовать эту реализацию. В противном случае он будет использовать реализациюEquals(object)
.
Комментарии:
1. Я попытался реализовать, но, похоже, это не решает проблему. Пожалуйста, посмотрите ссылку на код. Ошибка не отображается, но отображаются повторяющиеся элементы.
2. @JDoshi: эта скрипка все еще реализуется
IEqualityComparer<T>
. Приведенный мною код (IEquatable<T>
вместо реализации) работает с приведенным вами примером.3. На самом деле я отредактировал ту же скрипку и поделился с вами ссылкой, которая изначально была предоставлена stuartd . Итак, вы случайно поделились неправильной скрипкой. Извините за ошибку: (
4.@JDoshi: ваш код не переопределяет
Object.GetHashCode
. Вместо этого он все еще реализуетсяIEqualityComparer<T>.GetHashCode
. Если бы вы использовали код в моем ответе, все было бы в порядке — и точно так же вы бы увидели в этом проблему, если бы посмотрели на предупреждения компилятора (которые вы переопределялиEquals
, но нетGetHashCode
). Всегда обращайте внимание на предупреждения.
Ответ №2:
Вам нужно передать экземпляр an IEqualityComparer
в Union
метод. Метод имеет перегрузку для передачи в вашем компараторе.
Самое простое и уродливое решение
var comparer = new CustomFormat(null,0);
l1 = l1.Union(l2, comparer).ToList();
Вы допустили некоторые ошибки в своей реализации. Вы не должны реализовывать IEqualityComparer
метод в своем type ( CustomFormat
), а в отдельном классе, например CustomFormatComparer
.
В вашем type ( CustomFormat
) вы должны быть реализованы IEquatable
.
Комментарии:
1. Ссылка MSDN для объединения вводит в заблуждение, тогда, как говорится, «Средство сравнения равенства по умолчанию, Default, используется для сравнения значений типов, которые реализуют универсальный интерфейс IEqualityComparer<T> . Чтобы сравнить пользовательский тип данных, вам необходимо реализовать этот интерфейс и предоставить свои собственные методы GetHashCode и Equals для типа .» это не оговаривает, что это должно быть в отдельном классе.
2. @Jehof Не могли бы вы объяснить использование
IEquatable
в этом случае. ЗдесьIEquatable
используется прямая реализация, а неIEqualityComparer
ошибка, посколькуUnion
используется ошибка.3. @JDoshi: Нет, это не так — реализация
IEquatable<T>
действительно является способом продвижения вперед.