C #에서 권투가 발생하는 모든 상황을 수집하려고합니다.
-
값 유형을
System.Object
유형으로 변환 :struct S { } object box = new S();
-
값 유형을
System.ValueType
유형으로 변환 :struct S { } System.ValueType box = new S();
-
열거 유형의 값을 유형으로 변환
System.Enum
:enum E { A } System.Enum box = E.A;
-
값 유형을 인터페이스 참조로 변환 :
interface I { } struct S : I { } I box = new S();
-
C # 문자열 연결에서 값 유형 사용 :
char c = F(); string s1 = "char value will box" + c;
참고 :
char
유형의 상수는 컴파일 타임에 연결됩니다.참고 : 버전 6.0의 C # 컴파일러 이후 를 최적화 연결이 포함
bool
,char
,IntPtr
,UIntPtr
유형 -
값 형식 인스턴스 메서드에서 대리자 만들기 :
struct S { public void M() {} } Action box = new S().M;
-
값 유형에 대해 재정의되지 않은 가상 메서드 호출 :
enum E { A } E.A.GetHashCode();
-
식에서 C # 7.0 상수 패턴 사용
is
:int x = …; if (x is 42) { … } // boxes both 'x' and '42'!
-
C # 튜플 형식 변환의 권투 :
(int, byte) _tuple; public (object, object) M() { return _tuple; // 2x boxing }
-
object
값 유형 기본값 이있는 유형의 선택적 매개 변수 :void M([Optional, DefaultParameterValue(42)] object o); M(); // boxing at call-site
-
제한되지 않은 제네릭 유형의 값 확인
null
:bool M<T>(T t) => t != null; string M<T>(T t) => t?.ToString(); // ?. checks for null M(42);
참고 : 일부 .NET 런타임에서 JIT에 의해 최적화 될 수 있습니다.
-
/ 연산자를 사용
struct
하여 제한되지 않거나 일반 유형 의 유형 테스트 값 :is
as
bool M<T>(T t) => t is int; int? M<T>(T t) => t as int?; IEquatable<T> M<T>(T t) => t as IEquatable<T>; M(42);
참고 : 일부 .NET 런타임에서 JIT에 의해 최적화 될 수 있습니다.
당신이 알고있는 복싱의 더 많은 상황이 있습니까?
답변
좋은 질문입니다!
Boxing은 정확히 한 가지 이유로 발생합니다 . 값 유형에 대한 참조가 필요할 때 입니다. 나열한 모든 것이이 규칙에 해당합니다.
예를 들어 object는 참조 유형이므로 값 유형을 object로 캐스팅하려면 값 유형에 대한 참조가 필요하므로 boxing이 발생합니다.
가능한 모든 시나리오를 나열하려면 객체 또는 인터페이스 유형을 반환하는 메서드에서 값 유형을 반환하는 것과 같은 파생 항목도 포함해야합니다. 이렇게하면 값 유형이 객체 / 인터페이스로 자동 캐스팅되기 때문입니다.
그건 그렇고, 당신이 현명하게 식별 한 문자열 연결 사례도 캐스팅에서 객체로 파생됩니다. + 연산자는 컴파일러에 의해 문자열의 Concat 메서드에 대한 호출로 변환됩니다.이 메서드는 전달하는 값 형식에 대한 개체를 허용하므로 개체로 캐스팅되므로 권투가 발생합니다.
수년 동안 저는 항상 개발자들에게 모든 경우를 외우는 대신 권투에 대한 단일 이유 (위에서 지정했습니다)를 기억하라고 조언했습니다. 목록이 길고 기억하기 어렵 기 때문입니다. 이는 또한 컴파일러가 C # 코드에 대해 생성하는 IL 코드에 대한 이해를 촉진합니다 (예 : + on string은 String.Concat에 대한 호출을 생성 함). 컴파일러가 생성하는 내용과 복싱이 발생하면 IL 디스어셈블러 (ILDASM.exe)를 사용할 수 있습니다. 일반적으로 box opcode를 찾아야합니다 (IL에 box opcode가 포함되어 있지 않더라도 boxing이 발생할 수있는 경우가 하나뿐입니다. 자세한 내용은 아래 참조).
그러나 나는 일부 권투 사건이 덜 분명하다는 데 동의합니다. 그 중 하나를 나열했습니다. 값 유형의 재정의되지 않은 메서드 호출. 사실, 이것은 다른 이유로 덜 분명합니다. IL 코드를 확인하면 상자 opcode가 표시되지 않지만 제약 조건 opcode가 표시되므로 IL에서도 권투가 발생하는지 분명하지 않습니다! 이 답변이 더 길어지지 않도록하는 이유에 대해서는 자세히 설명하지 않겠습니다.
덜 분명한 권투의 또 다른 경우는 구조체에서 기본 클래스 메서드를 호출하는 경우입니다. 예:
struct MyValType
{
public override string ToString()
{
return base.ToString();
}
}
여기에서 ToString이 재정의되었으므로 MyValType에서 ToString을 호출하면 권투가 생성되지 않습니다. 그러나 구현은 기본 ToString을 호출하므로 권투가 발생합니다 (IL 확인!).
그건 그렇고,이 두 가지 명확하지 않은 권투 시나리오는 위의 단일 규칙에서 파생됩니다. 값 유형의 기본 클래스에서 메소드가 호출 될 때 this 키워드가 참조 할 무언가가 있어야합니다 . 값 유형의 기본 클래스는 (항상) 참조 유형이므로 this 키워드는 참조 유형을 참조해야하므로 값 유형에 대한 참조가 필요하므로 단일 규칙으로 인해 boxing이 발생합니다.
다음은 권투에 대해 자세히 설명하는 온라인 .NET 코스 섹션에 대한 직접 링크입니다. http://motti.me/mq
고급 권투 시나리오에만 관심이있는 경우 여기에 직접 링크가 있습니다 (위의 링크는 기본 항목에 대해 논의한 후에도 해당 링크로 이동합니다). http://motti.me/mu
이게 도움이 되길 바란다!
모티
답변
값 유형에서 비가 상 GetType () 메서드 호출 :
struct S { };
S s = new S();
s.GetType();
답변
Motti의 답변에서 언급되었으며 코드 샘플로 설명합니다.
관련된 매개 변수
public void Bla(object obj)
{
}
Bla(valueType)
public void Bla(IBla i) //where IBla is interface
{
}
Bla(valueType)
그러나 이것은 안전합니다.
public void Bla<T>(T obj) where T : IBla
{
}
Bla(valueType)
반환 유형
public object Bla()
{
return 1;
}
public IBla Bla() //IBla is an interface that 1 inherits
{
return 1;
}
제한되지 않은 T를 null에 대해 확인
public void Bla<T>(T obj)
{
if (obj == null) //boxes.
}
동적 사용
dynamic x = 42; (boxes)
다른 것
답변
- 의 제네릭이 아닌 컬렉션을 사용하여
System.Collections
같은
ArrayList
나HashTable
.
이는 첫 번째 사례의 특정 인스턴스이지만 숨겨진 문제 일 수 있습니다. 이들 대신 사용 오늘에서 아직도 올 코드의 양 놀라운 List<T>
와 Dictionary<TKey,TValue>
.
답변
ArrayList에 값 유형 값을 추가하면 boxing이 발생합니다.
ArrayList items = ...
numbers.Add(1); // boxing to object