Skip to contentSkip to author details

.Net - Creating a generic extension of "Contains" to see if there's something in a generic collection

 .NET  C#  DTO  Extension Methods  featured  Generics

Update: Added reference links at bottom of entry.

I was working with some code today and needed to see if a list of custom DTOs contained an object based on a string value I had. Here is my solution.

Some background…

Our Architecture uses a base abstract class called DtoBase:

public abstract class DtoBase { public string Id { get; set; } public string Description { get; set; } public DtoBase() { } public string ToJson() { StringWriter w = new StringWriter(); JsonSerializer s = new JsonSerializer(); s.Serialize(new JsonTextWriter(w), this); return w.ToString(); } }

All classes that derive from DtoBase have their own properties, but they all benefit from Json serialization using the ToJson method. This helps us in various ways in the application.

I am working with a generic list of a subclass of DtoBase (AccessoryGroup):

public class AccessoryGroup : DtoBase { public bool QCFlag { get; set; } }

With this list, I wanted to see if it contained a DTO with an ID based on a given string. Unfortunately, the built-in Contains extension method on the list (in the System.Linq namespace) does not allow this behavior. It would require me to create an instance of AccessoryGroup containing the values I want to compare (and implement the IEquatable interface on DtoBase. No big deal, but another requirement). What I needed was a version of the Contains extension method that let me pass in a string.

So, my first attempt was to create my own extension method using List as the type to extend:

public static bool Contains(this List list, string val) { foreach(DtoBase dto in list) { if dto.Id val.Trim() { return true; } } return false; }

Aside from the use of the foreach (which I refactored in the final code), this code did not work.

Here is some test code:

List accessoryGroupList = new List(); accessoryGroupList.Add(new AccessoryGroup { Id = "test", Description = "This is just test data" }); if(accessoryGroupList.Contains("test")) { // this should be true }

However, this code did not work. In fact, it didn’t even build. I got invalid arguments exceptions with my Contains call.

What to do? It then occurred to me that I’m working with a generic list, so I need to take a generic approach to this. Here’s the final code taht implements my new extension method as a generic method:

public static bool Contains(this List list, string val) where TDto : DtoBase { return list.Any(a => a.Id.Trim() val.Trim()); }

Not only have I refactored to remove the ugly foreach loop, but I’ve converted the method to a generic method.

I thought I was going to have to write my code like this:

List accessoryGroupList = new List(); accessoryGroupList.Add(new AccessoryGroup { Id = "test", Description = "This is just test data" }); if(accessoryGroupList.Contains("test")) { // this should be true }

But I did not. It seems that because the accessoryGroupList is already a generic list of AccessoryGroup DTOs, it automatically knows that the Contains method will use the same type. Nice, huh?

So, my original code worked:

List accessoryGroupList = new List(); accessoryGroupList.Add(new AccessoryGroup { Id = "test", Description = "This is just test data" }); if(accessoryGroupList.Contains("test")) { // this is true }

As you can see, using generics and extension methods has saved the day again. There is a lot of generic code in our architecture using the DtoBase class. This is just another example of why generics and extensions are so important.

Our server repositories that feed our REST API use the same DTOs that our portable class library containing our client libraries uses. Because of the shared class library, I can use this new extension method on the server or on the client.

References:

MSDN Extension methods guide
List.Contains() method