Click here to Skip to main content
Click here to Skip to main content

Local Inversion of Control

, 20 Feb 2013
Rate this:
Please Sign up or sign in to vote.
Kind of like inversion of control, but without DI containers

The term ‘Inversion of Control’ is typically used in relation to DI/IoC frameworks. However, that same idea works at the micro scale too. I for one encounter a lot of what I could call Local Inversion of Control (LIoC) all the time, and this is what this short post is all about.

Examples

Let’s start with the simplest example. Say you want to add an item to the collection: this is typically written as

myCollection.Add(x)

If you pause for a moment and think about the above statement and voice it in English, what you’ll get is myCollection should add to itself item x which is very much unreadable. This is precisely the case where IoC is useful, and here’s how: let us define an AddTo() extension method:

public static T AddTo<T>(this T self, ICollection<T> collection)
{
  collection.Add(self);
  return self;
}

Now, instead of calling the Add() method on the collection, control is inverted, so the AddTo() method is called on the collection:

var aList = new List<int>();
2.AddTo(aList);

Translated to English, the above now reads 2 should be added to aList. Makes more sense, doesn’t it? Also, as an added bonus, the AddTo() operation is fluent because it returns the original argument. This means that a value can be added to more than one list at once, e.g.:

2.AddTo(someList).AddTo(someOtherList);

Let’s try something else. Suppose you want to check that a collection is empty. Typically, you’d write

if (myClass.Fields.Count == 0) { ... }
// or
if (!myClass.Fields.Any()) { ... }

Both of these approaches are okay, I guess, but the code reads strange. The first example says if myClass’s fields count is equal to zero whereas the second reads if not myClass’s fields are any in number, which is even worse.

So here’s a pair of extension methods:

public static bool HasSome<TSubject, T>(this TSubject subject, 
  Func<TSubject, IEnumerable<T>> propertyToCheck)
{
  return propertyToCheck(subject).Any();
}
public static bool HasNo<TSubject, T>(this TSubject subject, 
  Func<TSubject, IEnumerable<T>> propertyToCheck)
{
  return !HasSome(subject, propertyToCheck);
}

Now, a check for the presence of fields turns into

if (myClass.HasNo(c => c.Fields)) {}

which reads as myClass has no fields, which is precisely what we’re checking here.

Okay, one more example. Say you’re checking a string value, and you want to compare it with a set of values. This is typically represented as

if (myOp == "AND" || myOp == "OR" || myOp == "XOR") { ... }

This reads in a kind of OK, but fragmented, fashion. A much better way would be to group the variants into an array but, in C#, this just looks ugly:

if (new[]{"AND", "OR", "XOR"}.Contains(myOp)) { ... }

This is even less semantic. What if there was an IsOneOf() function instead?

public static bool IsOneOf<T>(this T self, params T[] variants)
{
  return variants.Contains(self);
}

Now, the above check would turn to

if (myOp.IsOneOf("AND", "OR", "XOR")) { ... }

The great thing about the above approach is that the arguments are declared as params T[], so we don’t have to initialize any arrays with new[] before using them as a test.

Summary

So here’s the basic premise of LIoC: given a function f(x,y...), it may in certain cases be more benefitial from the semantic and usability perspectives to instead define an extension method on x that applies the function f using the argument(s) y.

Additional benefits are provided by the fact that:

  • Extension methods may take generic arguments, thus letting you define inverted operations on groups of similar objects.

  • The params keyword lets your API behave in a ‘variadic’ fashion by unchaining you from explicit collection initialization.

  • Additional level of expressiveness can be gained from passing in lambdas or expressions.

The disadvantages of this approach are:

  • Having to manage separate classes containing extension methods.

  • API pollution — any time you define an extension method on a generic type, all objects get an additional IntelliSense popup member.

  • Possible performance overhead — for example, in cases where you use a lambda instead of direct member access.

I use LIoC all over the place, just like I use the Maybe monad. It makes the code a lot more readable, maintainable and amenable to refactoring. ▪

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dmitri Nеstеruk
Founder ActiveMesa
United Kingdom United Kingdom
I work primarily with the .NET technology stack, and specialize in accelerated code production via code generation (static or dynamic), aspect-oriented programming, MDA, domain-specific languages and anything else that gets products out the door faster. My languages of choice are C# and F#, though I'm open to suggestions.
 
I'm a Microsoft MVP (Visual C#) since 2009. I run a collective tech blog at DevTalk.net. I use my own editor called TypograFix to typeset articles and blog posts.
 
Like the article and want this implemented in your product? Got a project that can benefit from Microsoft.Net goodness? Then get in touch!
Follow on   Twitter

Comments and Discussions

 
GeneralMy Vote of 5 PinmemberIndifferentDisdain31-Oct-13 8:03 
QuestionThere are two ideas PinmemberAndrej Juhas26-Feb-13 0:44 
AnswerRe: There are two ideas PinmemberDmitri Nеstеruk26-Feb-13 1:20 
SuggestionNot Inversion of Control per se Pinmembermrchief_200021-Feb-13 8:49 
GeneralRe: Not Inversion of Control per se PinmemberDmitri Nеstеruk21-Feb-13 8:56 
GeneralRe: Not Inversion of Control per se Pinmembermrchief_200021-Feb-13 9:34 
GeneralMy vote of 5 PinmemberMark Johnston (SSCGP)21-Feb-13 7:21 
QuestionMy vote of 5 Pinmemberednrg20-Feb-13 9:17 
GeneralAn interesting idea! Pinmembersupernorb20-Feb-13 7:53 
GeneralMy vote of 5 Pinmemberkerryrarson20-Feb-13 7:24 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 20 Feb 2013
Article Copyright 2013 by Dmitri Nеstеruk
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid