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

Local Inversion of Control

Rate me:
Please Sign up or sign in to vote.
4.75/5 (20 votes)
20 Feb 2013CPOL3 min read 23.9K   24   10
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

C#
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:

C#
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:

C#
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.:

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

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

C#
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:

C#
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

C#
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

C#
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:

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

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

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

Now, the above check would turn to

C#
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)


Written By
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 C++, though I'm open to suggestions.

Comments and Discussions

 
GeneralMy Vote of 5 Pin
IndifferentDisdain31-Oct-13 9:03
IndifferentDisdain31-Oct-13 9:03 
QuestionThere are two ideas Pin
Andrej Juhas26-Feb-13 1:44
Andrej Juhas26-Feb-13 1:44 
AnswerRe: There are two ideas Pin
Dmitri Nеstеruk26-Feb-13 2:20
Dmitri Nеstеruk26-Feb-13 2:20 
SuggestionNot Inversion of Control per se Pin
mrchief_200021-Feb-13 9:49
mrchief_200021-Feb-13 9:49 
GeneralRe: Not Inversion of Control per se Pin
Dmitri Nеstеruk21-Feb-13 9:56
Dmitri Nеstеruk21-Feb-13 9:56 
Yes it is. Seriously, in foo.Add(bar) foo 'controls' bar, whereas bar.AddTo(foo) inverts the control flow. I'm not sure what 'traditional sense' is. Inversion of control is not restricted exclusively to IoC containers, which are just one example of application of IoC.
Dmitri Nesteruk Company | Blog | Twitter | MVP C#

GeneralRe: Not Inversion of Control per se Pin
mrchief_200021-Feb-13 10:34
mrchief_200021-Feb-13 10:34 
GeneralMy vote of 5 Pin
Mark Johnston (SSCGP)21-Feb-13 8:21
Mark Johnston (SSCGP)21-Feb-13 8:21 
QuestionMy vote of 5 Pin
ednrg20-Feb-13 10:17
ednrg20-Feb-13 10:17 
GeneralAn interesting idea! Pin
supernorb20-Feb-13 8:53
supernorb20-Feb-13 8:53 
GeneralMy vote of 5 Pin
kerryrarson20-Feb-13 8:24
kerryrarson20-Feb-13 8:24 

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.