有没有方法可以告诉编译器不要为匿名委托创建内部类?

3

让我们来看一个例子(请注意,成员为静态的原因纯粹是为了让它尽可能地简短并且可运行):

public class Program
{
    private static readonly ReaderWriterLock Lock = new ReaderWriterLock();

    public static void SafeRead(Action action)
    {
        Lock.AcquireReaderLock(1000);

        try
        {
            action();
        }
        finally
        {
            Lock.ReleaseReaderLock();
        }
    }

    public static void Main(string[] args)
    {
        SafeRead(() => Console.WriteLine(args));
    }
}

在ILSpy中查看后,我发现一个名为“c__DisplayClass2_0”的内部类被创建用于lambda表达式。该类包含方法和对本地变量“args”的引用,存储在公共字段中,如下所示:

.class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass2_0'
extends [mscorlib]System.Object
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
        01 00 00 00
    )
    // Fields
    .field public string[] args

    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {} // end of method '<>c__DisplayClass2_0'::.ctor

    .method assembly hidebysig 
        instance void '<Main>b__0' () cil managed 
    {} // end of method '<>c__DisplayClass2_0'::'<Main>b__0'

} // end of class <>c__DisplayClass2_0

我知道在 args 局部变量和 Main 方法本身的生命周期之外使用 lambda 表达式时需要类和字段。但是如果我可以保证 lambda 在 Main 方法内同步调用,那么怎么办呢?也许只需在 Program 类中创建一个内部字段和方法即可,而不必再创建一个额外的类。我意识到这样会增加 Program 类的大小,但如果包含此类匿名委托的任何方法非常频繁地被调用,每个调用都会创建一个额外的类,如果我更注重性能而非内存消耗,则情况似乎更糟。在我的情况下,是否有一种方法可以指示编译器不创建内部类?或者我错过了什么重要的东西?


3
这个实现细节极其不可能成为真正程序中性能问题的根本原因。这是编译器的一个实现细节,它会基于各种原因生成类。 - Damien_The_Unbeliever
1
此外,请记住,通常情况下,任何方法都可能被多个线程同时调用。采用“只需保留本地字段”的方法时,如果第二个调用者在第一个调用者的调用仍在运行且尚未使用它时传递了不同的args值,会发生什么? - Damien_The_Unbeliever
1
但我敢打赌,当它成为性能瓶颈并且无法达到性能目标时,你肯定会把注意力集中在那里。那么现在是不是意味着你永远不再使用 foreach,还是在大多数常见的使用场景中仍然使用它? - Damien_The_Unbeliever
1
这个额外类的整个目的是将所有闭合变量捆绑到一个持久对象中。如果您不使用闭包,则没有理由使用匿名委托(即,您可以直接引用类方法),并且您将不再拥有“隐藏”的类的大量存在。总的来说,这可能是一个毫无意义的微观优化教训。 - spender
1
我觉得这是骗局,你是不是有垃圾回收或性能问题? - TheGeneral
显示剩余3条评论
1个回答

2

好的,不要使用lambda表达式:

private static string[] args;
public static void Main(string[] args)
{
    Program.args = args;
    SafeRead(Write);
}

private static void Write()
{
    Console.WriteLine(args);
}

这段代码不会为带有参数的lambda表达式生成中间类。

你也可以将操作改为Action<string[]>,并在SafeRead之间传递参数。


但是我意识到,作为一个普遍的观点,如果您希望Main被多个线程调用,那么您不能使用这种策略。 - Damien_The_Unbeliever
请查看将参数传递给 SafeRead 的更新 @Damien_The_Unbeliever - Patrick Hofman
嗯,在你的情况下,我需要声明私有静态参数.. 这就是我想避免的。也许我表达得不太清楚,我想知道是否可以更接近,比如说,#define C++ 的行为.. 无论如何,我非常感谢你们所有人的回复。谢谢! - Rexxowski
生成的类是使用lambda的直接结果。因此,没有其他方法可以强制编译器以不同的方式进行编译。@Rexxowski - Patrick Hofman
@PatrickHofman -- 所以,没有其他方法可以强制编译器进行不同的编译。-- 这正是我想知道的..谢谢.. - Rexxowski
1
据我所知,没有记录 @Rexxowski - Patrick Hofman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接