C#编译器会优化变量吗?

mik*_*ike 4 c# multithreading compiler-optimization

这是我的帖子的后续内容,这是并发可观察集合的正确实现吗?

在那篇文章中,我有一个自定义类,它实现了通用并发可观察列表,包括IEnumerable<T>.GetEnumerator(). 这是原来的代码:

public IEnumerator<T> GetEnumerator()
{
    var localSnapshot = _snapshot; //create local variable to protect enumerator, if class member (_snapshot) should be changed/replaced while iterating
    return ((IEnumerable<T>)localSnapshot).GetEnumerator();
}
Run Code Online (Sandbox Code Playgroud)

作为_snapshot私有字段,每当修改实际内部集合 ( ) 时Array,都会使用 a 重建。lock()List<T> _list

但现在我认为该localSnapshot变量根本不需要,代码应该是:

public IEnumerator<T> GetEnumerator()
{
    return ((IEnumerable<T>)_snapshot).GetEnumerator();
}
Run Code Online (Sandbox Code Playgroud)

因为localSnapshot只是将引用分配给所_snapshot引用的同一地址。GetEnumerator不关心(也不能告诉)使用了哪个变量(当然,为了自己的使用,会创建另一个引用同一数组的变量)。

如果我的上述假设是正确的,我想知道编译器是否会优化该变量?那么生成的代码将是相同的。或者,如果不是:理论上复制引用是否可能是“有害的”,因为副本的最新程度会低于其应有的水平(另一个线程可以在_snapshot复制完成后但在GetEnumerator调用之前刷新)?而且,这个“副作用”是编译器不优化代码的原因吗——因为优化是“无副作用”的?

Kev*_*sse 5

两个版本的代码编译后将产生相同的结果。正如 TheGeneral 在评论中指出的那样,确定的一个好方法是检查Sharplab.io

请注意,只有在发布模式下编译时才会出现这种情况。如果您在调试模式下编译,那么编译器将假设您可能需要中间变量用于调试目的,并且不会对其进行优化。

从理论上讲,复制引用甚至可能是“有害的”,因为副本的最新程度可能低于其应有的水平(另一个线程可以在复制完成后、调用 GetEnumerator 之前刷新 _snapshot)

var localSnapshot = _snapshot;如果您在和之间执行一些代码,就会出现这种情况return ((IEnumerable<T>)localSnapshot).GetEnumerator()。在这种情况下,优化是不可能的。否则,在这两种情况下,您都会读取该值并直接使用它。两个版本的代码在“新鲜度”上没有区别。