Anonymous delegate as function parameter

No1Lives4Ever picture No1Lives4Ever · Feb 15, 2014 · Viewed 34.4k times · Source

I'm trying to pass parameter, which is anonymous delegate (no input parameters, no return value), to function.

Something like this:

private function DoSomething(delegate cmd)
{
     cmd();
}

Then, I'm want to use this function to call function in this way:

DoSomething(delegate
{
     Console.WriteLine("Hooah!");
});

I'm want this specific way, because it's easy to use writing style.

Possible?

Answer

keenthinker picture keenthinker · Feb 15, 2014

Exactly for such purposes Microsoft has created the Action and Func wrapper classes in the .NET framework. Both classes are relying on anonymous functions. Use Action if you don't need to return any result, just to execute the anonymous function:

private void DoSomething(Action action)
{
    action();
}

It can be used like this:

DoSomething(() => 
{
    Console.WriteLine("test");
});

The () => term is a lambda expression and means something like input with no parameters is calling .... See the documentation for a thorough explanation.

If you want to return a result, then use the Func delegate:

private T DoSomething<T>(Func<T> actionWithResult)
{
    return actionWithResult();
}

Usage:

Console.WriteLine(DoSomething<int>(() => 
{
    return 100;
}));

Both wrappers have overrides that accept up to 8 parameters.

When using Func, the last parameter is always the return type:

// returns a string
Func<string> t = () => { return "test string"; };
// first parameter is of type int, result of type string
Func<int, string> toString = (id) => { return id.ToString(); };
// parameters are of type int and float, result is string
Func<int, float, string> sumToString = (n1, n2) => { return (n1 + n2).ToString(); };

The Func wrapper can be used directly with a typed parameter:

Func<string, string> up = text => text.ToUpper();
Console.WriteLine(up("test"));

I often use Func's in order to create a generic executor that is wrapped in a try/catch block and logging if something happens. This way i reduce the repetative code:

private T safeCallWithLog<T>(Func<T> action)
{
    try
    {           
        return action();
    }
    catch (Exception ex)
    {
        Console.WriteLine(String.Format("Oops ...: {0}", ex.Message));
    }
    // return default type if an error occured
    return default(T);
}

Usage:

var result = safeCallWithLog<DbEntry>(() =>
            {
                return databaseContext.GetEntryWithId(42);
            });

var id = safeCallWithLog<int>(() =>
                    {
                        return databaseContext.GetIdFor("J.D.");
                    }); 

You could still use the original delegate concept. The Action and the Func classes are just wrappers around predefined generic delegate methods.

// declare delegate contract
private delegate void output();
// create caller method
private void method(output fun)
{
    fun();
}
// some test functions, that must match exactly the delegate description
// return type and number of arguments
private void test1()
{
    Console.WriteLine("1");
}

private void test2()
{
    Console.WriteLine(DateTime.Now.ToString());
}

// call different methods
method(test1);
method(test2);
// inline call without hard coded method
method(delegate() 
{
    Console.WriteLine("inline");
});