T : 클래스의 제약 조건이있는 일반적인 방법은 권투를 초래합니까? [복제하다
문제
이 질문은 이미 여기에 답이 있습니다.
- C#에서 제네릭을 사용할 때 권투 2 답변
T에 T를 제한하는 일반적인 방법이 MSIL 코드를 생성하는 권투 지침이있는 이유는 무엇입니까?
T가 참조 유형으로 제한되기 때문에 분명히이 문제에 놀랐습니다. 생성 된 코드는 권투를 수행 할 필요가 없습니다.
C# 코드는 다음과 같습니다.
protected void SetRefProperty<T>(ref T propertyBackingField, T newValue) where T : class
{
bool isDifferent = false;
// for reference types, we use a simple reference equality check to determine
// whether the values are 'equal'. We do not use an equality comparer as these are often
// unreliable indicators of equality, AND because value equivalence does NOT indicate
// that we should share a reference type since it may be a mutable.
if (propertyBackingField != newValue)
{
isDifferent = true;
}
}
생성 된 IL은 다음과 같습니다.
.method family hidebysig instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed
{
.maxstack 2
.locals init (
[0] bool isDifferent,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldarg.1
L_0004: ldobj !!T
L_0009: box !!T
L_000e: ldarg.2
L_000f: box !!T
L_0014: ceq
L_0016: stloc.1
L_0017: ldloc.1
L_0018: brtrue.s L_001e
L_001a: nop
L_001b: ldc.i4.1
L_001c: stloc.0
L_001d: nop
L_001e: ret
}
주목하십시오 상자 !! t 지침.
왜 이것이 생성 되는가?
이것을 피하는 방법?
해결책
당신은 box
인수가 참조 유형이기 때문에 box
교육은 아무것도하지 않습니다. 여전히 이상하지만 box
지시가 만들어졌습니다 (코드 생성시 게으른/더 쉬운 디자인).
다른 팁
권투가 왜 발생하는지 잘 모르겠습니다. 권투를 피하는 한 가지 가능한 방법은 권투를 사용하지 않는 것입니다. 권투없이 다시 컴파일하십시오. 전:
.assembly recomp_srp
{
.ver 1:0:0:0
}
.class public auto ansi FixedPBF
{
.method public instance void .ctor() cil managed
{
}
.method hidebysig public instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed
{
.maxstack 2
.locals init ( bool isDifferent, bool CS$4$0000)
ldc.i4.0
stloc.0
ldarg.1
ldobj !!T
ldarg.2
ceq
stloc.1
ldloc.1
brtrue.s L_0001
ldc.i4.1
stloc.0
L_0001: ret
}
}
... recomp_srp.msil 파일에 저장하면 다음과 같이 간단히 다시 컴파일 할 수 있습니다.
ildasm /dll recomp_srp.msil
그리고 그것은 내 끝에 권투없이 괜찮습니다.
FixedPBF TestFixedPBF = new FixedPBF();
TestFixedPBF.SetRefProperty<string>(ref TestField, "test2");
... 물론, 나는 그것을 보호에서 공개로 변경했는데, 다시 변경을 다시 변경하고 나머지 구현을 제공해야합니다.
나는 이것이 디자인의 의도라고 생각합니다. 당신은 특정 클래스로 t를 제한하지 않으므로 그것을 객체로 캐스팅하는 것이 가장 가능성이 높습니다. 따라서 IL에 권투가 포함 된 이유는 무엇입니까?
나는이 코드를 t : actualclass와 함께 시도 할 것입니다.
몇 가지 점에서 후속 조치. 우선,이 버그는 일반 수업 제약으로 where T : class
그리고 또한 일반적인 방법 동일한 제약 조건으로 (일반 또는 비 게 니체 클래스에서). 사용하는 (그렇지 않으면 동일한) 비 게 릭 방법에 대해서는 발생하지 않습니다. Object
대신에 T
:
// static T XchgNullCur<T>(ref T addr, T value) where T : class =>
// Interlocked.CompareExchange(ref addr, val, null) ?? value;
.locals init (!T tmp)
ldarg addr
ldarg val
ldloca tmp
initobj !T
ldloc tmp
call !!0 Interlocked::CompareExchange<!T>(!!0&, !!0, !!0)
dup
box !T
brtrue L_001a
pop
ldarg val
L_001a:
ret
// static Object XchgNullCur(ref Object addr, Object val) =>
// Interlocked.CompareExchange(ref addr, val, null) ?? value;
ldarg addr
ldarg val
ldnull
call object Interlocked::CompareExchange(object&, object, object)
dup
brtrue L_000d
pop
ldarg val
L_000d:
ret
첫 번째 예제에 몇 가지 추가 문제가 있습니다. 간단한 대신 ldnull
우리는 외부가 있습니다 initobj
초과 로컬 변수를 무의미하게 대상으로 호출하십시오 tmp
.
그러나 좋은 소식은 힌트를 주었다 여기,이 중 어느 것도 중요하지 않습니다. 위의 두 가지 예에 대해 생성 된 IL 코드의 차이에도 불구하고 X64 JIT 거의 동일한 코드를 생성합니다. 다음과 같은 결과는 .NET 프레임 워크 4.7.2입니다 릴리스 모드 "억압되지 않은"최적화로.