How to use C# LINQ Union to get the Union of Custom list1 with list2

I am using the Enumerable.Union<TSource> method to get the union of the Custom List1 with the Custom List2. But somehow it does not work as it should in my case. I am getting all the items also the duplicate once.

I followed the MSDN Link to get the work done, but still I am not able to achieve the same.

Following is the Code of the custom class:-

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

This I am calling as follows:-

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

Please suggest the appropriate way to achieve the same!

Jon Skeet
people
quotationmark

The oddity here is that your class implement IEqualityComparer<CustomClass> instead of IEquatable<CustomClass>. You could pass in another instance of CustomClass which would be used as the comparer, but it would be more idiomatic to just make CustomClass implement IEquatable<CustomClass>, and also override Equals(object).

The difference between IEquatable<T> and IEqualityComparer<T> is that IEquatable<T> says "I know how to compare myself with another instance of T" whereas IEqualityComparer<T> says "I know how to compare two instances of T". The latter is normally provided separately - just as it can be provided to Union via another parameter. It's very rare for a type to implement IEqualityComparer<T> for its own type - whereas IEquatable<T> should pretty much only be used to compare values of the same type.

Here's an implementation using automatically implemented properties for simplicity and more idiomatic parameter names. I'd probably change the hash code implementation myself and use expression-bodied members, but that's a different matter.

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

Now it doesn't help that (as noted in comments) the documentation for Enumerable.Union is wrong. It currently states:

The default equality comparer, Default, is used to compare values of the types that implement the IEqualityComparer<T> generic interface.

It should say something like:

The default equality comparer, Default, is used to compare values when a specific IEqualityComparer<T> is not provided. If T implements IEquatable<T>, the default comparer will use that implementation. Otherwise, it will use the implementation of Equals(object).

people

See more on this question at Stackoverflow