Karl Seguin

Sponsors

The Lounge

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
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!)


Posted Thu, Jul 3 2008 8:15 PM by karl

[Advertisement]

Comments

DotNetKicks.com wrote Get Your Func On
on Fri, Jul 4 2008 3:08 AM

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

Peter Morlion wrote re: Get Your Func On
on Fri, Jul 4 2008 3:09 AM

Very interesting article, thanks.

Reflective Perspective - Chris Alcock » The Morning Brew #129 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #129
on Fri, Jul 4 2008 3:33 AM

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

Todd wrote re: Get Your Func On
on Fri, Jul 4 2008 5:51 AM

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?

karl wrote re: Get Your Func On
on Fri, Jul 4 2008 8:28 AM

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...

Sean Chambers wrote re: Get Your Func On
on Fri, Jul 4 2008 12:42 PM

Excellent example!

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

Bryan Reynolds wrote re: Get Your Func On
on Fri, Jul 4 2008 2:27 PM

As always, nice post!

Dew Drop - July 4, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - July 4, 2008 | Alvin Ashcraft's Morning Dew
on Fri, Jul 4 2008 5:08 PM

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

jbland wrote re: Get Your Func On
on Fri, Jul 4 2008 8:09 PM

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

Andrei Butnaru's blog wrote Programming links 07.05.2008
on Sat, Jul 5 2008 12:56 PM

Programming links 07.05.2008

Rafael Rosa Fu wrote re: Get Your Func On
on Sun, Jul 6 2008 5:14 PM

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

Community Blogs wrote Cache Access Pattern Revised
on Mon, Jul 7 2008 9:52 AM

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

Readed By Wrocław NUG members wrote Cache Access Pattern Revised
on Mon, Jul 7 2008 5:25 PM

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

Karl Seguin wrote Scale Cheaply - Memcached
on Mon, Jul 7 2008 7:45 PM

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

Community Blogs wrote Scale Cheaply - Memcached
on Mon, Jul 7 2008 8:18 PM

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

Karl Seguin wrote TextBoxFor(u => u.Name) - Unleash the power
on Thu, Nov 13 2008 9:50 AM

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

Community Blogs wrote TextBoxFor(u => u.Name) - Unleash the power
on Thu, Nov 13 2008 1:34 PM

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

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas wrote TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas
on Fri, Nov 14 2008 11:02 AM

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

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas wrote TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas
on Sat, Nov 15 2008 7:10 PM

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

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas wrote TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas
on Sun, Nov 16 2008 9:15 PM

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

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas wrote TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas
on Tue, Nov 18 2008 9:41 PM

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

TextBoxFor(u => u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas wrote TextBoxFor(u =&gt; u.Name) - Unleash the power - taccato! trend tracker, cool hunting, new business ideas
on Fri, Nov 21 2008 1:02 AM

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

Karl Seguin wrote Back to Basics: Generics
on Fri, Nov 21 2008 9:20 AM

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

Community Blogs wrote Back to Basics: Generics
on Fri, Nov 21 2008 9:48 AM

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

Karl Seguin wrote Back to Basics: Delegates, Anonymous Methods and Lambda Expressions
on Thu, Nov 27 2008 10:16 AM

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

Community Blogs wrote Back to Basics: Delegates, Anonymous Methods and Lambda Expressions
on Thu, Nov 27 2008 10:43 AM

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

Baka.Blog wrote How to use Func<T>
on Fri, Nov 28 2008 10:33 AM

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

Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas wrote Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas
on Tue, Dec 2 2008 10:15 AM

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

Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas wrote Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas
on Wed, Dec 3 2008 11:04 AM

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

Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas wrote Back to Basics: Delegates, Anonymous Methods and Lambda Expressions - taccato! trend tracker, cool hunting, new business ideas
on Thu, Dec 4 2008 11:08 AM

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

Func<T> based Generic List Initializers « If only I were wrote Func&lt;T&gt; based Generic List Initializers &laquo; If only I were
on Sat, Dec 6 2008 2:19 PM

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

Karl Seguin wrote Making the untestable testable with Anonymous Methods and Dependency Injection
on Fri, May 8 2009 10:43 AM

It can be frustrating to want to write unit tests, only to hit some code which is rather untestable.