Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LINQ and Element comparison issue

2 REPLIES 2
SOLVED
Reply
Message 1 of 3
snajjar
423 Views, 2 Replies

LINQ and Element comparison issue

I am getting unexpected results when trying any of the LINQ queries that involve element comparison.

 

For example List<Element>.Except(....) does not exclude the identical elements from the list unless I provide a comparer class that compares by ElementId.

 

Is this by design? Or is there something wrong with the Element equality operator in the API?

 

I have included a minimal example file in Revit 2022 with embedded macro that has 2 methods, SelectInverseFails and SelectInverseSucceeds, They both have the same exact code apart from using the custom comparer class in the second one inside the Except Linq function.

It asks the user to select walls and then tries to invert the selection.

SelectInverseFails will always select all the walls regardless of the user's selection.

 

Here is the code I'm using in the macro and you can find it embedded in the attached Revit file.

 

/*
 * Created by SharpDevelop.
 * User: snajjar
 * Date: 2/11/2022
 * Time: 4:26 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;

namespace TestElementComparison
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.DB.Macros.AddInId("A48B9712-B364-4266-8A9D-6E07CF36B1AC")]
	public partial class ThisDocument
	{
		private void Module_Startup(object sender, EventArgs e)
		{

		}

		private void Module_Shutdown(object sender, EventArgs e)
		{

		}

		#region Revit Macros generated code
		private void InternalStartup()
		{
			this.Startup += new System.EventHandler(Module_Startup);
			this.Shutdown += new System.EventHandler(Module_Shutdown);
		}
		#endregion
		public void SelectInverseFails()
		{
			var doc = this.Document;
			var selection = Selection.PickObjects(ObjectType.Element, new WallSelectionFilter());
			var selectedWalls = selection.Select(r => doc.GetElement(r)).ToList();
			var allWalls = new FilteredElementCollector(doc).WhereElementIsNotElementType().OfClass(typeof(Wall)).ToList();
			var unselectedWalls = allWalls.Except(selectedWalls).ToList();
			this.Selection.SetElementIds(unselectedWalls.Select(w => w.Id).ToList());
		}
		public void SelectInverseSucceeds()
		{
			var doc = this.Document;
			var selection = Selection.PickObjects(ObjectType.Element, new WallSelectionFilter());
			var selectedWalls = selection.Select(r => doc.GetElement(r)).ToList();
			var allWalls = new FilteredElementCollector(doc).WhereElementIsNotElementType().OfClass(typeof(Wall)).ToList();
			var unselectedWalls = allWalls.Except(selectedWalls, new ElementComparerById()).ToList();
			this.Selection.SetElementIds(unselectedWalls.Select(w => w.Id).ToList());
		}
	}
	
	public class WallSelectionFilter : ISelectionFilter{
		
		
		public bool AllowElement(Element elem)
		{
			return elem is Wall;
		}
		
		public bool AllowReference(Reference reference, XYZ position)
		{
			return true;
		}
	}
	
	public class ElementComparerById : IEqualityComparer<Element> 
    {
        public bool Equals(Element x, Element y)
        {
            if (ReferenceEquals(x, y)) return true;
            if (ReferenceEquals(x, null)) return false;
            if (ReferenceEquals(y, null)) return false;
            if (x.GetType() != y.GetType()) return false;
            return Equals(x.Id.IntegerValue, y.Id.IntegerValue);
        }

        public int GetHashCode(Element obj)
        {
            return (obj.Id != null ? obj.Id.IntegerValue.GetHashCode() : 0);
        }
    }
}

 

I It would be great if someone can shed some light on this.

Thanks,

Sam

2 REPLIES 2
Message 2 of 3
RPTHOMAS108
in reply to: snajjar

As you found Element class does not Implement IEquatable(Of Element) or override Object.Equals. If you want to do such comparisons then do them with ElementId i.e. ToElementIds (Elementid class overrides Object.Equals). You can also provide your own comparer as you've done.

 

Can confirm the above for each class in RevitAPI.chm under the member listing. Usually the members that come directly from object unchanged are in bold black font. The description will also list if it has been overridden.

 

They could have probably overridden Element.Equals to compare by ElementId and document combination. ElementId comparisons themselves are probably ignorant to the document the id resides in (likely it just compares the integer value). Largely not an issue because when do people mix the ids from two documents into one list. It's always a trade off between speed of comparison and true uniqueness of identity.

Message 3 of 3
snajjar
in reply to: RPTHOMAS108

Thank you for the confirmation, it just seems weird not to be able to compare the same object to itself out-of-the-box.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Forma Design Contest


Rail Community