c# - 共享一个类的离散匿名方法?

标签 c# delegates anonymous-methods

我在玩 Eric Lippert 的 Ref<T>来自 here 的类(class).我在 IL 中注意到,看起来两个匿名方法都在使用相同的生成类,即使这意味着该类有一个额外的变量。

虽然只使用一个新的类定义似乎有点合理,但令我惊讶的是,只有一个 <>c__DisplayClass2 的实例被 build 。这似乎暗示 Ref<T> 的两个实例引用相同的 <>c__DisplayClass2那不是说y吗?直到 vart1 才能收集被收集,这可能比 joik 之后发生得晚得多返回?毕竟,不能保证某些白痴不会编写直接访问 y 的函数(直接在 IL 中)。通过vart1之后 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 的公共(public)字段。 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

最佳答案

是的,匿名方法的 MS 实现有效地为每个范围级别创建了一个隐藏类,它需要从中捕获变量,并从该范围中捕获所有相关变量。我相信这样做是为了简单起见,但它确实会不必要地增加某些对象的生命周期。

如果每个匿名方法捕获它实际感兴趣的变量,会更优雅。然而,这可能会使生活相当变得更复杂......如果一个匿名方法捕获了 xy,一个捕获了 x 一个捕获了 y,你需要三个类: 一种用于捕获 x,一种用于捕获 y,一种用于组合两者(但只有两个变量)。棘手的一点是,对于任何单个变量实例化,该变量需要恰好存在于一个位置,以便所有引用它的东西都看到相同的值,无论它发生什么变化。

这并没有以任何方式违反规范,但它可以被认为是不幸的——我不知道它是否真的在现实生活中被咬伤,但它肯定是可能的。

好消息是,如果 C# 团队决定对此进行改进,他们应该能够以完全向后兼容的方式进行,除非某些木偶依赖不必要地延长生命周期。

关于c# - 共享一个类的离散匿名方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3885106/

相关文章:

c# - 如何使用c#监控windows服务

c# - 缺少小数点分隔符

c# - 有没有办法在 C# 中简化这个反射委托(delegate)代码?

C# 抽象类,使用匿名而不是声明具体类?

c# - 无法使用 C# 访问 app.config 中的连接字符串

c# - 由带有索引的二维数组构建的具有 x 列的数据表?

swift - 委托(delegate)的函数返回 nil

swift - 弃用 Swift 3 升级协议(protocol)

java - 从匿名声明中重写的方法获取代码

c# - 使用 .NET + Linq 创建项目列表时是否可以使用匿名方法?