Click here to Skip to main content
15,886,830 members
Articles / Programming Languages / C#
Article

Filtering object collections using reflection

Rate me:
Please Sign up or sign in to vote.
3.19/5 (9 votes)
25 Oct 20052 min read 39.8K   311   19   8
An article that shows how to filter a strongly typed collection using reflection.

Introduction

We all know that when writing software, re-use is of importance to get the most out of the time we have. When we apply this to object collections, we should be aiming to write collection agnostic code that can be used over and over again. For example, when we use a collection of say User objects, we should be able to give it the same operation as we give a collection of Car objects. The operation that we pass to the collection shouldn't care if the collection has cars, users or elephants. It just knows how to perform its operation.

Background

I stumbled on a great article recently on sorting object collections using reflection. The PropertySorter would be passed to the sort method of any strongly typed collection and would sort it depending on the values passed. Doing the same thing with filtering was therefore my aim: to be able to have a strongly typed collection that would implement an IFilterable interface and take in a ReflectiveFilter.

Reflective Filter Features

The features of the ReflectiveFilter are as follows:

  • Use the FilterOperand to decide if you want to place And or Or between the FilterProperties.
  • Use the FilterClause to decide if you want to place a Equals or IsNotEqualTo in the filter criteria.
  • Add as many filters as you like using the AddFilter method of the ReflectiveFilter.

Using the code

To use the ReflectiveFilter is easy. Simply instantiate a new instance of the filter, add the filters you need, and the Filter method will pass back a strongly typed collection that is filtered on your requirements.

Example: We have a collection of names and IDs and want to filter it so we only get back names that are equal to Phil or Richard.

C#
// In the reflective filter, we pass it (at the least)
// the typeof object that is contained in the collection we are filtering
// We also pass in the type of collection we arefiltering (and expect back)

ReflectiveFilter rFilter = new 
 ReflectiveFilter(typeof(CompanyName.DomainObjects.ConstrainedType.NameIdReference),
 typeof(CompanyName.DomainObjects.ConstrainedType.NameIdReferenceCollection), 
 FilterClause.Equals, FilterOperand.Or);
// add  filters 
// as above we have used the OR operator and the Equals clause
// this means we will get all records where
// the name=Richard OR name=Phil
rFilter.AddFilter("Name", "Richard");
rFilter.AddFilter("Name", "Phil");

ACollectionType filteredCollection = new NameIdReferenceCollection();
filteredCollection = nameIdReferenceCollection.Filter(rFilter);

As you can see from the example, we pass in up to four parameters.

  • The type of object contained in the collection you are filtering (required).
  • The type of collection being filtered (required).
  • The FilterClause (optional / defaults to FilterClause.Equals).
  • The FilterOperand (optional / defaults to FilterOperand.Or).

The FilterClause can be set to either Equals or IsNotEqualTo and defaults to Equals.

The FilterOperand can be set to either Or or And, and defaults to Or. The job of the FilterOperand is when we have more than one filter applied, to either specify that we need to look for (for example) records where Parameter=Value Or/And Parameter=Value.

The default constructor accepts just two parameters:

  • The typeOfObject: The type of object that is contained in the collection we are filtering.
  • The typeOfCollection: The type of collection that is being filtered.

Conclusion

The ReflectiveFilter seems to perform quickly and because it is collection agnostic, means that most other requirements for custom filters are not needed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralFiltering object collections using reflection Pin
Martin Van de Moortel28-Nov-07 1:21
Martin Van de Moortel28-Nov-07 1:21 
GeneralThanks! Pin
kennster12-Apr-06 5:05
kennster12-Apr-06 5:05 
This code was exactly what I needed, and incredibly fast compared to the other alternatives on CodeProject. I first tried to adapt my collection class for my business object to inherit from your ACollection class, etc, then realized that was pretty stupid, the whole point of your class is to return a filtered collection of objects.

In case this helps anybody, here is how I used your code:

My application is a Windows Forms based application leveraging a relatively old .NET Data Access Layer based upon something like what a DataRow is supposed to do. This DAL has Collection Classes and Business Object classes, which make it simple to do normal data operations.

My application was tasked with extending one particular entity in the DAL and allowing user's to filter data within a collection of that entity. Instead of modifying my own classes, all I did was copy all of the code from the NameIdReferenceCollection into a new class designed to filter my own business object, and rename all appropriate methods etc to refer to my business object instead of the NameIdReferece object.

Next, I simply copied the sample code you provided to create new filtered collection, iterating through my application's documents collection, and adding or removing documents as appropriate based upon if they were in the filtered collection. I had this run twice: once with FilterClause.Equals, the next without.

Following is what I have so far that works with no noticeable issues: (Note there are more comments following the code about the code)
private void cboStatus_SelectedIndexChanged(object sender, EventArgs e)
{
	if (!fLoaded)
		return;
	FilterDocuments();
}

private bool FilterDocuments()
{
	ProjectCommentsFilterCollection projectCommentsFilterCollection = new ProjectCommentsFilterCollection();
	foreach(ObjectModel.ProjectComment p in Application.ProjectComments)
		projectCommentsFilterCollection.Add(p);
	
	ArrayList CurrentDocuments = new ArrayList();
	foreach (ObjectModel.Document d in Application.Documents)
		CurrentDocuments.Add(d);
	
	ReflectiveFilter rFilter = GetReflectiveFilter();
	
	ACollectionType filteredCollection = new ProjectCommentsFilterCollection();
	filteredCollection = projectCommentsFilterCollection.Filter(rFilter);
	
	if (filteredCollection.Count == 0)
		return false;
	
	// First iterate through filteredCollection, adding Projects not in Documents collection.
	foreach (ObjectModel.ProjectComment p in filteredCollection)
	{
		bool fFound = false;
		foreach (ObjectModel.Document d in CurrentDocuments)
		{
			if (d.ProjectComment.IncidentID == p.IncidentID)
			{
				fFound = true;
				break;
			}
		}
		if (!fFound)
			Application.Documents.Add(new ObjectModel.Document(p.Name, "", p, null));
	}
	// Now that we have added all of our documents, remove non-existant from Docume
	foreach (ObjectModel.Document d in CurrentDocuments)
	{
		bool fFound = false;
		foreach (ObjectModel.ProjectComment p in filteredCollection)
		{
			if (d.ProjectComment.IncidentID == p.IncidentID)
			{
				fFound = true;
				break;
			}
		}
		if (!fFound)
			Application.Documents.Remove(d);
	}

	return true;
}

private ReflectiveFilter GetReflectiveFilter()
{
	ReflectiveFilter rFilter = new ReflectiveFilter(typeof(ObjectModel.ProjectComment),
		typeof(Filter.ProjectCommentsFilterCollection), FilterClause.Equals, FilterOperand.And) ;	

	if ( (this.cboStatus.SelectedIndex != -1) && (cboStatus.SelectedValue.ToString() != ""))
		rFilter.AddFilter("ProjectStatus", cboStatus.SelectedValue.ToString());
	
	if ((this.cboDepartment.SelectedIndex != -1) && (cboDepartment.SelectedValue.ToString() != ""))
		rFilter.AddFilter("DepartmentCode", cboDepartment.SelectedValue.ToString());
	
	if ((this.cboSubDepartment.SelectedIndex != -1) && (cboSubDepartment.SelectedValue.ToString() != ""))
		rFilter.AddFilter("SubDepartmentCode", cboSubDepartment.SelectedValue.ToString() );	

	if ((this.cboBusinessReason.SelectedIndex != -1) && (cboBusinessReason.SelectedValue.ToString() != ""))
		rFilter.AddFilter("BusinessReason", cboBusinessReason.SelectedValue.ToString() );	

	return rFilter;
}


If you are wondering what that application class is, I derived it and my current application from Omar Al Zabir's outstanding MSDN article Implement a Microsoft Word-like Object Model for Your .NET Framework Application[^]


Omar's Code Project Profile[^]

-- modified at 14:30 Wednesday 12th April, 2006
GeneralRe: Thanks! Pin
Richard J Slade12-Apr-06 7:45
Richard J Slade12-Apr-06 7:45 
GeneralRe: Thanks! Pin
kennster12-Apr-06 9:16
kennster12-Apr-06 9:16 
Questionso, reflection...? Pin
toxcct25-Oct-05 6:41
toxcct25-Oct-05 6:41 
AnswerRe: so, reflection...? Pin
Richard J Slade25-Oct-05 7:38
Richard J Slade25-Oct-05 7:38 
GeneralRe: so, reflection...? Pin
Keith Farmer26-Oct-05 10:50
Keith Farmer26-Oct-05 10:50 
GeneralRe: so, reflection...? Pin
Richard J Slade27-Oct-05 1:31
Richard J Slade27-Oct-05 1:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.