Home > Articles > Programming > C#

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Introducing Generic Anonymous Methods

For newer programmers, word reuse can be confusing. For example, anonymous methods are unrelated to anonymous types except to the extent that it means the type of the method is unnamed. Anonymous methods are covered in this section because they are valuable and worth covering, but, for the most part, this section switches topics.

Anonymous methods behave like regular methods except that they are unnamed. They were introduced as an alternative to defining delegates that did very simple tasks, where full-blown methods amounted to more than just extra typing. Anonymous methods also evolved further into Lambda Expressions, which are even shorter (terse) methods. Chapter 5, "Understanding Lambda Expressions and Closures," delves deeper into the evolution of methods. For now, this section takes an introductory look at anonymous generic methods.

An anonymous method is like a regular method but uses the delegate keyword, and doesn't require a name, parameters, or return type. Listing 1.15 shows a regular method (used as a delegate for the CancelKeyPress event, Ctrl+C in a console application) and an anonymous delegate that performs the same role.

Listing 1.15. A Regular Method and Anonymous Method Handling the CancelKeyPress Event in a Console Application

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace AnonymousMethod
{
  class Program
  {
    static void Main(string[] args)
    {
      // ctrl+c
      Console.CancelKeyPress += new ConsoleCancelEventHandler
        (Console_CancelKeyPress);

      // anonymous cancel delegate
      Console.CancelKeyPress +=
        delegate

        {
          Console.WriteLine("Anonymous Cancel pressed");
        };

      Console.ReadLine();

    }

    static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
    {
      Console.WriteLine("Cancel pressed");
    }
  }
}

The regular method (used as a delegate) is named ConsoleCancelEventHandler. Although the double-Tab trick generates these stubbed delegates for you, they are overkill for one-line event handlers. The second statement that begins with the Console.CancelKeyPress += delegate demonstrates an anonymous method (delegate) that is equivalent to the longer form of the method. Notice that because the parameters in the delegate aren't used, they are omitted from the anonymous delegate. You have the option of using the parameter types and names if they are needed in the delegate.

Using Anonymous Generic Methods

Delegates are really just methods that are used (mostly) as event handlers. Generic methods are those that have parameterized types. (Think replaceable data types.) Therefore, anonymous generic delegates are anonymous methods that are associated with replaceable parameterized types. A very useful type is Func<T> (and Func<T, T1, ... T n >, demonstrated in Listing 1.16). This generic delegate (defined in the System namespace) can be assigned to delegates and anonymous delegates with varying return types and parameters, which makes it a very flexible delegate holder.

Listing 1.16. Demonstrating How to Use System.Func to Define an Essentially Nested Implementation of the Factorial Function

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace AnonymousGenericDelegate
{
  class Program
  {
    static void Main(string[] args)
    {
      System.Func<long, long> Factorial =
        delegate(long n)
        {
          if(n==1) return 1;
          long result=1;
          for(int i=2; i<=n; i++)
            result *= i;
          return result;
        };

      Console.WriteLine(Factorial(6));
      Console.ReadLine();
    }
  }
}

For all intents and purposes, Factorial is a nested function. Listing 1.16 used Func<long, long>, where the first long parameter represents the return type and the second is the parameter. Notice that the listing also used a named parameter for the anonymous delegate.

Implementing Nested Recursion

Now, you can have a little fun bending and twisting the Factorial function to use recursion. The challenge is that the named delegate is not named until after the delegate definition—the name being Factorial. Hence, you can't use the name in the anonymous delegate itself, but you can make it work.

There is a class called StackFrame. StackFrame permits getting methods (and information from the call stack) and you can use this class and reflection to invoke the anonymous delegate recursively. (This code is obviously esoteric—referred to this as programmer esoterrorism—but it is fun and demonstrates a lot of features of the framework in a little bit of space, as shown in Listing 1.17.)

Listing 1.17. Nested, Recursive Anonymous Generic Methods—as a Routine Practice

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;


namespace AnonymousGenericRecursiveDelegate
{
  class Program
  {
    static void Main(string[] args)
    {
      Func<long, long> Factorial =
        delegate(long n)
        {
          return n > 1 ?
            n * (long)(new StackTrace()
            .GetFrame(0).GetMethod().Invoke(null, new object[]{n-1}))
            : n;
        };

      Console.WriteLine(Factorial(6));
      Console.ReadLine();
    }
  }
}

Again, writing code like the Factorial delegate in Listing 1.17 is only fun for the writer, but elements of it do have utility. For example, anonymous delegates like the Factorial can be useful for one-time, simple event handling. Assigning behaviors to the Func<T> delegate type effectively makes nested functions and reusable delegates that can be passed as arguments, a very dynamic way to program. Getting the StackFrame can be a great way to create a utility that tracks function calls during debugging—like writing the StackTrace to the Debug window in a way that is useful to you—and reflection has many uses.

Reflection can be useful for dynamically loaded assemblies, as demonstrated by NUnit and Visual Studio's unit testing.

  • + Share This
  • 🔖 Save To Your Account