【问题标题】:Is there a way how to instruct compiler not to create internal class for anonymous delegate?有没有办法指示编译器不要为匿名委托创建内部类?
【发布时间】:2018-11-25 01:44:16
【问题描述】:

让我们看下面的例子(请注意,成员之所以是静态的,纯粹是因为我希望它尽可能短且可运行):

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 中查看后,我可以看到为 lambda 创建了一个内部类“c__DisplayClass2_0”。该类包含存储在公共字段中的方法和对局部变量“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

我知道如果 lambda 超出 args 局部变量和 Main 方法本身的生命周期,则需要类和字段。但是,如果我可以确保像我的示例中那样在 Main 方法中同步调用 lambda 怎么办?可能在 Program 类中创建一个内部字段和一个方法就足够了,而不是创建一个额外的类。我知道 Program 类的大小会因这个内部字段而增加,但是如果非常频繁地调用任何包含此类匿名委托的方法,那么每次调用都会创建一个额外的类,如果我更喜欢性能,这在我看来情况更糟内存消耗过多..有没有办法指示编译器不要在我的场景中创建内部类?还是我错过了什么重要的东西?

【问题讨论】:

  • 异常这个实现细节不太可能成为实际程序中任何性能问题的根本原因。它是编译器的一个实现细节,它会出于各种原因生成类。
  • 另外,请记住,一般而言,任何方法都可能被多个线程同时调用。使用您的“只保留一个本地字段”方法,当第二个调用者 Mainargs 传递一个不同的值而第一个调用者的调用仍在运行并且尚未使用它时会发生什么?
  • 但我敢打赌,当它成为性能瓶颈且未达到性能目标时,您会将注意力集中在此处。这是否意味着您现在从不使用foreach,或者您仍然在最常见的使用场景中使用它?
  • 这个额外类的全部原因是将所有封闭变量捆绑到一个持久对象中。如果您不使用闭包,则没有理由使用匿名委托(即您可以直接引用类方法),并且您将不再有“隐藏”类的扩散。总而言之,这可能是毫无意义的微优化的一课。
  • 我叫恶作剧,你有 gc 或性能问题吗?

标签: c# delegates anonymous


【解决方案1】:

好吧,是的,不要使用 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&lt;string[]&gt;,并将参数传递给SafeRead

【讨论】:

  • 但我意识到,一般来说,如果您希望Main 被多个线程调用,则不能使用此策略。
  • 查看将参数传递给SafeRead@Damien_The_Unbeliever 的更新
  • :) 好吧,在你的情况下,我需要声明私有静态参数。这就是我想要避免的。也许我不是很清楚,我想知道我是否可以更接近,比如说,#define C++ 行为。无论如何,我真的很感谢你们的所有回复,伙计们。谢谢!
  • 生成的类是使用 lambda 的直接结果。所以,不,没有其他方法可以强制编译器以不同方式编译@Rexxowski
  • 不,据我所知没有记录@Rexxowski
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多