Tags: , , | Posted by Kevin Babcock on 5/21/2009 7:33 PM | Comments (1)

C# delegates are fun to use. They let you encapsulate a bit of logic in a nice, neat little object that behaves like a method. All you have to do is declare the signature of the method that the delegate can reference without having to specify the actual method itself. This allows you to define how a method should be used but defer its implementation to another place in the application, giving it a polymorphic behavior. You can call the delegate directly or pass it as a parameter to another method, thereby “delegating” its functionality. Think function pointers in C/C++. Here’s an example of how we would instantiate a delegate, use it as a parameter, and call it in C# 2.0:

public delegate bool Validator(string value);

public class EmailAddress
{
    private Validator _isValid;
    private string _value;

    public EmailAddress()
        : this(new Validator(ValidateEmailAddress))
    {
    }

    public EmailAddress(Validator validator)
    {
        _isValid = validator;
    }

    public string Value
    {
        get { return _value; }
        set
        {
            if (!_isValid(value))
                throw new ArgumentException("Invalid email address", "value");
            _value = value;
        }
    }

    private static bool ValidateEmailAddress(string emailAddress)
    {
        string regex = @"\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b";
        return Regex.IsMatch(emailAddress, regex);
    }
}

As you can see, I’ve created a simple EmailAddress class which which allows an optional delegate to be passed in as a parameter to its constructor. The Validator delegate is declared outside of the class and can reference any method which accepts a string parameter and returns a boolean result. You can see that if the default constructor is used, a new instance of the delegate is instantiated with the static ValidateEmailAddress() method, giving the class a default validation behavior. The delegate is called whenever a new value is assigned to the Value property of the class. As you can probably see, this implementation is flexible enough that we can pass in any validation rule we want to the class using the delegate parameter.

We can create our own delegate to pass as a parameter to the EmailAddress constructor:

Validator emailRule = delegate(string emailAddress)
{
    return emailAddress.EndsWith("@live.com");
};
EmailAddress email = new EmailAddress(emailRule);

Or we can pass in an anonymous delegate:

EmailAddress myEmail = new EmailAddress(delegate(string emailAddress)
{
    return emailAddress.EndsWith("@live.com");
});

.NET 3.5 includes two new generic delegates that were added to the Framework. System.Func is a method pointer that returns a value. It has the following 5 overloads:

  • Func<TReturn>
  • Func<TParam, TReturn>
  • Func<TParam1, TParam2, TReturn>
  • Func<TParam1, TParam2, TParam3, TReturn>
  • Func<TParam1, TParam2, TParam3, TParam4, TReturn>

System.Action simply encapsulates a method, returning no value. It has 4 overloads:

  • Action<TParam>
  • Action<TParam1, TParam2>
  • Action<TParam1, TParam2, TParam3>
  • Action<TParam1, TParam2, TParam3, TParam4>

As you can see, System.Func and System.Action allow you to reference any method that requires up to 4 parameters without having to create your own delegate type. This is handy because now I don’t have to create (and manage) my own delegate types. Here’s a new version of the same EmailAddress class:

public class EmailAddress
{
    private Func<string, bool> _isValid;
    private string _value;

    public EmailAddress()
        : this(e => { return ValidateEmailAddress(e); })
    {
    }

    public EmailAddress(Func<string, bool> validator)
    {
        _isValid = validator;
    }

    public string Value
    {
        get { return _value; }
        set
        {
            if (!_isValid(value))
                throw new ArgumentException("Invalid email address", "value");
            _value = value;
        }
    }

    private static bool ValidateEmailAddress(string emailAddress)
    {
        var regex = @"\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b";
        return Regex.IsMatch(emailAddress, regex);
    }
}

Did you notice the lambda expression on line 7? Another cool new feature of C# 3.0! Here’s how we might create an instance of the EmailAddress class:

var email = new EmailAddress(e => e.EndsWith("@gmail.com"));

With lambda expressions we now have more terse syntax for anonymous delegates, and a very easy way to extract a bit of logic into a variable that we can reuse. If we wanted to create multiple instances of the EmailAddress class, each with the same validation rule, we might do so like this:

var emailRule = (Func<string, bool>)(e => e.EndsWith("@gmail.com"));
var myEmail = new EmailAddress(emailRule);
var yourEmail = new EmailAddress(emailRule);

Notice in the code above that I had to explicitly case the lambda expression on line 1 to type Func<string, bool>. This was only required because I was using the var keyword (another C# 3.0 feature), which lets the compiler infer the type used by the variable. Since the compiler could have no way of accurately inferring the variable type with just the lambda expression, I had to cast it to the correct type. I could have just as easily instantiated it this way:

Func<string, bool> emailRule = e => e.EndsWith("@gmail.com");

So that’s a quick primer on passing delegates as parameters, using both C# 2.0 and 3.0 syntax. It’s easy to do and can add a great deal of flexibility to your code. Pretty cool, huh?

kick it on DotNetKicks.com

Comments

devix on 5/22/2009 9:54 AM Nice information on C