如何在循环中使用匿名方法实例化唯一委托(在 C# 中)?

How to instantiate unique delegates using an anonymous method in a loop (in C#)?

代码:

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() );
    }
}

输出:

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

为什么在循环 "point" 中实例化的委托指向相同的方法 (m__2) 而在循环外实例化的委托指向两个不同的方法 (m__0m__1)?

有什么方法可以在循环中实例化指向different/unique方法的委托

用法示例:我需要将委托作为字典中的键,因此它们必须是唯一的。必须在循环内实例化才能提供足够的灵活性。

Why do the delegates instantiated in the loop "point" to the same method (m__2) whereas the ones instantiated outside the loop point to two different methods (m__0 and m__1)?

因为编译器在后台缓存委托创建。当您创建前两个委托时,编译器不知道它们是相同的,因此他创建了两个不同的缓存委托和两个命名方法。在您的 for 循环中,编译器通过仅实例化一次委托进行优化。他可以确定每次都是同一个委托,实例化一次,然后缓存。

当你反编译你的代码时,它实际上看起来像这样:

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)
{
}

我肯定会 not rely on a delegate being a proper key Dictionary

Is there any way how to instantiate delegates that point to different/unique methods inside a loop?

您只能通过自己显式创建一个 new Del 实例并每次传递一个新的命名方法来强制委托成为 "fresh instance"。还有其他更多 "fishy" 方法可以做到这一点,但我不建议仅仅为了获得新委托而采用这些方法。

Is there any way how to instantiate delegates that point to different/unique methods inside a loop?

您不能让每个循环迭代都创建不同的方法,因为方法是硬编码到程序集中的。他们的数量是固定的,而循环可以是无限的。

您可以使用某种 hack 使 lambda 的每个句法外观都有不同的方法:

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

这迫使每个方法体都是唯一的,编译器永远无法优化它。您当然可以将主体拉入辅助方法。

如果您希望每次循环迭代都有唯一的委托,您需要在运行时创建方法或保留一组静态编译的方法:

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

想到 T4 模板。

还有一种类似于@usr 提出的方法。您可以强制编译器使用反射方法 Delegate.CreateDelegate(type, this, methodInfo) 创建委托对象的新实例。诀窍在于 this 参数始终是一个新对象,因此迫使 myMethod 被调用,因此每个委托实际上代表编译器的不同上下文。

这要求委托方法位于一个单独的 class 中,您可以对其进行实例化。我不确定这个要求是否适合您的实际任务。也许您会基于这个解决方案得到启发...

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;
    }
}

此代码将所有委托添加到字典中。您可以尝试添加数字元素。

希望对您有所帮助