James Michael Hare

...hare-brained ideas from the realm of software development...
posts - 137 , comments - 1106 , trackbacks - 0

My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in Seattle, WA. I've been doing C++/C#/Java development for over 18 years, but have definitely learned that there is always more to learn!

All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.

Blogs I Read

MCC Logo MVP Logo

Follow BlkRabbitCoder on Twitter

Tag Cloud

Archives

Post Categories

C#/.NET Little Wonders: The Generic Action Delegates

Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.

Back in one of my three original “Little Wonders” Trilogy of posts, I had listed generic delegates as one of the Little Wonders of .NET.  Later, someone posted a comment saying said that they would love more detail on the generic delegates and their uses, since my original entry just scratched the surface on them.

So over the next few weeks, I’ll be looking at some of the handy generic delegates built into .NET.  For this week, I’ll give a quick overview of delegates and their usefulness.  Then I’ll launch into a look at the Action generic delegates and how they can be used to support more generic, reusable algorithms and classes.

Delegates in a nutshell

Delegates are similar to function pointers in C++ in that they allow you to store a reference to a method.  They can store references to either static or instance methods, and can actually be used to chain several methods together in one delegate.

Delegates are very type-safe and can be satisfied with any standard method, anonymous method, or a lambda expression.  They can also be null as well, so care should be taken to make sure that the delegate is not null before you call it.

So let’s go back to the early days of .NET, before generic delegates, when you typically had to define delegates explicitly.  For example, if we wanted to define a delegate for a logging method, we could define a delegate that takes a string as an argument and returns void:

   1: // This delegate matches any method that takes string, returns nothing
   2: public delegate void Log(string message);

This delegate defines a type named Log that can be used to store references to any method(s) that satisfies its signature (whether instance, static, lambda expression, etc.).

Let’s look at some example code using this delegate type:

   1: // creates a delegate instance named currentLogger defaulted to Console.WriteLine (static method)
   2: Log currentLogger = Console.WriteLine;
   3: currentLogger("Hi Console!");
   4:  
   5: using(var file = File.CreateText("c:\\sourcecode\\out.txt"))
   6: {
   7:     // changes delegate to now refer to the file instance's WriteLine method
   8:     currentLogger = file.WriteLine;
   9:     currentLogger("Hello file!");
  10:  
  11:     // now append Console.WriteLine to delegate (instead of replace, adds)
  12:     currentLogger += Console.WriteLine;
  13:     currentLogger("Hi to both!");
  14: }

Let’s examine what’s going on in the code above:

  • At line 2, we create an instance of a delegate of type Log and have it refer to a static method that takes a string and returns void by assigning it the method name, Console.WriteLine() has an overload that fits this signature.
  • At line 3, we invoke the delegate just like we would any other method, by using the delegate name and passing the arguments in the parenthesis.  If our delegate returned a value, we could assign it like a normal method as well.
  • At line 8, we change the delegate to refer to an instance method, in this case the WriteLine() method of the file variable. 
  • At line 9, we invoke the delegate again, this time it will write to file.
  • At line 12, instead of replacing the delegate, we are appending a method to the delegate.  This means that both Console.WriteLine (from the previous assignment) and file.WriteLine() appended with the += will be called.

So, both = and += can be used to assign method references to a delegate, albeit in different ways.  The = releases any previous method(s) referenced, and assigns a new method reference (or you can assign to null to remove all and leave unassigned).  And the += is used to chain a new method reference to any existing method referenced (if any).  Likewise, you can use –= to remove a method from a delegate chain.

Always remember that on the call to invoke the delegate, you should make sure the delegate reference isn’t currently null.  Depending on the structure of your code, you may or may not have to check at the point you call it, but you should at least be logically sure it has a value (by comparing to null) before you invoke it.

Also remember that delegates can be used to refer to anonymous methods or lambda expressions as well:

   1: // assign to an anonymous method
   2: currentLogger = delegate(string message) { Console.WriteLine(message.ToUpper()); };
   3: currentLogger("This will be upper case.");
   4:  
   5: // assign to a lambda expression
   6: currentLogger = msg => Console.WriteLine(msg.Length);
   7: currentLogger("This will output 19");

Even though you can accomplish the same thing in either anonymous method syntax or lambda expressions, the lambda expression syntax is much more concise and tends to be used the most often.

Lights, Camera, Action delegates!

So, to the real reason we’re here, the Action delegate family!  Delegates give us a lot of power to hold references to methods to execute, but it can be cumbersome to define a new delegate for every situation in which you need one.

As such, .NET introduced the Action family of generic delegates.  This started with Action<T> in .NET 2.0, and then expanded through each version to eventually support actions that take anywhere from 0 to 16 arguments in .NET 4.0.

Currently, the Action family can be used to refer to methods that take 0-16 arguments and return no result.

For example:

  • Actionmatches a method that takes no arguments and returns no value.
  • Action<T> – matches a method that takes an argument of type T and returns no value.
  • Action<T1, T2> – matches a method that takes an argument of type T1, a second argument of type T2, and returns no value.
  • Action<T1, T2, …> – and so on up to 16 arguments and returns no value.

These are very handy because they quickly allow you to be able to specify that a method or class you design will perform an action as long as the method you specify meets the signature.

For example, in our previous code, instead of creating a new delegate type for Log, we can just use Action<string>:

   1: // creates a delegate instance named currentLogger defaulted to Console.WriteLine (static method)
   2: Action<string> currentLogger = Console.WriteLine;
   3: currentLogger("Hi Console!");

This makes it easy to store delegates of nearly any method signature as long as it returns void, and you don’t have to define a new delegate type every time you need one for a class or method.

If there is one downside of using the generic delegates, it’s that you loose a little bit of the implied usage from the name.  For example, Log gives you a pretty good indication that the method you are to specify should log the string, but Action<string> really only tells you that the method you give it must take a string.  In addition, Action<> delegates with the larger number of arguments tend to become less readable in practice.

These things aside, however, really don’t detract from the usefulness of Action delegates when used judiciously.  Their name already implies they are to perform an action on the types they are given, and you can name the variable name in such a way to impart intention of it’s use, such as:

   1: // in this case, the delegate reference name indicates it wants a 
   2: // method to perform in case the work fails on the item.
   3: public void PerformWork<T>(T item, Action<T> failureCallback)
   4: {
   5:    ...
   6: }

Thus, in the method above, it will attempt to PerformWork() on the item, and if that work fails for whatever reason, it will call the failureCallback action you have specified to give you a chance to handle the item for errors.

Action delegates and contra-variance in .NET 4.0

As of .NET 4.0, the Action family of delegates is contra-variant.  To support this, in .NET 4.0 the signatures of the Action<> delegates with generic type parameters changed to:

  • Action<in T> – matches a method that takes an argument of type T (or narrower) and returns no value.
  • Action<in T1, in T2> – matches a method that takes an argument of type T1 (or narrower), a second argument of type T2 (or narrower), and returns no value.
  • Action<in T1, in T2, …> – and so on up to 16 arguments and returns no value.

Notice the addition of the in keyword before each of the generic type placeholders.  This is the new keyword to specify that a generic type can be contra-variant.  What that means is that the method being assigned to this delegate can specify type arguments that are the same type or a less derived type. 

If you get confused as to the meaning of in, think of a typical class hierarchy from a few root types up high and many different child types below, and think of in as going higher up the hierarchy to super-classes and interfaces.

Why allow this? If you think about it, if you are saying you need an action that will accept a string, you can just as easily give it an action that refers to that item as an object.  In other words, if you say “give me an action that will wash dogs”, I could pass you a method that will wash any animal, because all dogs are animals.

For example, in the code below, even though our method takes an Action<string> we can satisfy it by passing an Action<object> instead.  This is because object is less derived than string, thus you can handle any string as an object:

   1: // method which takes a sequence of string and an action to perform on each string
   2: public static void VisitCollection(IEnumerable<string> collection, Action<string> action)
   3: {
   4:     foreach (var item in collection)
   5:     {
   6:         action(item);
   7:     }
   8: }
   9:  
  10: public static void Main()
  11: {
  12:     // instead, we have an action that applies to any object
  13:     Action<object> printObject = o => Console.WriteLine(o.ToString());
  14:     List<string> items = new List<string> { "A", "B", "C" };
  15:  
  16:     // Because a string can be passed to an Action<object> due to the fact
  17:     // that object is less derived than string, the contra-variant action
  18:     // can be passed in .NET 4.0, in previous versions of .NET this didn't compile.
  19:     VisitCollection(items, printObject);
  20: }

Previous versions of .NET implemented some forms of co-variance and contra-variance before, but .NET 4.0 goes one step further and allows you to pass or assign an Action<X> to an Action<Y> as long as X is the same (or a less derived) super-type of Y.

Sidebar: Action vs. Inheritance

The nice thing about the Action family (and really all delegates in general) is that it makes it easy to define reusable logic without having to override a class every time to specify user actions.  For example, if we wanted to create a Consumer class that will read items from a queue until stopped, but want to allow the user of the class to specify how each item is consumed, we could do this by making the class abstract and making the user override an abstract method:

   1: public abstract class Consumer<T>
   2: {
   3:     private BlockingCollection<T> _collection = new BlockingCollection<T>();
   4:  
   5:     // the method to override to consume a single item
   6:     protected abstract void Consume(T item);
   7:  
   8:     // Assume this is launched by a task or thread somewhere else in this class...
   9:     // (rest omitted for brevity)
  10:     private void ConsumptionLoop()
  11:     {
  12:         while (!_collection.IsCompleted)
  13:         {
  14:             T item;
  15:             if (_collection.TryTake(out item, TimeSpan.FromSeconds(1.0)))
  16:             {
  17:                 // call the override to consume the item
  18:                 Consume(item);
  19:             }
  20:         }
  21:     }
  22: }

The problem with this concept is that every time we want to define a new Consumer, we would need to inherit from the Consumer and override the Consume() method, this can quickly lead to a lot of clutter classes that really only are specifying an action to take and don’t really alter the behavior of the class itself.

   1: // This is a lot of work just to specify that we want each line sent to Console.WriteLine()...
   2: public class ConsoleConsumer : Consumer<string>
   3: {
   4:     protected override void Consume(string item)
   5:     {
   6:         Console.WriteLine(item);
   7:     }
   8: }

When you have a method or class that is generic in such a way that the mechanism that the action a user wants to perform is different from what the class or method itself does, this is a prime case for using a delegate.  For example, the Consumer<T> class defines how to process a collection and hand off elements to be consumed, but it let’s the user define what action to take on Consume().

So, we could make this class much easier to use by just allowing the user to give it an Action<T> to perform on the item to consume:

   1: public sealed class Consumer<T>
   2: {
   3:     private BlockingCollection<T> _collection = new BlockingCollection<T>();
   4:     private Action<T> _consumeItem;
   5:  
   6:     // pass in the delegate on construction
   7:     public Consumer(Action<T> consumeItem)
   8:     {
   9:         if (consumeItem == null) throw new ArgumentNullException("consumeItem");
  10:  
  11:         _consumeItem = consumeItem;
  12:     }
  13:  
  14:     // Assume this is launched by a task or thread somewhere else in this class...
  15:     // (rest omitted for brevity)
  16:     private void ConsumptionLoop()
  17:     {
  18:         while (!_collection.IsCompleted)
  19:         {
  20:             T item;
  21:             if (_collection.TryTake(out item, TimeSpan.FromSeconds(1.0)))
  22:             {
  23:                 // call the override to consume the item
  24:                 _consumeItem(item);
  25:             }
  26:         }
  27:     }
  28: }

Now, there’s nothing to override!  This leads to safer and somewhat more efficient code because I can seal the class and keep anyone from overriding its core behavior.  Plus, it’s now very flexible in that I can easily create many different types of Consumer just by constructing them with different Action<T> arguments!

   1: // consumer that writes each item to console
   2: var consoleConsumer = new Consumer<string>(Console.WriteLine);
   3:  
   4: // consumer that writes length of each item to console
   5: var lengthConsumer = new Consumer<string>(item => Console.WriteLine(item.Length));
   6:  
   7: // consume that writes each item in upper case to console.
   8: var upperCaseConsumer = new Consumer<string>(item => Console.WriteLine(item.ToUpper()));

These are just simplistic examples, but you get the point.  This gives us the flexibility as users to define consumption any way we want as long as we provide an Action<string>.  No classes to override!

Summary

Generic delegates give us a lot of power to make truly generic algorithms and classes.  The Action family of delegates is a great way to be able to specify actions to be performed that can take from 0-16 arguments and return no value.  Stay tuned in the weeks that follow for other generic delegates in the .NET Framework!

Technorati Tags: , , , , , ,

Print | posted on Thursday, November 3, 2011 8:14 PM | Filed Under [ My Blog C# Software .NET Little Wonders ]

Feedback

Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

I use the Action<> (and, in a similar vein, Func<>) rather frequently myself for basic callbacks and similar requirements. I find that for more "intensive" callbacks- such as those that make the backbone of a class or method, it's often better to create a delegate with descriptive names for the delegate itself as well as it's parameters. Another advantage of using your own delegates is you can document them more thoroughly with doc comments. I guess it depends if you need it for just this one method or class for a single purpose (in which case Action<> or Func<> would be ideal) or if you want to or intend to use the delegate in several locations in your project, in which case it might be a better practice to create a delegate to allow for more thorough documentation of it's intent, both for the caller within your own code as well as the client implementing the callback.
11/3/2011 9:40 PM | BC_Programmer
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

@BC_Programmer:

My general rule of thumb is if the delegate really needs documentation to indicate proper usage, then yes a custom delegate may be in order. 95% of the time though I find the generic delegates meet my needs especially for zero, one, or two arg actions.

It can be a matter of style and taste, of course. If nothing else they are good to know because Linq and TPL use them heavily.
11/3/2011 10:59 PM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

Since I’ve started using Action<> and Func<> I have noticed I’ve written code in c# that is more like Javascript. Similar to the consumer example given, I’ll define call backs and other similar functionality. I use events a lot less, and find I need to create fewer instances of classes… I’m not sure if I like where I’m heading from a maintainability standpoint. But for now, it feels right to call a method that takes a callback parameter to alert that the method has finished. For example. If I have a little ship in a game and I need to tell the ship to move to such and such a location and then turn, and then fire a gun when it’s done turning. Ship.Move(1,1,()=>Ship.Turn(90,()=>Ship.Fire()); Instead of hooking up temporary events for all that. And with a bit more work, and still using the Action<> and Func<> objects you can do this: Ship.Move(1,1).done(()=>Ship.Turn(90)).done(()=>Ship.Fire()); And that pattern is pulled from JQuerys Deferred Objects. Like I said above, I’m still torn on if this is a good idea. Perhaps a year from now when I look back at my code, and see I was doing this sort of thing, I’ll kick myself 
11/4/2011 8:48 AM | Adam
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

@Adam: It is definitely a paradigm shift for folks more traditional OO programming where this type of thing was typically handled with inheritance. It takes a bit of getting used to, but when coupled with lambda expressions it gives a LOT of power to write very generic functional code in a way that's concise and easy to read.

Granted, there is the hurdle of getting over lambda expression syntax, but once you learn it, it's very easy read and apply. Ever since I made the leap to using more delegates and less inheritance, I feel like my classes are more robust and easier to use, but it does take a while to fully make the shift :-)

It's really the generic delegates and lambda expressions (coupled with extension methods of course) that give LINQ so much power.
11/4/2011 9:09 AM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

If you do want to give a name to your generic delegate you could use an alias. I use that a lot if I use tuples or other generic stuff.

using Log = System.Action<string>;

namespace ...

//Log refers to Action<string> but has a more descriptive name.
Log currentLogger = Console.WriteLine;
currentLogger("Hi Console!");
11/4/2011 4:17 PM | Roy
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

I'm not sure if your contra-variance example is correct or at least complete.

The sample provided compiles happily (for me anyway) in versions < .NET 4.0. In fact 3.5 is required due to the use of LINQ. However, removing the use of .Count() in ProcessList() means it will compile all the way down to .NET 2.0.

As such I think all the sample demonstrates is actually simple inheritance in that a delegate defined to take an argument to a derived class can be assigned a method which takes an argument to the base class of that argument. In this case the ProcessList takes an IEnumerable<string> which is a base of List<string>.

In order to demonstrate contra-variance the example needs extending so that an additional delegated is defined, i.e. Action<IEnumerable<string>> handleBaseStuff = ProcessList. There is nothing special here as the delegate's argument type is the same as that of ProcessList.

The contra-variance is demonstrated by assigning handleBaseStuff to handleStuff, i.e. handleStuff = handleBaseStuff. Here handleStuff is an instance of a delegate that expects an argument of type List<string> but it's been assigned an instance of a delegate that takes an argument of IEnumerable<string>.

Action<List<string>> handleStuff = ProcessList;

var myList = new List<string> { "A", "B", "C" };

handleStuff(myList);

Action<IEnumerable<string>> handleBaseStuff = ProcessList;
handleBaseStuff(myList);

// This is contra-variance in action
handleStuff = handleBaseStuff;
11/8/2011 7:18 AM | Pete Barber
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

@Pete: You're right in that I inadvertently demonstrated behavior previously available in other versions of .NET :-) I'll clean up my example.

This is a really good post on the different types of .NET support from 1.0 to 4.0 which covers the different levels of co/contra-variant support and at what framework versions they became available.

http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
11/8/2011 9:12 AM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

@Pete: Updated my code example with one more indicative of .NET 4.0 contra-variance enhancements to generic type parameters.
11/8/2011 2:15 PM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

@James: Excellent. The updated sample demonstrates contra-variance now. Demonstrating it via delegates is fun given the various levels of indirection, i.e. use of the delegate itself and then having to use (invoke) the delegate on the contra-variant method (well the other way round:-))
11/9/2011 8:35 AM | Pete Barber
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

"Now, there’s nothing to override! "

It seems there s nothing to override because u inject the dependancy using the constructor.

public Consumer(Action<T> consumeItem)

{...}

I don t get why u could not do the same thing with a delegate.
11/9/2011 8:36 AM | alf
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

@Alf: Yes, that's the point. The class is now `sealed` and we don't have to worry about a developer changing the core behaviors in ways we don't anticipate.

This did do the same thing as a delegate, it's just a generic delegate instead of creating a new custom delegate for the job. This is handy because everyone should be pretty certain what an Action<string> takes/returns from the signature (once you learn about them of course) and once you start using LINQ enough, they are second nature.

It's much better to just use Action<T> for classes than need an Action to be performed on items of type T then to create a new delegate for each class that needs a delegate since it's meaning is well-known and it is used throughout the .NET Framework (LINQ, TPL, etc).
11/9/2011 9:29 AM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: The Generic Action Delegates

@Pete: Thanks and thanks for the catch on my original example! Corrections and constructive contributions are always welcome! I'd be the last to claim I knew everything :-)
11/9/2011 9:30 AM | James Michael Hare
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: