如何在循环中使用匿名方法实例化唯一委托(在 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__0
和 m__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;
}
}
此代码将所有委托添加到字典中。您可以尝试添加数字元素。
希望对您有所帮助
代码:
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__0
和 m__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;
}
}
此代码将所有委托添加到字典中。您可以尝试添加数字元素。
希望对您有所帮助