Anonymous Methods

An anonymous method is a nameless method. Sometimes a function is used exclusively with a delegate. It is never called directly as a stand-alone method. Without an anonymous method, you must define a separate function as a member of a class. The function then is assigned to the delegate. Using an anonymous method prevents creating a separate method unnecessarily, which is cleaner and more convenient than using a named method. Anonymous methods can be substituted for delegates in most circumstances. Lambda expressions might make anonymous methods irrelevant. However, anonymous methods remain an artifact of the C# language. Lambda expressions were introduced in Chapter 1.

Define an anonymous method with the delegate keyword and a nameless function body. This code assigns an anonymous method to a delegate:

using System;
using System.Threading;

namespace Donis.CSharpBook {

    public delegate void DelegateClass();

    public class Starter {
        public static void Main() {
            DelegateClass del = delegate { // anonymous method
                Console.WriteLine("Running anonymous method");
            };
            del();
        }
    }
}

As shown in the preceding example, the anonymous method need not have a signature. The signature of the anonymous method and the return type is inferred from the delegate, which is another example of delegate inference. If the delegate had two parameters and returned an integer, the anonymous method would have the same two parameters and return an integer. However, anonymous methods can be called without parameters, regardless of the delegate type. The advantage of an anonymous method without a signature is its compliance with almost any delegate. The disadvantage is that anonymous methods defined without a signature cannot access the parameters of the delegate. Therefore, the parameters cannot be used in the function body. For this reason, anonymous methods without a signature cannot be employed if the delegate has an out parameter. Out parameters must be initialized in the called method, which is impossible in an anonymous method without a signature. When an anonymous method is called through the delegate, the parameters still must be provided in the method invocation even though the anonymous methods cannot use them.

Here is the syntax of an anonymous method:

delegate optional_signature {anonymous method expression}

In support of an anonymous method, the C# compiler does several things:

  • The compiler infers the signature of the anonymous method from the delegate.

  • The compiler confirms there are no out parameters for anonymous methods without parameters.

  • The compiler confirms that the return type of the delegate is compatible with the anonymous method.

  • The compiler creates a new delegate that is initialized with a reference to the anonymous method.

If a signature is specified, the parameters for an anonymous method are appended to the delegate keyword. The parameters are initialized with the parameters from the delegate invocation. Here is an example of an anonymous method with a signature:

using System;
using System.Threading;

namespace Donis.CSharpBook {

    public delegate int DelegateClass(out int param);

    public class Starter {
        public static void Main() {
            int var;
            DelegateClass del = delegate(out int inner) {
                inner = 12;
                Console.WriteLine("Running anonymous method");
                return inner;
            };
            del(out var);
            Console.WriteLine("Var is {0}", var);
        }
    }
}

As demonstrated in the preceding code in this section, anonymous methods can be used similarly to the way delegates are used. The exception is the -= compound assignment operator, which removes function references of a delegate from a multicast delegate. Because anonymous methods are unnamed, you cannot remove the method from a multicast delegate.

Anonymous methods are not literally nameless. The C# compiler implements the anonymous method as a named method in the class where it is defined. This method is both static and private. The delegate is assigned this method in the class constructor. The following is the compiler’s naming convention for anonymous methods:

<functionname>uniqueid

The method has the same signature as the target delegate.

A delegate initialized with the anonymous method also is added to the class as a static and private field. The following shows the format of the delegate name. The id and the # represent characters that are compiler generated to create a unique name.

<>id__CachedAnonymousMethodDelegate#

The following code has two delegates and two anonymous methods:

using System;

namespace Donis.CSharpBook {

    public delegate void ADelegate(int param);
    public delegate int BDelegate(int param1, int param2);

    public class Starter {
        public static void Main() {
            ADelegate del = delegate(int param) {
                param = 5;
            };
        }

        public int MethodA() {
            BDelegate del = delegate(int param1, int param2) {
                return 0;
            };
            return 0;
        }
    }
}

Figure 10-3 is an internal view of the preceding code using ILDASM. It shows the delegates that are created as fields and the named methods created in the class for the anonymous methods.

An internal view of delegates and the names created for the anonymous methods

Figure 10-3. An internal view of delegates and the names created for the anonymous methods

Outer Variables

Anonymous methods can refer to local variables of the surrounding code. In the anonymous method, you can use anything available in the surrounding code. Local variables from the surrounding code that are used in an anonymous method are called outer variables.

The scope of a local variable is closely linked to the method where the variable is declared. A local variable is typically removed from memory when the function or block in which it is declared is exited. Outer variables used in an anonymous method are captured, which extends the lifetime of the variable. The lifetime of an outer variable is the same as that of the delegate. The outer variable is removed when the delegate is garbage-collected. Because the lifetime of an outer variable is aligned with the delegate, it can persist across invocations of the anonymous method.

In the following code, MethodA returns an anonymous function of type DelegateClass, which is added to the del delegate. The increment variable is local to MethodA and normally would be removed when MethodA returns. However, the increment variable is used in the anonymous method contained in MethodA. As such, the increment variable is an outer variable and persists beyond MethodA. The anonymous method returned by MethodA is called three times in Main, outside MethodA (that is, beyond the normal scope of the increment variable). Each time the anonymous method is called, the increment variable is available and is incremented:

using System;

namespace Donis.CSharpBook {

    public delegate void DelegateClass(out int var);

    public class Starter {

        public static void Main() {

            DelegateClass del = MethodA();
            int var1, var2, var3;
            del(out var1);
            del(out var2);
            del(out var3);
            Console.WriteLine(var3);
        }

        public static DelegateClass MethodA() {
            int increment = 0;
            return delegate(out int var) {
                var = ++increment;
            };
        }
    }
}

The C# compiler creates a private nested class for anonymous methods that have outer variables. This class is where outer variables are captured. Conversely, anonymous methods that have no outer variables are implemented as private static methods, as described previously. The nested class has a public field for each outer variable used in the anonymous method. The local variable is persisted to this public field. The nested class also has a named method for the anonymous method. The named method has the same signature as the related delegate. Figure 10-4 shows the nested class created for an anonymous method with outer variables.

A view of the nested class created for an anonymous method with an outer variable

Figure 10-4. A view of the nested class created for an anonymous method with an outer variable

Local variables usually are fixed in memory. They are not collected by the CLR during garbage collection. Local variables are located on the stack and removed from memory when the surrounding method ends. When a local variable is captured (the outer variable) in an anonymous method, it is placed on the managed heap and is movable. The outer variables are thus available for garbage collection. For this reason, outer variables must be pinned or otherwise fixed before using unsafe code.

Generic Anonymous Methods

Anonymous methods can use generic parameters of the surrounding class or delegate. However, anonymous methods cannot define new generic parameters or constraints. Here are two examples of anonymous methods using generic parameters:

using System;

namespace Donis.CSharpBook {

    public delegate void ADelegate<T>(T tvalue);

    public class ZClass {
        public void MethodA() {
            ADelegate<int> del = delegate(int var) {
                Console.WriteLine("ZClass MethodA: {0}",var);
            };
            del(3);
        }
    }

    public class YClass<T> {
        public delegate void BDelegate(T tVal);
        public void MethodA(T tVal) {
            BDelegate del = delegate(T tValue) {
                Console.WriteLine("YClass MethodA: {0}",tValue);
            };
            del(tVal);
        }
    }

    public class Starter {

        public static void Main() {
           ZClass z = new ZClass();
           z.MethodA();
           YClass<int> y = new YClass<int>();
           y.MethodA(55);
        }
    }
}

Limitations of Anonymous Methods

Although anonymous methods are similar to other methods, they also have some limitations (some of which have already been mentioned):

  • Do not attempt to jump out of an anonymous method.

  • Do not define new generic parameters or constraints.

  • Do not apply attributes to anonymous methods.

  • Do not use anonymous method with the -= compound assignment operator.

  • Cannot be an unsafe method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.255.178