Wednesday, 15 April 2009

Action<T> and Func<TResult> delegates

In this post I would like to write a little bit about two types of delegates which were added recently with ASP.NET 3.5 (System Namespace):

Action<T> - "Encapsulates a method that takes a single parameter and does not return a value". There are additional versions for two, three and four parameters:
It can be used with method like this:

   1:  public static void UpdatePage(PageData page, Action<PageData> action)
   2:  {
   3:      var writableClone = page.CreateWritableClone();
   4:      action(writableClone);
   5:      DataFactory.Instance.Save(writableClone, SaveAction.Publish);
   6:  }

To updated page in EPiServer it's required to create writable clone first, then we can apply our changes and finally instance of PageData has to be saved. This is a procedure which you can probably find in many places in your projects. With delegates you can easily "close" standard steps into a single method and pass changeable part as a parameter, like this:

   1:  PresenterUtils.UpdatePage(CurrentPage,
   2:                            p =>
   3:                                {
   4:                                    p.Property["MetaAuthor"].Value = "Marek";
   5:                                    p.Property["MainBody"].Value = "Hello World!";
   6:                                });

I think it should be pretty obvious that the above piece of code changes MetaAuthor and MainBody properties on CurrentPage. For me, main benefit of this approach is increased readability, hence the code is easier to maintain. Note that there is nothing in this call about creating writable clone, calling save method on DataFactory, those are all important and required steps to get things working properly but in the same time they are irrelevant from businesses logic point of view. You don't need to see those "standard" actions to understand what your class/method is doing.

You can also use delegates which return a value:
The simples version Func<TResult> - "encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter."

In this example, method returns default value if property doesn't exist or is not set. If property is set then its value can be formatted in some special way:

   1:  public static string GetProperty<T>(PageData page, string propertyName, string defaultValue, Func<T, string> customAction)
   2:  {
   3:      if (page == null || string.IsNullOrEmpty(propertyName) || page[propertyName] == null)
   4:      {
   5:          return defaultValue;
   6:      }
   7:  
   8:      return customAction((T) page[propertyName]);
   9:  }

And this is how this method can be used:

   1:  public string GetFormattedDate(PageData page, string format)
   2:  {
   3:      return PresenterUtils.GetProperty<DateTime>(page, "EventDate", "", x => x.ToString(format));
   4:  }

In the above example we are interested in EventDate property, if this property is not set then empty string (default value) will be returned, otherwise DateTime will be formatted appropriately.

Of course, in pre-ASP.NET 3.5 era things like this were also possible but it was required to define your own delegates etc. Now you can use those which are available for you in the framework. Personally I really like lambda expressions and this "way" of crating APIs, it's much more readable for me therefore I encourage you to give it a try! :)

1 comment:

Yaghiyah said...

Thanks you so much i now understand how to apply delegates and "Action" in my code !