编译器是否以相同的方式解释以下片段?

时间:2014-01-27 03:36:24

标签: c#

考虑

public List<GroupByDateType> GroupByItems
{
    get { return Enum.GetValues(typeof(GroupByDateType)).Cast<GroupByDateType>().ToList(); }
}

private List<GroupByDateType> _GroupByItems;
public List<GroupByDateType> GroupByItems
{
    get {return _GroupByItems??(_GroupByItems=Enum.GetValues(typeof(GroupByDateType)).Cast<GroupByDateType>().ToList());}
}

通常我会选择第二个,因为当我看到第一个时,在我看来列表是重新生成的,但实际上会发生什么,CLR是否会为我创建支持字段?

3 个答案:

答案 0 :(得分:4)

没有。虽然编译器和抖动都可以自由地执行所有方式的优化,但通常编译器只执行那些最明显的(特别是删除死代码),而抖动往往不会做任何会增加的事情。布局或开发人员选择避免的缺点。

Memoisation通过添加字段来更改布局,并以内存为代价优化速度,因此您不希望每次都发生这种情况(如果您考虑重复调用的次数乘以计算的成本而不是值得额外的记忆使用)。除此之外,决定这种平衡涉及的只是这些代码之外的因素,例如将创建多少这样的对象(以及因此使用了多少内存)。在很多情况下,这不会是一个很好的自动更改。

还有一个事实就是记忆很容易;如果你想要的话,你已经做到了,如果你不想要它,你就不会有(事实上,当然不应该每次都有,只有当它具有真正的优势时)。

相反,作为一个可能的结果覆盖null的记忆稍微复杂一点,并且必须再次线程安全棘手(在不同的并发情况下有一些不同的方法,所有这些都很容易,但是选择哪个经常使用不是)。将“线程安全”的方法(包括属性获取器)转换为不会或者至少在并发调用面前改变对象行为的方法(“优化”)是完全不合适的。

备忘录有时也会产生细微的语义差异,例如你的例子;如果调用代码更改列表,行为会有所不同。这可能是至关重要的(列表会被其他代码检查这些更改)或错误(下一个调用者得到一个不正确的更改列表)或不相关(它只是内部的,你不会在任何地方改变它,所以你'重新削减保护成本)。并非所有的memoisation都有这个问题(例如,可以返回一个只读列表),但它确实意味着它无法自动完成。

答案 1 :(得分:3)

不,编译器会将这两个语句完全区别对待。

在第一个语句中,您将在每个属性获取请求上重新评估List<T>。在第二个陈述中,你基本上是写这个(简写)。

private List<GroupByDateType> _GroupByItems;
public List<GroupByDateType> GroupByItems
{
    get 
    {
        if(_GroupByItems != null)
            return _GroupByItems;
        _GroupByItems=Enum.GetValues(typeof(GroupByDateType)).Cast<GroupByDateType>().ToList());
        return _GroupByItems;
    }
}

如果您查看重写的样本,这就是您的单行程序的执行方式。

基本上使用双问号(??)null coalescing operator,如果??的实例(_GroupByItems之前的对象或语句,编译器将在??之后执行代码}}) 一片空白。

如果_GroupByItems实例不为null,则返回_GroupByItems。还值得注意的是,第二个语句(在??之后)实际上是设置了_GroupByItems的实例,所以在下次调用时会返回该对象的实例。

最后,在设置实例时,所有后续调用都将使用List的实例,并且不会重新评估列表。这意味着如果列表发生更改(尽管此示例它不是枚举),您必须将实例_GroupByItems设置为null(或列表的新实例)。

希望这有帮助。

答案 2 :(得分:2)

不,没有创建支持字段,因为属性不是自动实现的属性。

private List<GroupByDateType> _GroupByItems;
public List<GroupByDateType> GroupByItems
{
    get { return _GroupByItems ?? (_GroupByItems = Enum.GetValues(typeof(GroupByDateType)).Cast<GroupByDateType>().ToList()); }
}

public List<GroupByDateType> GroupByItems2
{
    get { return Enum.GetValues(typeof(GroupByDateType)).Cast<GroupByDateType>().ToList(); }
}

如果您尝试检查生成的IL,您将只看到一个支持字段,即您在该属性的第二个版本中定义的支持字段:

enter image description here

getter只调用所有指定的方法,而不在某处存储结果:

.method public hidebysig specialname 
    instance class [mscorlib]System.Collections.Generic.List`1<valuetype Program/GroupByDateType> get_GroupByItems2 () cil managed 
{
    // Method begins at RVA 0x2052
    // Code size 26 (0x1a)
    .maxstack 8

    IL_0000: ldtoken Program/GroupByDateType
    IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_000a: call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    IL_000f: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Cast<valuetype Program/GroupByDateType>(class [mscorlib]System.Collections.IEnumerable)
    IL_0014: call class [mscorlib]System.Collections.Generic.List`1<!!0> [System.Core]System.Linq.Enumerable::ToList<valuetype Program/GroupByDateType>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
    IL_0019: ret
} // end of method Program::get_GroupByItems2

对于具有字段的字段,您将获得其他分配和空检查:

.method public hidebysig specialname 
    instance class [mscorlib]System.Collections.Generic.List`1<valuetype Program/GroupByDateType> get_GroupByItems () cil managed 
{
    // Method begins at RVA 0x2070
    // Code size 45 (0x2d)
    .maxstack 3
    .locals init (
        [0] class [mscorlib]System.Collections.Generic.List`1<valuetype Program/GroupByDateType> CS$0$0000
    )

    IL_0000: ldarg.0
    IL_0001: ldfld class [mscorlib]System.Collections.Generic.List`1<valuetype Program/GroupByDateType> Program::_GroupByItems
    IL_0006: dup
    IL_0007: brtrue.s IL_002c

    IL_0009: pop
    IL_000a: ldarg.0
    IL_000b: ldtoken Program/GroupByDateType
    IL_0010: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0015: call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    IL_001a: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Cast<valuetype Program/GroupByDateType>(class [mscorlib]System.Collections.IEnumerable)
    IL_001f: call class [mscorlib]System.Collections.Generic.List`1<!!0> [System.Core]System.Linq.Enumerable::ToList<valuetype Program/GroupByDateType>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
    IL_0024: dup
    IL_0025: stloc.0
    IL_0026: stfld class [mscorlib]System.Collections.Generic.List`1<valuetype Program/GroupByDateType> Program::_GroupByItems
    IL_002b: ldloc.0

    IL_002c: ret
} // end of method Program::get_GroupByItems