罗斯林 C#6 .运算符编译在泛型类型中看起来很混乱



让我们考虑以下类定义:

public class MyClass<T>
{
    public T t;
    public bool? c1(T obj) => obj?.Equals(null);
    public bool? c2() => t?.Equals(null);
}

毕竟,一些注意事项:

  • MyClass<T>不对T类型施加任何约束 - 因此,T可以是classstruct;
  • c2() == c1(t)必须始终正确。
  • 我使用 http://tryroslyn.azurewebsites.net/站点编译一些代码片段,看看 Roslyn 发出什么。

现在,让我们分析一下罗斯林是如何编译MyClass<T>的:

1.) c1(T)案例:

在验证 Roslyn 编译器生成的代码后,我们可以看到以下内容:

public bool? c1(T obj)
{
    return obj != null ? new bool?(obj.Equals(null)) : null;
}

2.) c2()案例:

我期望的是与c1(T)相同的代码。但是,我看到的是:

public unsafe bool? c2()
{
    T* arg_33_0 = ref this.t;
    T t = default(T);
    bool? arg_43_0;
    if (t == null)
    {
        t = this.t;
        arg_33_0 = ref t;
        if (t == null)
        {
            arg_43_0 = null;
            return arg_43_0;
        }
    }
    arg_43_0 = new bool?(arg_33_0.Equals(null));
    return arg_43_0;
}

哇,为什么要发出所有这些不必要的代码?在发布编译模式下,我们可以看到 C1 的代码大小为 39 字节,而 C2 方法的代码大小为 68 字节。这是可以优化的吗?

对于c2情况,c1 CIL 代码将是错误的。

c1版本中,Equalsobj的副本上被调用。在c2版本中,必须格外小心地在t上调用Equals,而不是t的副本。这是因为T可能是一个值类型,它已被重写Equals以修改其自己的实例数据。由于您在t上调用Equals,因此修改应该在t中可见。

优化对c1来说只是因为任何人都无法在方法返回后检查obj,因此无论是obj还是可能被修改的obj副本都无关紧要。

相关内容

  • 没有找到相关文章

最新更新