Microsoft MVP성태의 닷넷 이야기
.NET Framework: 2031. C# 11 - 사용자 정의 checked 연산자 [링크 복사], [링크+제목 복사],
조회: 17003
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 18개 있습니다.)
.NET Framework: 1110. C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (DIM for Static Members)
; https://www.sysnet.pe.kr/2/0/12814

.NET Framework: 1118. C# 11 - 제네릭 타입의 특성 적용
; https://www.sysnet.pe.kr/2/0/12839

.NET Framework: 1182. C# 11  - ref struct에 ref 필드를 허용
; https://www.sysnet.pe.kr/2/0/13015

.NET Framework: 2025. C# 11  - 원시 문자열 리터럴(raw string literals)
; https://www.sysnet.pe.kr/2/0/13085

.NET Framework: 2026. C# 11 - 문자열 보간 개선 2가지
; https://www.sysnet.pe.kr/2/0/13086

.NET Framework: 2030. C# 11 - UTF-8 문자열 리터럴
; https://www.sysnet.pe.kr/2/0/13096

.NET Framework: 2031. C# 11 - 사용자 정의 checked 연산자
; https://www.sysnet.pe.kr/2/0/13099

.NET Framework: 2032. C# 11 - shift 연산자 재정의에 대한 제약 완화 (Relaxing Shift Operator)
; https://www.sysnet.pe.kr/2/0/13100

.NET Framework: 2035. C# 11 - 새로운 연산자 ">>>" (Unsigned Right Shift)
; https://www.sysnet.pe.kr/2/0/13110

.NET Framework: 2036. C# 11 - IntPtr/UIntPtr과 nint/nuint의 통합
; https://www.sysnet.pe.kr/2/0/13111

.NET Framework: 2037. C# 11 - 목록 패턴(List patterns)
; https://www.sysnet.pe.kr/2/0/13112

.NET Framework: 2038. C# 11 - Span 타입에 대한 패턴 매칭 (Pattern matching on ReadOnlySpan<char>)
; https://www.sysnet.pe.kr/2/0/13113

.NET Framework: 2042. C# 11 - 파일 범위 내에서 유효한 타입 정의 (File-local types)
; https://www.sysnet.pe.kr/2/0/13117

.NET Framework: 2045. C# 11 - 메서드 매개 변수에 대한 nameof 지원
; https://www.sysnet.pe.kr/2/0/13122

.NET Framework: 2046. C# 11 - 멤버(속성/필드)에 지정할 수 있는 required 예약어 추가
; https://www.sysnet.pe.kr/2/0/13123

.NET Framework: 2048. C# 11 - 구조체 필드의 자동 초기화(auto-default structs)
; https://www.sysnet.pe.kr/2/0/13125

.NET Framework: 2049. C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용
; https://www.sysnet.pe.kr/2/0/13126

.NET Framework: 2102. C# 11 - ref struct/ref field를 위해 새롭게 도입된 scoped 예약어
; https://www.sysnet.pe.kr/2/0/13276




C# 11 - 사용자 정의 checked 연산자

(Visual Studio 2022 17.3 이후 버전에서 테스트할 수 있습니다.)

현재 overflow 체크는 다음의 동작에서 영향을 주는데요,

  • 정수형 타입에 대한 ++, --, 음수 부호 -, 4칙 연산자(+, -, *, /)
  • 정수 형식끼리의 형변환, 또는 float, double 타입을 정수형 타입에 형변환

단, 이미 만들어진 타입에 한해서만 동작을 할 뿐 사용자 정의 타입에서는 overflow 제어를 위한 checked/unchecked 구문에 참여할 수 없습니다.

예를 들어 볼까요?

checked {
    sbyte a = 127;
    a++; // Unhandled exception. System.OverflowException: Arithmetic operation resulted in an overflow.
}

위의 코드는 (컴파일 시점이 아닌) 실행 시에 System.OverflowException 예외가 발생합니다. 반면, checked 예약어를 제거하면 아무런 오류 없이 a의 값은 -128로 바뀝니다.

즉, C#은 기본적으로 overflow를 체크하지 않는 코드를 생성합니다. 이러한 기본값은 csproj의 CheckForOverflowUnderflow 옵션을 이용하면 변경할 수 있습니다.

<Project Sdk="Microsoft.NET.Sdk">

...[생략]...
        <LangVersion>preview</LangVersion>
        <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
...[생략]...

</Project>

그래서 위와 같이 설정한 후부터는 다음의 코드도 런타임 시에 System.OverflowException이 발생합니다.

sbyte a = 127;
a++; // CheckForOverflowUnderflow 옵션이 true인 경우 System.OverflowException 예외 발생

그런데, 이 상황에서도 예외가 발생하지 않도록 하는 방법이 있습니다. 바로 unchecked를 설정하는 것입니다.

unchecked {
    sbyte a = 127;
    a++; // CheckForOverflowUnderflow 옵션이 true여도, 예외 발생하지 않고 a == -128
}

대충 CheckForOverflowUnderflow, checked, unchecked 관계를 아시겠죠? ^^




그런데 위와 같은 overflow 처리 정책을 우리가 만드는 C# 클래스에서는 따를 수가 없습니다. 가령 3 바이트 정수형 타입을 정의한다고 했을 때,

public struct Int3
{
    int value;

    public Int3(int value)
    {
        this.value = value;
    }

    public static implicit operator Int3(int value) => new Int3(value);

    public static Int3 operator ++(Int3 lhs)
    {
        return new Int3(lhs.value + 1);
    }

    public override string ToString()
    {
        return $"{value}";
    }
}

이렇게 사용한다고 해서 overflow 예외가 발생할 수는 없습니다.

checked
{
    Int3 n = 0x007F_FFFF;
    n++; // n == 8388608
}

당연한 결과인데요, 이때 개발자는 checked/unchecked 문맥과 상관없이 그냥 무조건 overflow 예외를 발생하는 정도로 구현할 수는 있습니다.

public static Int3 operator ++(Int3 lhs)
{
    if (lhs.value + 1 > 8388607)
    {
        throw new OverflowException((lhs.value + 1).ToString());
    }

    return new Int3(lhs.value + 1);
}

그래서 설령 unchecked여도,

unchecked
{
    Int3 n = 0x007F_FFFF;
    n++; // 또는 CheckForOverflowUnderflow 옵션이 false여도 무조건 OverflowException 예외 발생
}

overflow 처리가 되는 것입니다.




이 문제를 해결하려면, 근본적으로 C# 컴파일러와의 연동이 필요한데 애당초 C# 컴파일러는 현재 코드 영역이 checked/unchecked 인지 파악할 수 있기 때문입니다. 그렇다면 이제 필요한 것은, C# 컴파일러가 overflow 문맥에 따라 생성할 코드도 함께 checked 또는 unchecked 유형에 맞는 것으로 고를 수 있게 해주면 됩니다. 이를 위해 C# 11부터 추가된 것이 바로 "checked" 유형의 연산자 재정의입니다. 그리하여, 위에서 정의한 Int3 구조체에 다음과 같이 2가지 overflow 문맥에 따른 연산자를 제공할 수 있고,

public static Int3 operator checked ++(Int3 lhs)
{
    if (lhs.value + 1 > 8388607)
    {
        throw new OverflowException((lhs.value + 1).ToString());
    }

    return new Int3(lhs.value + 1);
}

public static Int3 operator ++(Int3 lhs) => new Int3(lhs.value + 1);

결국 C# 컴파일러는 대상 코드 영역의 overflow 문맥에 따라 "operator checked ++" 또는 "operator ++" 호출 코드를 적절하게 선택해 빌드를 하게 됩니다.




checked 연산자 재정의가 된 경우 내부 메서드 이름에는 "Checked"가 붙습니다. 가령 위에서 정의한 ++ 연산자의 경우 원래 이름은 "op_Increment"지만 checked 버전의 경우 "op_CheckedIncrement"라는 이름으로 정의됩니다.

또한, 새롭게 추가한 연산자를 "checked operator"라고 하고 기존의 연산자를 "regular operator"라고 부릅니다. 이러한 연산자는 1개 또는 2개를 제공할 수 있지만 "checked operator" 하나만을 제공할 수는 없습니다. (이것은 C# 컴파일러에서의 제약이고, 다른 언어라면 설계에 따라 그것이 가능할 수도 있습니다. 즉, IL 언어 수준에서의 제약은 아닙니다.)

public struct Int3
{
    // checked와 regular 연산자 모두 제공하면 OK (checked/unchecked 문맥에 따라 다른 메서드가 선택됨)
    public static Int3 operator checked +(Int3 lhs, Int3 rhs);
    public static Int3 operator +(Int3 lhs, Int3 rhs);

    // 혹은, regular 연산자 하나만 제공해도 OK (기존처럼 동작)
    public static Int3 operator -(Int3 lhs, Int3 rhs);

    // 하지만 checked 연산자 하나만 제공하는 경우 컴파일 에러
    // error CS9025: The operator 'Int3.operator checked ++(Int3)' requires a matching non-checked version of the operator to also be defined
    public static Int3 operator checked *(Int3 lhs, Int3 rhs);
}

아래의 코드는 부호 연산자 -, 4칙 연산자 *, 증가 연산자 ++를 재정의한 Int3 타입의 예를 보여줍니다.

public struct Int3
{
    public static Int3 MaxValue = new Int3(8388607);
    public static Int3 MinValue = new Int3(-8388608);

    int value;

    public Int3(int value) : this(value, false) { }

    public Int3(int value, bool checkOverflow)
    {
        int checkValue = value;

        if (value > 8388607)
        {
            checkValue = -(value & 0x00FF_FFFF);
        }
        else if (value < -8388608)
        {
            checkValue = value & 0x007F_FFFF;
        }

        if (checkOverflow == true)
        {
            if (value != checkValue)
            {
                throw new OverflowException($"{checkValue}");
            }
        }

        this.value = checkValue;
    }

    public static implicit operator Int3(int value) => new Int3(value);

    public static Int3 operator checked ++(Int3 lhs) => new Int3(lhs.value + 1, true);
    public static Int3 operator ++(Int3 lhs) => new Int3(lhs.value + 1);

    public static Int3 operator checked *(Int3 lhs, Int3 rhs) => new Int3(lhs.value * rhs.value, true);
    public static Int3 operator *(Int3 lhs, Int3 rhs) => new Int3(lhs.value * rhs.value);

    public static Int3 operator checked -(Int3 lhs) => new Int3(-lhs.value, true);
    public static Int3 operator -(Int3 lhs) => new Int3(-lhs.value);


    public override string ToString()
    {
        return $"{value}";
    }
}

(첨부 파일은 이 글의 예제 코드를 포함합니다.)

구현 가능한 checked 버전의 연산자는 ++, --, 음수 부호 연산자(-), 사칙 연산자(+, -, *, /)입니다.

마지막으로, 위의 코드를 구현하면서 "implicit operator checked" 형변환 연산자를 재정의할 수 있다면 "public Int3(int value, bool checkOverflow) {...}" 생성자를 굳이 구현할 필요가 없어 편리하겠다는 생각을 했는데요, 이에 대해 C# 개발자들은 다음과 같이 답변하고 있습니다.

질문: (Resolved) Should we support implicit checked conversion operators?

답변: In general, implicit conversion operators are not supposed to throw.

Proposal: No.

Resolution: Approved - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-07.md#checked-implicit-conversions

또한 위의 링크에 더 자세한 답변이 나옵니다. ^^

Checked implicit conversions?

We clarified why conversions cannot be implicit and checked. Generally, implicit conversions should not fail, throw or be lossy. That's already the case with (most) existing conversions and .NET guidelines. There's an exception: integer to floating point conversion in C# does allow loss.

Conclusion: Stick with proposed restriction (conversions cannot be implicit and checked).





C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (공식 문서, Static Abstract Members In Interfaces C# 10 Preview)
; https://www.sysnet.pe.kr/2/0/12814

C# 11 - 제네릭 타입의 특성 적용 (공식 문서, Generic attributes)
; https://www.sysnet.pe.kr/2/0/12839

C# 11 - 사용자 정의 checked 연산자 (공식 문서, Checked user-defined operators)
; https://www.sysnet.pe.kr/2/0/13099

C# 11 - shift 연산자 재정의에 대한 제약 완화 (공식 문서, Relaxing Shift Operator)
; https://www.sysnet.pe.kr/2/0/13100

C# 11 - IntPtr/UIntPtr과 nint/unint의 통합 (공식 문서, Numeric IntPtr)
; https://www.sysnet.pe.kr/2/0/13111

C# 11 - 새로운 연산자 ">>>" (Unsigned Right Shift) (공식 문서, Unsigned right shift operator)
; https://www.sysnet.pe.kr/2/0/13110

C# 11 - 원시 문자열 리터럴 (공식 문서, raw string literals)
; https://www.sysnet.pe.kr/2/0/13085

C# 11 - 문자열 보간 개선 2가지 (공식 문서, Allow new-lines in all interpolations)
; https://www.sysnet.pe.kr/2/0/13086

C# 11 - 목록 패턴 (공식 문서, List patterns)
; https://www.sysnet.pe.kr/2/0/13112

C# 11 - Span 타입에 대한 패턴 매칭 (공식 문서, Pattern matching on ReadOnlySpan<char>)
; https://www.sysnet.pe.kr/2/0/13113

C# 11 - Utf8 문자열 리터럴 지원 (공식 문서, Utf8 Strings Literals)
; https://www.sysnet.pe.kr/2/0/13096

C# 11 - ref struct에 ref 필드를 허용 (공식 문서, ref fields)
; https://www.sysnet.pe.kr/2/0/13015

C# 11 - 파일 범위 내에서 유효한 타입 정의 (공식 문서, File-local types)
; https://www.sysnet.pe.kr/2/0/13117

C# 11 - 메서드 매개 변수에 대한 nameof 지원 (공식 문서, nameof(parameter))
; https://www.sysnet.pe.kr/2/0/13122

C# 11 - 멤버(속성/필드)에 지정할 수 있는 required 예약어 추가 (공식 문서, Required members)
; https://www.sysnet.pe.kr/2/0/13123

C# 11 - 구조체 필드의 자동 초기화 (공식 문서, auto-default structs)
; https://www.sysnet.pe.kr/2/0/13125

C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용 (공식 문서, Cache delegates for static method group)
; https://www.sysnet.pe.kr/2/0/13126

Language Feature Status
; https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 5/5/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 106  107  108  109  110  111  112  113  114  115  [116]  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11025정성태8/12/201622356개발 환경 구성: 294. .NET Core 프로젝트에서 "Copy to Output Directory" 처리 [1]
11024정성태8/12/201621663오류 유형: 350. "nProtect GameMon" 실행 중에는 Visual Studio 디버깅이 안됩니다! [1]
11023정성태8/10/201623202개발 환경 구성: 293. Azure 구독 후 PaaS 서비스 만들어 보기
11022정성태8/10/201623858개발 환경 구성: 292. Azure Cloud Service 배포시 사용자 정의 작업을 추가하는 방법
11021정성태8/10/201620891오류 유형: 349. System.Runtime.Remoting.RemotingException - Type '..., ..., Version=..., Culture=neutral, PublicKeyToken=null' is not registered for activation [2]
11020정성태8/10/201623628VC++: 98. 원본과 대상 버퍼가 같은 경우 memcpy, wmemcpy 주의점
11019정성태8/10/201640290기타: 60. 도서: 시작하세요! C# 6.0 프로그래밍: 기본 문법부터 실전 예제까지 (2쇄 정오표)
11018정성태8/9/201624762.NET Framework: 600. 단일 메서드 내에서의 할당으로 알아보는 자바와 닷넷의 GC 차이점 [1]
11017정성태8/9/201626849웹: 33. HTTP 쿠키에 한글 값을 설정하는 방법
11016정성태8/7/201624029개발 환경 구성: 291. Windows Server Containers 소개
11015정성태8/7/201622274오류 유형: 348. Windows Server 2016 TP5에서 Windows Containers의 docker run 실행 시 encountered an error during Start failed in Win32
11014정성태8/6/201623065오류 유형: 347. Hyper-V Virtual Machine Management service Account does not have permission to open attachment
11013정성태8/6/201633857개발 환경 구성: 290. Windows 10에서 경험해 보는 Windows Containers와 docker [4]
11012정성태8/6/201623913오류 유형: 346. Windows 10에서 Windows Containers의 docker run 실행 시 encountered an error during CreateContainer failed in Win32 발생
11011정성태8/6/201625541기타: 59. outlook.live.com 메일 서비스의 아웃룩 POP3 설정하는 방법
11010정성태8/6/201622883기타: 58. Outlook에 설정한 SMTP/POP3(예:천리안 메일) 계정 암호를 잊어버린 경우
11009정성태8/3/201628079개발 환경 구성: 289. 2016-08-02부터 시작된 윈도우 10 1주년 업데이트에서 Bash Shell 사용 [8]
11008정성태8/1/201621917오류 유형: 345. 2의 30승 이상의 원소를 갖는 경우 버그가 발생하는 이진 검색(Binary Search) 코드
11007정성태8/1/201623634오류 유형: 344. RDP ActiveX 컨트롤로 특정 PC에 연결할 수 없을 때, 오류 상황을 해결하기 위한 팁파일 다운로드1
11006정성태7/22/201626596개발 환경 구성: 288. SSL 인증서를 Azure Cloud Service에 적용하는 방법
11005정성태7/22/201625251개발 환경 구성: 287. Let's Encrypt 인증서 업데이트 주기: 90일
11004정성태7/22/201620092오류 유형: 343. Invalid service definition or service configuration. Please see the Error List for more details.
11003정성태7/20/201627374VS.NET IDE: 110. Visual Studio 2015에서 .NET Core 응용 프로그램 개발 [1]
11002정성태7/20/201620851개발 환경 구성: 286. Microsoft Azure 서비스의 구독은 반드시 IE로!
11001정성태7/19/201631933.NET Framework: 599. .NET Core/SDK 설치 및 기본 사용법 [6]
11000정성태7/16/201620624오류 유형: 342. Microsoft Visual Studio 2010 Tools for Office Runtime (x86 and x64) 설치 시 오류
... 106  107  108  109  110  111  112  113  114  115  [116]  117  118  119  120  ...