[c#] C #에서 권투 발생

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

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



답변

좋은 질문입니다!

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)

다른 것

enumValue.HasFlag


답변

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

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


답변

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

ArrayList items = ...
numbers.Add(1); // boxing to object


답변