2016-10-04 1 views
7

Я использую метод Enumerable.Union<TSource>, чтобы получить объединение пользовательского списка1 с помощью Custom List2. Но почему-то это не работает, как должно быть в моем случае. Я получаю все предметы и дубликат один раз.Как использовать C# LINQ Union для получения Объединения пользовательского списка1 со списком2

Я следовал за MSDN Link, чтобы выполнить эту работу, но все же я не могу добиться того же.

Ниже приводится код пользовательского класса: -

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 && y != null && x.Mask.Equals(y.Mask) && 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); 
} 

Пожалуйста, предложите подходящий способ для достижения того же!

+3

Кажется странным, но ваш код работает, если вы a) предоставляете конструктор без параметров для CustomFormat и передаете экземпляр этого класса в метод Union - см. Https://dotnetfiddle.net/YTVwTI. Тогда возникает вопрос, почему Union игнорирует реализацию IEqualityComparer внутри класса. – stuartd

ответ

8

Чувство в том, что ваш класс реализует 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 && other.Mask == Mask && 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 неправильно. В настоящее время он гласит:

По умолчанию Comparer равенства, Default, используется для сравнения значений типов, которые реализуют общий интерфейс IEqualityComparer<T>.

Он должен сказать что-то вроде:

компаратора равенства по умолчанию, Default, используются для сравнения значений, когда конкретный IEqualityComparer<T> не предусмотрен. Если T реализует IEquatable<T>, то по умолчанию используется этот компаратор по умолчанию. В противном случае он будет использовать реализацию Equals(object).

+0

Я попытался реализовать, но, похоже, это не проблема. См. Код [link] (https://dotnetfiddle.net/YTVwTI). Он не показывает ошибки, но отображаются повторяющиеся элементы. – JDoshi

+0

@JDoshi: Эта скрипка все еще реализует 'IEqualityComparer '. Код, который я дал (вместо этого реализует «IEquatable ») * работает * с примером, который вы указали. –

+0

На самом деле я отредактировал эту ту же скрипку и поделился с вами ссылкой, которая была совместно использована stuartd. Таким образом, вы случайно поделились неправильной скрипкой. Извините за ошибку :( – JDoshi

3

Вам необходимо сдать экземпляр IEqualityComparer к методу Union. Метод имеет перегрузку, которую вы можете передать в своем компаменте.

Самое простое и уродливое решение

var comparer = new CustomFormat(null,0); 

l1 = l1.Union(l2, comparer).ToList(); 

Вы сделали какую-то ошибку в вашей реализации. Вам не следует применять метод IEqualityComparer для вашего типа (CustomFormat), но в отдельном классе, например CustomFormatComparer.

На ваш тип (CustomFormat) вы должны реализовать IEquatable.

+4

Ссылка MSDN для Союза вводит в заблуждение, тогда, как сказано: «По умолчанию равенство по умолчанию, по умолчанию используется для сравнения значений типов, реализующих общий интерфейс IEqualityComparer . ** Для сравнения пользовательского типа данных вам необходимо для реализации этого интерфейса и предоставления собственных методов GetHashCode и Equals для типа **. "_ это не оговаривается, что это должно быть в отдельном классе. – stuartd

+0

@Jehof Не могли бы вы объяснить использование «IEquatable» в этом случае. Здесь реализация 'IEquatable' напрямую, а не' IEqualityComparer' дает ошибку, так как используется 'Union'. – JDoshi

+0

@JDoshi: Нет, это не реализует 'IEquatable ' действительно путь вперед. –

Смежные вопросы