C#编译器和局部变量的缓存

时间:2009-01-15 10:43:18

标签: c# optimization compiler-construction

编辑:糟糕 - 正如所指出的那样,无法知道相关类的构造函数是否对其调用的次数或次数敏感,或者是否对象的状态在方法期间发生了变化,因此每次都必须从头开始创建。忽略字典,只考虑在方法过程中在线创建的代理: - )


假设我有以下方法使用Dictionary of Type to Action局部变量。

void TakeAction(Type type)
{
    // Random types chosen for example.
    var actions = new Dictionary<Type, Action>()
    {
        {typeof(StringBuilder), () =>
            {
                // ..
            }},
         {typeof(DateTime), () =>
            {
                // ..
            }}
    };

    actions[type].Invoke();
}

调用方法时,Dictionary总是一样的。 C#编译器可以注意到这一点,只创建一次并将其缓存到某个地方,以便将来调用该方法吗?或者它每次只是从头开始创建?我知道它可能是包含类的一个字段,但是对于我来说这样的东西似乎更适合包含在使用它的方法中。

4 个答案:

答案 0 :(得分:7)

每次C#编译器应该如何知道它是“相同的”字典?您每次都明确地创建一个新的字典。 C#不支持静态局部变量,因此您必须使用字段。即使没有其他方法使用该字段,也没有错。

如果C#编译器做了那样的事情会很糟糕。如果变量的构造函数使用随机输入怎么办? :)

答案 1 :(得分:7)

简答:不。

稍微长一点的回答:我相信它会缓存从lambda表达式创建委托的结果,该表达式不捕获任何东西(包括“this”),但这是一个非常特殊的情况。

更正代码的正确方法:为字典声明私有静态只读变量。

private static readonly Dictionary<Type,Action> Actions = 
    new Dictionary<Type, Action>()
{
    { typeof(StringBuilder), () => ... },
    { typeof(DateTime), () => ... },
}

void TakeAction(Type type)
{
    Actions[type].Invoke();
}

答案 2 :(得分:2)

对于任何能够执行此操作的编译器,它必须有某种方法来保证以下问题:

  • 以完全相同的方式构造两个对象以任何方式产生相同的对象,除了它们在内存中的位置。 这意味着第二次构造的对象与第一次构建的对象没有区别,相反,缓存随机类型的对象。
  • 与对象交互不会改变其状态。 这意味着缓存对象将是安全的,不会改变后续调用的行为。例如,这将排除以任何方式修改字典。

这样做的原因是编译器必须能够保证第一次构造的对象下次可以同样使用,而不必重新创建它。

现在,C#和.NET没有制作这些保证的机制可能不是编译器不支持这种优化的原因,但这些必须首先实现。编译器可能还需要有其他此类保证才能完成我不知道的事情。

Jon Skeet建议的更改基本上是说我知道这两个保证适用于我的代码的方式,因此您可以自己控制情况。

答案 3 :(得分:1)

每次都会重新创建该词典;否则,例如,你可以将其他东西放入字典中,意图就会丢失。