Now, we move on to where extension methods are used the most, enumerated types. In an article by Eric Lippert, a principal developer on the C# team; LINQ was the reason extension methods were created in the first place. In this recipe, we will look at writing our own extension methods for the IEnumerable
and IList
type and take a look at an existing extension method in the .NET framework from the LINQ namespace.
Refer to the IEnumerableExtensions.cs
and IListExtensions.cs
file in the ExtensionMethods.Library
project for the extension methods. These methods are used in the IEnumerableExtensionTests.cs
file in the ExtensionMethods.Tests
project.
The following code snippet shows the extension method Count()
from the LINQ namespace:
public static int Count<TSource>(this IEnumerable<TSource> source) { if (source == null) { throw Error.ArgumentNull("source"); } ICollection<TSource> is2 = source as ICollection<TSource>; if (is2 != null) { return is2.Count; } int num = 0; using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { num++; } } return num; }
The following code is our custom code snippet of an extension method for using bubble sort on an IList:
/// <summary> /// Sort list using bubble sort algorithm /// </summary> /// <typeparam name="T"></typeparam> /// <param name="list"></param> public static void BubbleSort<T>(this IList<T> list) where T : IComparable { BubbleSort(list, 0, list.Count - 1); } /// <summary> /// Sort list using bubble sort algorithm /// </summary> /// <typeparam name="T"></typeparam> /// <param name="list"></param> /// <param name="startIndex">starting index</param> /// <param name="endIndex">end index</param> public static void BubbleSort<T>(this IList<T> list, int startIndex, int endIndex) where T : IComparable { //Bubble Sort for (int i = startIndex; i < endIndex; i++) for (int j = endIndex; j > i; j--) if (list[j].IsLesserThan(list[j - 1])) list.Exchange(j, j - 1); } /// <summary> /// Swap values of list by index /// </summary> /// <typeparam name="T"></typeparam> /// <param name="list"></param> /// <param name="index1">1st index to swap with 2nd index</param> /// <param name="index2">2nd index to swap with 1st index</param> private static void Exchange<T>(this IList<T> list, int index1, int index2) { T tmp = list[index1]; list[index1] = list[index2]; list[index2] = tmp; } private static bool IsLesserThan(this IComparable value, IComparable item) { return value.CompareTo(item) < 0; }
The following code snippet is our custom extension method for counting a sorted IEnumerable:
public static class IEnumerableExtensions { public static int CountSorted<T>(this IEnumerable<T> values, T item) where T : IComparable { //convert to list to operate on it better. var list = values.ToList(); //find random index of the item int index = list.BinarySearch(item); //if item isn't found, just return -1 if (index < 0) return -1; int leftEdge = findLeftEdge(list, 0, index, item, Comparer<T>.Default); int rightEdge = findRightEdge(list, index, list.Count - 1, item, Comparer<T>.Default); //count it by taking away the left from the right return rightEdge - leftEdge + 1; }}
The following code snippets show how to implement these extension methods:
[TestMethod] public void BubbleSortTest() { IList<int> list = new List<int> { 3, 2, 1 }; list.BubbleSort(); Assert.IsTrue(new List<int>{1,2,3}.SequenceEqual(list)); } [TestMethod] public void BubbleSort_And_Count_Test() { IList<int> list = new List<int> { 3,2,1,2 }; int count = list.BubbleSort().CountSorted(2); Assert.AreEqual(2,count); }
The first code snippet of Count()
was taken from the .NET framework System.Linq
namespace using a decompile tool. The System.Linq
namespace is filled with extension methods for enumerated types such as IEnumerable
. This is an example that demonstrates that even the owners of classes can create extension methods for the sake of maintainability of the projects.
Our second code snippet is BubbleSort
for the IList
interface type. The algorithm operates on the instance object and sorts it using the bubble sort algorithm. This extension method has rules that govern what T
should be, and should be inherited from the IComparable
type or any type that can be compared, so that the algorithm can know which items are greater and smaller.
Our third code snippet CountSorted
is used on sorted enumerable types using a modified binary search for an optimized count. This public method also calls private static functions which follows proper object-oriented guidelines.
When working with enumerated types, there are a few key points to keep in mind:
Inheritance: In the
BubbleSort_And_Count_Test
method, we chained theCountSorted
extension method (extendingIEnumerable
) on anIList
type. This is possible asIList
inherits fromIEnumerable
, whatever we can do onIEnumerable
, we can also do onIList
.int count = list.BubbleSort().CountSorted(2);
Manipulation: The
BubbleSort
extension method worked directly on the list it was operating on. As with classes, the list is passed by reference and can be manipulated. In theBubbleSortTest
method, there was no need to assign it back to itself after calling the method.IList<int> list = new List<int> { 3, 2, 1 }; list.BubbleSort(); //list is now sorted as {1,2,3}
Private extensions: In the
IEnumerableExtensions
class, we utilized both public and private extension methods so our class file can be properly structured. Both work just the same with the difference of public and private access.private static void Exchange<T>(this IList<T> list, int index1, int index2)
You will find that most of the extension methods you write will be operating on enumerated types.