CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Karl Seguin

.NET From Ottawa, Ontario - http://twitter.com/karlseguin/

Get Your Func On

I've noticed that I have a 2 step pattern for learning new framework or language features. I'm guessing this is pretty typical for most people. First, I'll use the feature within framework classes or 3rd party ddls. Then I'll leverage it more directly within my own code. What's surprising to me is the length of time which occurs between step 1 and step 2.

Take generics for example. Back in the 1.x days, I wrote a ton of repetitive classes that inherited from CollectionBase. So when 2.0 came out, I immediately and aggressively started to use generic collections. However, it was quite some time later (a year?) until I wrote my own class that leveraged them directly. Today, I don't write a new generic class every day, but I do consider them an important part of my toolbox and kinda wonder what took me so long to take them up.

I have a feeling that many developers are in the same boat - it's easy to consume code that implements new features, but not so easy to grasp how to implement those same features ourselves.

As it turns out, the other day, I had another such ah-hah moment with the System.Func generic delegate. Like me, you've probably consumed it often, or at least one of its cousins: System.Action and System.Predicate. I thought I'd show how I used it, in hopes that it might open up some possibilities for you.

Ovewview

First though, a brief overview. The three delegates above are essentially shortcuts that save you from having to write your own common delegate. The most common one is probably Predicate<T>, which returns a boolean. Predicte<T> is used extensively by the List<T> and Array classes. The most obvious is the Exist method:

List<string> roles = user.Roles;
if (roles.Exists(delegate(string r) { return r == "admin";}))
{
   //do something
}

or the lambda version (which I much prefer)

if (role.Exists(r => r == "admin))
{
}

Func<T> is a lot like Predicate, but instead of returning a boolean it returns T. Also, Func<T> has multiple overloads that let you pass 0 to 4 input parameters into the delegate. Action<T> is like Func<T> except it doesn't return anything - it does an action.

Code Decoupling

So, how can you make use of these within your own code? Well, here's what I did. First, I'm a big proponent of caching, as well as a big fan of unit testing. However, the two don't easily go hand-in-hand because Microsoft doesn't provide an interface to their built-in cache, which leads to tight coupling (which of course makes it difficult to change caching implementation down the road, and impossible to unit test). The first thing to do is create your own interface, a simple start might look like:

public interface ICacheManager
{
   T Get<T>(string key);
   void Insert(string key, object value);
}

Next comes our first implementation:

public class InMemoryCacheManager : ICacheManager
{
    public T Get<T>(string key)
    {
        return (T) HttpRuntime.Cache.Get(key);
    }
    public void Insert(string key, object value)
    {
         HttpRuntime.Cache.Insert(key, value);
    }
}

Func Fights Repitition

So, what does all this have to do with System.Func? Well, the above code is used in a very repetitive manner: get the value from the cache, if it's null, load it from somewhere and put it back in the cache. For example:

public User GetUserFromId(int userId)
{
    ICacheManager cache = CacheFactory.GetInstance;
    string cacheKey = string.Format("User.by_id.{0}", userId);
    User user = cache.Get(cacheKey);
    if (user == null)
    {
       user = _dataStore.GetUserFromId(userId);
       cache.Insert(cacheKey, user);
    }
   return user;
}

After a year or so of writing code like this, I figured there must be a better way, which of course is where Func comes in. Ideally, we'd like to get the value, and provide our callback code all at once. So, let's change our interface:

public interface ICacheManager
{
   T Get<T>(string key, Func<T> callback);
   void Insert(string key, object value);
}

The second parameter is the delegate we'll want to execute if Get returns null. Of course our delegate will return the same type (T) as Get would - just like in the above case where we expect a User from both Get and our data store. Here's the actual implementation:

public T Get<T> (string key, Func<T> callback)
{
   T item = (T) HttpRuntime.Cache.Get(key);
   if (item == null)
   {
       item = callback();
       Insert(key, item);
   }
   return item;
}

How do we use the above code?

public User GetUserFromId(int userId)
{
   return CacheFactory.GetInstance.Get(string.Format("User.by_id.{0}", userId), 
                                                                () => _dataStore.GetUserFromId(userId));
}

I know the () => syntax might be intimidating (especially if you aren't familiar with lambdas), but all it is is a parameterless delegate.

Of course, this system can easily be expanded to add additional caching instructions (absolute/sliding expiries, dependencies and so on) via overloaded Get<T> and Insert members.

(I just noticed this example also highlights how to use generics within your own code too!)



Comments

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# July 4, 2008 3:08 AM

Peter Morlion said:

Very interesting article, thanks.

# July 4, 2008 3:09 AM

Reflective Perspective - Chris Alcock » The Morning Brew #129 said:

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #129

# July 4, 2008 3:33 AM

Todd said:

Good article.  Just one suggestion.

callback is a horrible parameter name.  It's not descriptive at all and you have to look at the implementation to know what it does.  How about valueIfNull?

# July 4, 2008 5:51 AM

karl said:

Good call Todd.

I think I like Func<T> ifNull or something simpler. I originally named it block, influenced by rails, tried to pick a more NETish name, but I agree callback is still vague..makes it sound like Get is asynchronous or something...

# July 4, 2008 8:28 AM

Sean Chambers said:

Excellent example!

I was looking for good Func<T> descriptions and examples. More! More!

# July 4, 2008 12:42 PM

Bryan Reynolds said:

As always, nice post!

# July 4, 2008 2:27 PM

Dew Drop - July 4, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - July 4, 2008 | Alvin Ashcraft's Morning Dew

# July 4, 2008 5:08 PM

jbland said:

i saw the same pattern in a collaborative filtering library i ported from Java. It had pluggable data backends, so the cache was implemented similarly as you presented. The callback was called fetcher, FWIW

# July 4, 2008 8:09 PM

Andrei Butnaru's blog said:

Programming links 07.05.2008

# July 5, 2008 12:56 PM

Rafael Rosa Fu said:

Hi, very interesting post, as always.

I'd like to thank you for your Foundations of Programming series, which I noticed quite some ago but only had time to read them carefully this weekend, in the e-book format. The topics you picked are very important, and I really like the way you explain them. Even though I'm a seasoned programmer (13+ years) reading all that stuff in a structured way helped me settle some doubts about stuff I've been studying recently, like TDD and ORM. I'll ask my trainee to read it, and hopefully she will learn things I'm not sure I know how to explain (I'm a lousy teacher), even though many of them are easier to understand when we have some experience doing things the old-hard-clumsy way :)

Thanks a lot,

Rafael

São Paulo - Brazil

# July 6, 2008 5:14 PM

Community Blogs said:

Karl Seguin has an interesting post about using System.Func to fight repetitive code blocks , which actually

# July 7, 2008 9:52 AM

Readed By Wrocław NUG members said:

Karl Seguin has an interesting post about using System.Func to fight repetitive code blocks , which actually

# July 7, 2008 5:25 PM

Karl Seguin said:

I generally subscribe to the attitude that premature optimizations are evil, but I strongly believe that

# July 7, 2008 7:45 PM

Community Blogs said:

I generally subscribe to the attitude that premature optimizations are evil, but I strongly believe that

# July 7, 2008 8:18 PM

Karl Seguin said:

Introduction Three weeks ago, Jeremy Miller and Chad Myers laid down their MVC approach . From the comments

# November 13, 2008 9:50 AM

Community Blogs said:

Introduction Three weeks ago, Jeremy Miller and Chad Myers laid down their MVC approach . From the comments

# November 13, 2008 1:34 PM

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas

# November 14, 2008 11:02 AM

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas

# November 15, 2008 7:10 PM

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas

# November 16, 2008 9:15 PM

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas

# November 18, 2008 9:41 PM

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas

# November 21, 2008 1:02 AM

Karl Seguin said:

code{color:#833;background:#fcfcfc;} Introduction There are language features that are nothing more than

# November 21, 2008 9:20 AM

Community Blogs said:

code{color:#833;background:#fcfcfc;} h4{margin:30px 0px 0px 0px;font-color:#fff;font-weight:bold;border

# November 21, 2008 9:48 AM

Karl Seguin said:

code{color:#833;background:#fcfcfc;} h4{margin:30px 0px 0px 0px;font-color:#fff;font-weight:bold;border

# November 27, 2008 10:16 AM

Community Blogs said:

code{color:#833;background:#fcfcfc;} h4{margin:30px 0px 0px 0px;font-color:#fff;font-weight:bold;border

# November 27, 2008 10:43 AM

Baka.Blog said:

Here is a good example I found: codebetter.com/.../get-your-func

# November 28, 2008 10:33 AM

Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas

# December 2, 2008 10:15 AM

Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas

# December 3, 2008 11:04 AM

Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas

# December 4, 2008 11:08 AM

Func<T> based Generic List Initializers « If only I were said:

Pingback from  Func&lt;T&gt; based Generic List Initializers &laquo; If only I were

# December 6, 2008 2:19 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About karl

I'm a developer living in Ottawa, Ontario. I like to focus on medium to large scale enterprise development, maintainable code, DDD and unit testing. I occasionally speak at conferences, am an editor for DotNetSlackers and contribute to projects here and there. Check out Devlicio.us!

Our Sponsors

Proudly Partnered With