Can someone explain LINQ's intersect properly to me? I don't understand why this doesn't work

c#, linq, telerik-open-access

Solution

Consider the following implementation of `Page`:

public class Page
{
    public List<Attribute> AssociatedAttributes
    {
        get
        {
            return new List<Attribute>() { 
                new Attribute { Value = "a" }, 
                new Attribute { Value = "b" }, 
                new Attribute { Value = "c" }, 
            };
        }
    }
}

Here the `AssociatedAttributes` property is returning a different list each time `AssociatedAttributes` is called. Additionally, the actual items in the list are constructed each time the property is called, it's not just returning references to the same exact `Attribute` objects. Since `Attribute` (I assume) does not override `Equals` or `GetHashCode`, it will use the default `object` implementation, which considers objects equal if and only if they are referencing the same object. Since the objects in your two lists aren't referencing the same objects, none of them are equal to each other, even though they may have the same values internally.

The fact that `Attribute` implements `IComparable` is irrelivant.

There are several possible things that could be done to change the behavior:

Rather than constructing new objects in the property getter, return references to the same actual objects. This would likely mean having a private backing field for the property, constructing the objects once, and then returning references to the same list.

Override `Equals` and `GetHashCode` in `Attribute` to be dependent on its values, rather than its reference. Convention would dictate that you only do this if the object is immutable, as dealing with objects that have mutating `GetHashCode` values is...difficult. You could implement `IEquatable` in addition to doing this, if you wanted, to provide a statically typed `Equals` method. Note that it's vital to override `GetHashCode` if you implement `IEquatable`, if you still want it to be useful.

Create a new object that implements `IEqualityComparer<Attribute>`. This would be an object external to `Attribute` that knows how to compare them for equality based on something other than the `Equals` and `GetHashCode` implementation of the object itself. Provide an instance of such a type to `Intersect` and `Distinct`. (They each have overloads for custom equality comparers.)

Problem

I have an object called Page, with an instance called p that has a custom property called AssociatedAttributes. If I do the following: ``` int numMatchingAttributes = p.AssociatedAttributes.Intersect( p.AssociatedAttributes ).Distinct( ).Count( ); ``` numMatchingAttributes ends up equaling 0 even though p has 6 AssociatedAttributes. Why does it not equal 6? `AssociatedAttributes` is of Type `List<Attribute>` (`Attribute` is my own class, not `System.Attribute`) and `Attribute` implements `IComparable<Attribute>`, I did not have it implement `IEquatable`, should I? This is the implementation of `CompareTo` in Attribute: ``` public int CompareTo(Attribute other) { return Id.CompareTo(other.Id); } ``` `Id` is of type `Guid`. this is the AssociatedAttributes property on Page: ``` public List<Attribute> AssociatedAttributes { get { List<Attribute> list = new List<Attribute>( ); using ( PredictiveRecommendor dbContext = new PredictiveRecommendor()){ if ( dbContext != null ) { IQueryable<Attribute> query = from a in dbContext.PageAttributes where a.Page.Id.Equals(this.Id) select a.Attribute; list = query.ToList(); } } return list; } } ``` (dbContext is a Telerik OpenAccess context) Update: Here is what ended up working: In Page is the following method: ``` public int numberOfIntersectedAssociatedAttributes ( Page other ) { using ( PredictiveRecommendor dbContext = new PredictiveRecommendor( ) ) { IQueryable<Attribute> thisAssocAttributes = from a in dbContext.PageAttributes where a.Page.Id.Equals( this.Id ) select a.Attribute; IQueryable<Attribute> otherAssocAttributes = from a in dbContext.PageAttributes where a.Page.Id.Equals( other.Id ) select a.Attribute; IQueryable<Attribute> interSection = thisAssocAttributes.Intersect( otherAssocAttributes ); return interSection.ToList( ).Count; } } ``` It's ugly, but it works.

Original source