[REPOST] – Creating Local Methods with Func
I wrote briefly about the new Func generic delegate in Part 3 of the Upgrade your C# Skills series. Today, I wanted to explore this in a little more detail.
I’m sure that most of us have created many private methods to outsource code from one method to another. There are times we do this even when the method is only called from a single location. I’m going to use a simple example, but I think you’ll get the point:
{DateTime now = DateTime.Now;
int age = now.Year – person.Age;
Console.WriteLine(“{0} {1} was probably born in {2}.”,
person.FirstName,
person.LastName,
age);
}
Kind of a silly example, but fairly straight forward stuff, right? Well, what if we had two loops or other calculations in the same method that needed to know the Year born? Since we believe in code reuse, we would typically throw this into its own private method:
{
Console.WriteLine(“{0} {1} was probably born in {2}.”,
person.FirstName,
person.LastName,
CalculateYearBorn(person));
}…
privateint CalculateYearBorn(Person person)
{
DateTime now = DateTime.Now;
int age = now.Year – person.Age;
return age;
}
This should be code we can all relate to, and is a fine practice because it accomplishes two very important things. First, it promotes code reuse. Second, it simplifies and cleans up our logic. What unfortunately happens quite frequently is that the outsourced method is only called from one other method, even if it is multiple times. In this scenario, what we really need is a Local Method.
Local Methods
I don’t know if anyone else has coined this phrase before in this context, but it seems appropriate. A quick google search turned up several references to Java and some statistical analysis looking items. Adding the “C#” to the search terms found a couple of references where the term is misapplied to mean a private class method. To be clear, though, what I mean by Local Method is a method defined completely within a non-class parent scope. If you think about it, that’s what Anonymous Delegates are, but unlike an anonymous delegate, we need to be able to assign our method a name. The Func<> Generic Delegate can be used to accomplish this goal.
This approach is going to give us the best of both worlds. First, it is going to allow us to reuse code. Second, it is going to clean up our execution logic by replacing some of it (or maybe even all of it) with a method call. Finally, it is going to help reduce “Class clutter” by eliminating private methods that are only called from one location.
Creating and Using the Local Method
In order to use the local method, we first need to define it as a local variable. This local variable is of type Func<>, which has several different signatures. We are going to use Func<Person, int> to create a method that accepts a Person object as a parameter and returns an int:
delegate (Person per)
{
DateTime now = DateTime.Now;
int age = now.Year – per.Age;
return age;
});
Now, to use the local method, we simply call it like we would any other:
{
Console.WriteLine(“{0} {1} was probably born in {2}.”,
person.FirstName,
person.LastName,
CalculateYearBorn(person));
}
I think you’ll agree that this is pretty cool and could be very useful. And of course, since this is based on a Generic Delegate, we can use Lambda Expressions to write our code. So even though this is a fairly silly example, let’s go ahead and clean it up a bit:
new Func<Person, int>(per => { return DateTime.Now.Year – per.Age; });people.ForEach(person => Console.WriteLine(“{0} {1} was probably born in {2}.”,
person.FirstName,
person.LastName,
CalculateYearBorn(person)));
Some Final Notes
I really like this approach, but it naturally does not suit every purpose, so rule one, to paraphrase Sean Connery in The Untouchables, “don’t bring a knife to a gun fight.” In other words, don’t go crazy and use this just because you can. Use it to minimize repeated code within a specific method or to simplify complex code blocks.
There are a couple of additional things you should know. First, you must define the delegate and method details before using them, otherwise the compiler will complain. Second, since this is a local method it has complete access to the other local method variables: this should help reduce the number of parameters you must send in order to accomplish your task. Finally, since the local method is actually a local variable itself, you should be able to change the address dynamically to other delegates. I don’t think I would recommend this last item, but it could prove to be interesting experimentation fodder.