罗斯林 C#6?运算符编译在泛型中看起来很乱
Roslyn C#6 ?. operator compilation looks messy in generic types
让我们考虑以下 class 定义:
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
可以是 class
或 struct
;
c2() == c1(t)
必须始终为真。
- 我正在使用 http://tryroslyn.azurewebsites.net/ 站点编译一些代码片段并查看 Roslyn 发出的内容。
现在,让我们分析一下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
版本中,Equals
在 obj
的副本上被调用。在 c2
版本中,必须格外小心地调用 t
上的 Equals
,而不是 t
的副本。那是因为 T
可能是一个值类型,它覆盖了 Equals
以修改它自己的实例数据。由于您在 t
上调用 Equals
,所以修改应该在 t
.
中可见
只有c1
可以优化 因为在方法返回后任何人都无法检查obj
,所以它不会无论是 obj
还是 obj
的副本都可能被修改。
让我们考虑以下 class 定义:
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
可以是class
或struct
;c2() == c1(t)
必须始终为真。- 我正在使用 http://tryroslyn.azurewebsites.net/ 站点编译一些代码片段并查看 Roslyn 发出的内容。
现在,让我们分析一下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
版本中,Equals
在 obj
的副本上被调用。在 c2
版本中,必须格外小心地调用 t
上的 Equals
,而不是 t
的副本。那是因为 T
可能是一个值类型,它覆盖了 Equals
以修改它自己的实例数据。由于您在 t
上调用 Equals
,所以修改应该在 t
.
只有c1
可以优化 因为在方法返回后任何人都无法检查obj
,所以它不会无论是 obj
还是 obj
的副本都可能被修改。