C #에서 권투 발생


85

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하여 제한되지 않거나 일반 유형 의 유형 테스트 값 :isas

    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에 의해 최적화 될 수 있습니다.

당신이 알고있는 복싱의 더 많은 상황이 있습니까?


2
나는 얼마 전에 이것을 다루었 고, 이것은 매우 흥미 롭다는 것을 발견했다 : FxCop을 사용하여 (un) boxing 탐지
George Duckett

당신이 보여준 아주 좋은 변환 방법. 사실 나는 두 번째 것을 몰랐거나 시도한 적이 없을 것입니다 :) 감사합니다
Zenwalker

12
커뮤니티 위키 질문이어야합니다
Sly

2
nullable 유형은 어떻습니까? private int? nullableInteger
allansson 2011

1
@allansson, nullable 유형은 일종의 값 유형일뿐입니다
controlflow

답변:


42

좋은 질문입니다!

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

이게 도움이 되길 바란다!

모티


1
ToString()재정의하지 않는 특정 값 유형에 대해 a 가 호출되면 해당 값 유형이 호출 사이트에서 boxing되거나 메서드가 자동 생성 된 재정의로 전달됩니다 (boxing 없음). 권투) 기본 방법으로?
supercat

@supercat base값 유형 을 호출하는 모든 메서드를 호출 하면 권투가 발생합니다. 여기에는 구조체에 의해 재정의되지 않은 가상 메서드와 Object전혀 가상이 아닌 메서드 (예 :)가 포함 GetType()됩니다. 이 질문을 참조하십시오 .
Şafak Gür 2014

@ ŞafakGür : 최종 결과는 권투가 될 것이라는 것을 알고 있습니다. 나는 그것이 일어나는 정확한 메커니즘에 대해 궁금합니다. IL을 생성하는 컴파일러는 유형이 값 유형인지 참조인지 (일반적 일 수 있음) 여부를 알지 못할 수 있으므로 상관없이 callvirt를 생성합니다. JITter는 유형이 값 유형인지 여부와 ToString을 재정의하는지 여부를 알 수 있으므로 권투를 수행하는 호출 사이트 코드를 생성 할 수 있습니다. 또는 ToStringmehtod를 재정의하지 않는 모든 구조체에 대해 자동 생성 할 수 있습니다 public override void ToString() { return base.ToString(); }.
supercat

... 복싱이 해당 방법 내에서 발생하도록합니다. 방법이 매우 짧기 때문에 인라인 될 수 있습니다. 후자의 접근 방식을 사용하면 ToString()다른 것과 마찬가지로 리플렉션을 통해 구조체의 메서드에 액세스 할 수 있고 구조체 형식을 ref매개 변수 로 사용하는 정적 대리자를 만드는 데 사용할 수 있지만 [상속되지 않은 구조체 메서드에서 작동하는] 그런 대리자를 만들려고했지만 작동하지 않았습니다. 구조체의 ToString()메서드에 대한 정적 대리자를 만들 수 있습니까? 그렇다면 매개 변수 유형은 무엇이어야합니까?
supercat 2014

링크가 끊어졌습니다.
OfirD

5

값 유형에서 비가 상 GetType () 메서드 호출 :

struct S { };
S s = new S();
s.GetType();

2
GetTypeboxing은 가상이 아니기 때문이 아니라 힙 개체와 달리 값 유형 저장 위치에 GetType()유형을 식별하는 데 사용할 수 있는 "숨겨진"필드가 없기 때문에 필요합니다 .
supercat

@supercat 흠. 1. 컴파일러에 의해 추가 된 Boxing과 런타임에 사용되는 숨겨진 필드. 런타임에 대해 알고 있기 때문에 컴파일러가 권투를 추가 할 수 있습니다. 2. 열거 형 값에 대해 가상이 아닌 ToString (string)을 호출 할 때 권투도 필요하며 컴파일러가 Enum.ToString (string) 구현 세부 사항에 대해 알고 있기 때문에 이것을 추가한다고 믿지 않습니다. . 즉, "기본 값 유형"에 대한 비가 상 메서드가 호출 될 때 항상 권투가 발생했다고 말할 수 있습니다.
Viacheslav Ivanov

Enum대한 ToString()메서드는 Enum형식 정보에 액세스 할 필요가 있었지만 자체적으로 비가 상 메서드를 갖는 것을 고려하지 않았습니다 . 나는 궁금해 Object, ValueType또는 Enum유형 정보없이 작업을 수행 할 수있는 가상이 아닌 방법이있다.
supercat

3

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)

다른 것

enumValue.HasFlag


0
  • 의 제네릭이 아닌 컬렉션을 사용하여 System.Collections같은 ArrayListHashTable.

이는 첫 번째 사례의 특정 인스턴스이지만 숨겨진 문제 일 수 있습니다. 이들 대신 사용 오늘에서 아직도 올 코드의 양 놀라운 List<T>Dictionary<TKey,TValue>.


0

ArrayList에 값 유형 값을 추가하면 boxing이 발생합니다.

ArrayList items = ...
numbers.Add(1); // boxing to object
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.