离散匿名方法分享类?离散匿名方法分享类?(Discrete Anonymous methods sh

2019-05-12 03:08发布

我被打了一下,用埃里克利珀的Ref<T>类从这里 。 我在IL注意到,它看起来像匿名方法使用相同的生成的类,即使这意味着该班有一个额外的变量。

虽然只使用一个新的类定义似乎有些合理的,这令我很奇怪,只有一个实例<>c__DisplayClass2创建。 这似乎暗示着这两个实例Ref<T>是引用相同的<>c__DisplayClass2难道这不是意味着y不能收集到vart1收集,这可能不是之后很久以后发生joik回报? 毕竟,难保有些白痴不会写一个函数(直接在IL),它直接访问y通过vart1 aftrer joik回报。 也许这甚至可以与反思,而不是通过疯狂的IL来完成。

sealed class Ref<T>
{
    public delegate T Func<T>();
    private readonly Func<T> getter;
    public Ref(Func<T> getter)
    {
        this.getter = getter;
    }
    public T Value { get { return getter(); } }
}

static Ref<int> joik()
{
    int[] y = new int[50000];
    int x = 5;
    Ref<int> vart1 = new Ref<int>(delegate() { return x; });
    Ref<int[]> vart2 = new Ref<int[]>(delegate() { return y; });
    return vart1;
}

运行IL DASM确认vart1vart2中都使用<>__DisplayClass2 ,其中载有一个公共字段用于x和y的。 joik的IL:

.method private hidebysig static class Program/Ref`1<int32> 
        joik() cil managed
{
  // Code size       72 (0x48)
  .maxstack  3
  .locals init ([0] class Program/Ref`1<int32> vart1,
           [1] class Program/Ref`1<int32[]> vart2,
           [2] class Program/'<>c__DisplayClass2' '<>8__locals3',
           [3] class Program/Ref`1<int32> CS$1$0000)
  IL_0000:  newobj     instance void Program/'<>c__DisplayClass2'::.ctor()
  IL_0005:  stloc.2
  IL_0006:  nop
  IL_0007:  ldloc.2
  IL_0008:  ldc.i4     0xc350
  IL_000d:  newarr     [mscorlib]System.Int32
  IL_0012:  stfld      int32[] Program/'<>c__DisplayClass2'::y
  IL_0017:  ldloc.2
  IL_0018:  ldc.i4.5
  IL_0019:  stfld      int32 Program/'<>c__DisplayClass2'::x
  IL_001e:  ldloc.2
  IL_001f:  ldftn      instance int32 Program/'<>c__DisplayClass2'::'<joik>b__0'()
  IL_0025:  newobj     instance void class Program/Ref`1/Func`1<int32,int32>::.ctor(object,
                                                                                    native int)
  IL_002a:  newobj     instance void class Program/Ref`1<int32>::.ctor(class Program/Ref`1/Func`1<!0,!0>)
  IL_002f:  stloc.0
  IL_0030:  ldloc.2
  IL_0031:  ldftn      instance int32[] Program/'<>c__DisplayClass2'::'<joik>b__1'()
  IL_0037:  newobj     instance void class Program/Ref`1/Func`1<int32[],int32[]>::.ctor(object,
                                                                                        native int)
  IL_003c:  newobj     instance void class Program/Ref`1<int32[]>::.ctor(class Program/Ref`1/Func`1<!0,!0>)
  IL_0041:  stloc.1
  IL_0042:  ldloc.0
  IL_0043:  stloc.3
  IL_0044:  br.s       IL_0046
  IL_0046:  ldloc.3
  IL_0047:  ret
} // end of method Program::joik

Answer 1:

是的,MS执行的匿名方法有效地创建每范围级别一个隐藏的类,它需要捕捉变量,并捕获从范围内的所有相关变量。 我相信这是为简单起见完成,但它确实可以增加一些对象的生存不必要的。

这将是更优雅每个匿名方法只能捕捉到它真正感兴趣的变量。但是,这可能使生活复杂得多......如果一个匿名方法捕获xy ,一个捕获x和一个捕捉y ,你倒是需要三个类:一个用于捕获x ,一个用于捕获y ,和一个用于构成两个(但只是具有两个变量)。 棘手的一点是,任何单变量实例,该变量需要住在恰好一个位置,这是指它的一切看到相同的值,不管更改它。

这并不违反任何方式的规范,但它可以被认为是不幸的 - 我不知道是否它实际上咬在现实生活中的人,但它肯定可以的。

好消息是,如果C#团队决定要改善这一点,他们应该能够在一个完全向后兼容的方式这样做,除非有提线木偶是依靠生命周期被不必要地延长。



Answer 2:

乔恩当然是正确的。 这通常会导致的问题是:

void M()
{
    Expensive e = GetExpensive();
    Cheap c = GetCheap();
    D longLife = ()=>...c...;
    D shortLife = ()=>...e...;
    ...
}

因此,我们有一个昂贵的资源,其寿命现在依赖于长寿命的寿命,即使shortLife及早收集。

这是不幸的,但常见。 在JScript和VB倒闭的实现有同样的问题。

我想解决它在C#中的假想未来的版本,但我不作任何保证。 最明显的方式做到这一点是确定在此基础上的lambda它们被捕获封闭了变量的等价类,并生成封闭班每一个等价类,而不是一个单一的封闭类。

此外,还可能有东西,我们可以用什么封闭在变量被写入分析做。 作为乔恩指出,我们目前我们需要捕捉变量,而不是值的限制。 我们可以在我们的代码生成策略更加灵活,如果我们确定那些从来不写在创建关闭后变量,使那些进入封闭了值,而不是封闭了变数。



文章来源: Discrete Anonymous methods sharing a class?