How can I create unique delegates using an anonymous method in a loop (in C #)?

code:

using System.IO;
using System;
using System.Reflection;
using System.Collections.Generic;

class AnyClass
{
    delegate void Del(string str);
    static void Main()
    {
        List<Del> listDel = new List<Del>();

        listDel.Add(delegate(string str) { });
        Console.WriteLine( listDel[0].Method.ToString() );

        listDel.Add(delegate(string str) { });
        Console.WriteLine( listDel[1].Method.ToString() );

        for (int i = 0; i < 2; i++)
        {
            listDel.Add(delegate(string str) { });
        }
        Console.WriteLine( listDel[2].Method.ToString() );
        Console.WriteLine( listDel[3].Method.ToString() );
    }
}

      

Output:

Void m__0 (System.String)
Void m__1 (System.String)
Void m__2 (System.String)
Void m__2 (System.String)

Why did the delegates started in the cycle, "point" on the same method ( m__2

), while copies created outside the loop point, using two different methods ( m__0

and m__1

)?

Is there a way to create delegates that point to different / unique methods within the loop ?

Usage example: I need to have delegates as keys in a dictionary, so they must be unique. Creation within a loop is required to provide sufficient flexibility.

+3


source to share


3 answers


Why do delegates created in the loop "point" to the same method (m__2), while those created outside the loop point are two different methods (m__0 and m__1)?

Because behind the scenes, the compiler caches the creation of the delegate. When you create the first two delegates, the compiler doesn't know they are the same, so it creates two different cached delegates and two named methods. Inside the loop, the for

compiler is only optimized after the delegate instance once. It can be sure that it is the same delegate every time, instantiate it once, and then cache it.

When you decompile your code, it looks something like this:

private delegate void Del(string str);

[CompilerGenerated]
private static Launcher.Del CS$<>9__CachedAnonymousMethodDelegate3;

[CompilerGenerated]
private static Launcher.Del CS$<>9__CachedAnonymousMethodDelegate4;

[CompilerGenerated]
private static Launcher.Del CS$<>9__CachedAnonymousMethodDelegate5;

private static void Main()
{
    List<Launcher.Del> listDel = new List<Launcher.Del>();
    List<Launcher.Del> arg_24_0 = listDel;

    if (Launcher.CS$<>9__CachedAnonymousMethodDelegate3 == null)
    {
        Launcher.CS$<>9__CachedAnonymousMethodDelegate3 = 
                                 new Launcher.Del(Launcher.<Main>b__0);
    }
    arg_24_0.Add(Launcher.CS$<>9__CachedAnonymousMethodDelegate3);

    Console.WriteLine(listDel[0].Method.ToString());

    List<Launcher.Del> arg_5D_0 = listDel;
    if (Launcher.CS$<>9__CachedAnonymousMethodDelegate4 == null)
    {
        Launcher.CS$<>9__CachedAnonymousMethodDelegate4 = 
                                 new Launcher.Del(Launcher.<Main>b__1);
    }
    arg_5D_0.Add(Launcher.CS$<>9__CachedAnonymousMethodDelegate4);

    Console.WriteLine(listDel[1].Method.ToString());
    for (int i = 0; i < 2; i++)
    {
        List<Launcher.Del> arg_9A_0 = listDel;
        if (Launcher.CS$<>9__CachedAnonymousMethodDelegate5 == null)
        {
            Launcher.CS$<>9__CachedAnonymousMethodDelegate5 = 
                                 new Launcher.Del(Launcher.<Main>b__2);
        }
        arg_9A_0.Add(Launcher.CS$<>9__CachedAnonymousMethodDelegate5);
        Console.WriteLine(listDel[2 + i].Method.ToString());
    }
}

[CompilerGenerated]
private static void <Main>b__0(string str)
{
}
[CompilerGenerated]
private static void <Main>b__1(string str)
{
}
[CompilerGenerated]
private static void <Main>b__2(string str)
{
}

      



I would definitely not rely on the delegate being the proper key for Dictionary

.

Is there a way to instantiate delegates that point to different / unique methods within the loop?

You can only force a delegate to be a "fresh instance" by explicitly instantiating new Del

yourself and passing a new named method each time. There are other more "suspicious" ways to do this, but I would not recommend taking these paths just to get a new delegate.

+2


source


Is there a way to create delegates that point to different / unique methods within the loop?

You cannot force each iteration of the loop to create a different method because the methods are hardcoded in the assembly. Their number is fixed, while the cycle can be unlimited.

You can make each syntactic kind of lambda a different method using some kind of hack:

Action<int> x = i => {
   if (Environment.CurrentManagedThreadId < 0 /*always false*/)
     Console.WriteLine(i + uniqueIntegerHere);
};

      



This forces each method body to be unique and the compiler can never optimize it. You can, of course, pull the body into a helper method.

If you need unique delegates per loop iteration, you either need to create methods at runtime or keep a set of statically compiled methods:

void F1() { }
void F2() { }
void F3() { }
...

      

T4 patterns come to mind.

0


source


Another way similar to that suggested by @usr. You can force the compiler to create a new instance of the delegate object using the reflection method Delegate.CreateDelegate(type, this, methodInfo)

. The trick comes at the point where the parameter is this

always a new object, thus calling on it myMethod

, and therefore each delegate is effectively a different context for the compiler.

This requires the delegate method to be inside a separate class that you can create. I'm not sure if this requirement fits your actual task. You might be inspired by another solution based on this ...

using System.IO;
using System;
using System.Reflection;
using System.Collections.Generic;

class AnyClass
{
    delegate void Del(string str);

    private static Dictionary<Del, string> dict = new Dictionary<Del, string>();
    static void Main()
    {
        List<Del> listDel = new List<Del>();
        int count = 10;
        for (int i = 0; i < count; i++)
        {
            listDel.Add(factory());
            dict.Add(listDel[i ], "Delegate " + (i));
        }
        for (int i = 0; i < count; i++)
        {
            Console.WriteLine(listDel[i].Method.ToString());
            listDel[i].Invoke((i).ToString());
        }

        Console.ReadLine();
    }

    public class DelegateEncapsulator
    {
        private int _number;
        public DelegateEncapsulator(int number)
        {
            _number = number;
        }
        public void myMethod(string str) { 
             Console.WriteLine("Delegate " + _number + " " + str); 
        }
    }

    private static int delegateCounter = 100;
    private static Del factory()
    {
        var obj = new DelegateEncapsulator(delegateCounter++);
        var ret = (Del)Delegate.CreateDelegate(typeof(Del), obj, 
             typeof(DelegateEncapsulator).GetMethod("myMethod"));
        return ret;
    }
}

      

This code adds all delegates to the dictionary. You can play with the added number elements.

Hope it helps

0


source







All Articles