June 18th, 2023

Every project gets these C# extensions

I have been developing software in .NET since 2017, primarily using C#. One of my favorite things to use are extension methods, which are static methods that masquerade as instance methods on other types. To me they are most useful for limiting boilerplate code. Depending on how you use them, they can let you write fluent-style code so you can pretend your code is highly sophisticated. Over time I have noticed that whenever I start a new personal project in dotnet, I add the same standard set of extension methods. I will showcase some of them below. I used this site to convert the C# code to HTML. You can identify an extension method in C# by the use of keyword this before the first parameter. If you've never made one before or just forgot, remember that they must be placed inside a static class.

                        public static class Extensions
                        {
                            // place methods here
                        }
                    

Each

In simple terms, for a simple method - it replaces foreach blocks. We used something very similar[1]The difference is that it included support for some custom enumerators. Also, it had a slightly longer name - you can probably guess it. at my second job, in all corners of the codebase.

                        public static void Each<T>(this IEnumerable<T> sequence, Action<T> action) 
                        {
                            foreach (var item in sequence)
                                action(item);
                        }
                    

Now you may be wondering - why? Well, have you ever started a foreach but still had to filter the sequence passed into it? How are you supposed to indent all that stuff? Every developer I have spoken to has a different opinion on it. There is no way to make it look good:

                        foreach (var item in sequence
                            .Where(x => someConditions(x)
                                && someOtherCondition(x))
                        {
                            entity.State = EntityState.Deleted;
                        }
                    

Enter fluent-style method chains. No more debate on how to indent everything, no more separate lines for each curly brace taking up all your screen. Instead, we can do stuff like this:

                        entities
                            .Where(entity => someCondition(entity))
                            .Where(entity => someOtherCondition(entity))
                            .Each(entity => entity.State = EntityState.Deleted);
                    

Now that's just groovy. Any downsides? Well, if your action is not a single statement but a block, then you won't be able to use return to break out of the full method, it will only break out of the action. Compare the following methods to see the difference:

                        void UseExtension(IEnumerable<object> sequence)
                        {
                            sequence.Each(item =>
                            {
                                return;
                            }
                            // this line will be reached
                        }
                    
                        void UseForeach(IEnumerable<object> sequence)
                        {
                            foreach (var item in sequence)
                            {
                                return;
                            }
                            // this line will never be reached (assuming sequence has at least one element)
                        }
                    

Yield

Sometimes you need to go from a single element to a sequence. Unfortunately you cannot use the yield keyword except when inside a method that returns IEnumerable. I have seen developers create an array and put the element inside (and probably make an extension method that does it). Seems a little overkill if all you need is an enumerable.

                        public static IEnumerable<T> Yield<T>(this T instance)
                        {
                            yield return instance;
                        }
                    

A good example of when this is useful is when you have an early exit out of a method that is supposed to return a sequence, but the early return value is singular.

                        public IEnumerable<T> SomeMethod<T>(object[] bunchOfArguments)
                        {
                            if (someEarlyExitCondition())
                                return someDefaultValue.Yield();
                            
                            ...
                        }
                    

GetOrSetDefault

The most difficult part of software development is naming things. I cannot tell you how many people have independently come up with this method, but I can tell you that every single one of them gave it a different name. Let's get to it. When you have a dictionary, you may run into the situation where you want to access a value by key, and if it does not exist yet, then a specific value should be set.

                        public static TValue GetOrSetDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary,
                            TKey key, Func<TValue> defaultValueFunc)
                        {
                            if (dictionary.TryGetValue(key, out var value)
                                return value;

                            var defaultValue = defaultValueFunc();
                            dictionary[key] = defaultValue;
                            return defaultValue;
                        }
                    

A common use case for this is initializing objects in a dictionary when you cannot (easily) determine all possible keys in advance.

                        var map = new Dictionary<string, Entity>();
                        while (someCondition)
                        {
                            var entity = map.GetOrSetDefault(someKey, () => new Entity());
                            ...
                        }
                    

IsNullOrEmpty

This method gets used all the time if you work a lot with strings. The string class has this method as a static method, but using it requires the tedious writing of string everywhere. Moving it to an extension method gets rid of the boilerplate.

                        public static bool IsNullOrEmpty(this string value)
                        {
                            return string.IsNullOrEmpty(value);
                        }
                    

Let's look at the usage:

                        string s1 = "";
                        string s2 = null;
                    
                        return s1.IsNullOrEmpty() && s2.IsNullOrEmpty();
                    

Here you can see the beauty (or to some people, the evil) of extension methods. Look at s2 - from the outside, it looks like you are calling an instance method on null. However, this is actually just semantic sugar for a static method call where the null value is passed in. This behavior lets you do a lot of cool things, but it can also make the code more difficult to understand if it is not properly marked. Modern code editors will use a different color for the syntax highlighting of static methods and instance methods, but people stubbornly sticking to plain old Notepad will be left behind.

IsDigit and others

Taking the idea of IsNullOrEmpty further, a lot of old static methods can (and if you ask me, should) be moved to extension methods. I don't use them often enough to warrant doing it for all my projects, but if I ever make a general purpose Extensions project as a package, these will go there. See for example some static char methods:

                        public static bool IsDigit(this char c) =>
                            char.IsDigit(c);
                
                        public static char ToUpper(this char c) =>
                            char.ToUpper(c);
                
                        public static char ToLower(this char c) =>
                            char.ToLower(c);
                
                        public static double GetNumericValue(this char c) =>
                            char.GetNumericValue(c);
                    

Into

Saving the best for last, or the worst? This extension can go off the rails very quickly. It gives you ultimate flexibility, letting you turn anything into a fluent chain of methods. It also can make your code very hard to debug. Buyer beware.

                        public static TNext Into<TFrom, TNext>(this TFrom source, Func<TFrom, TNext> func)
                        {
                            return func(source);
                        }
                    

The possibilities are endless. See this example:

                        static string GetPropertyName<TSource, TProp>(Expression<Func<TSource, TProp>> sourceExpression)
                        {
                            sourceExpression
                                .Into(expression => expression.Body as MemberExpression)
                                .Into(memberExpression => memberExpression.Member as MemberInfo)
                                .Into(memberInfo => memberInfo.Name);
                        }
                    
                        var propertyName = GetPropertyName<string, int>(s => s.Length);
                    

In the majority of cases where I use this I am just prototyping. It lets me write something that is quick and dirty without needing to open up blocks. Once I finish whatever component I am working on, I have either cleaned it up or abstracted away enough parts that I no longer need to use Into. However, I have one use case where this method is used in a very elegant and fully intended way. I plan to show that off in a later blog.