Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일

(시리즈 글이 9개 있습니다.)
닷넷: 2275. C# 13 - (1) 신규 이스케이프 시퀀스 '\e'
; https://www.sysnet.pe.kr/2/0/13673

닷넷: 2277. C# 13 - (2) 메서드 그룹의 자연 타입 개선 (메서드 추론 개선)
; https://www.sysnet.pe.kr/2/0/13681

닷넷: 2286. C# 13 - (3) Monitor를 대체할 Lock 타입
; https://www.sysnet.pe.kr/2/0/13699

닷넷: 2287. C# 13 - (4) Indexer를 이용한 개체 초기화 구문에서 System.Index 연산자 허용
; https://www.sysnet.pe.kr/2/0/13701

닷넷: 2291. C# 13 - (5) params 인자 타입으로 컬렉션 허용
; https://www.sysnet.pe.kr/2/0/13705

닷넷: 2294. C# 13 - (6) iterator 또는 비동기 메서드에서 ref와 unsafe 사용을 부분적으로 허용
; https://www.sysnet.pe.kr/2/0/13710

닷넷: 2303. C# 13 - (7) ref struct의 interface 상속 및 제네릭 제약으로 사용 가능
; https://www.sysnet.pe.kr/2/0/13752

닷넷: 2304. C# 13 - (8) 부분 메서드 정의를 속성 및 인덱서에도 확대
; https://www.sysnet.pe.kr/2/0/13754

닷넷: 2305. C# 13 - (9) 메서드 바인딩의 우선순위를 지정하는 OverloadResolutionPriority 특성 도입 (Overload resolution priority)
; https://www.sysnet.pe.kr/2/0/13755




C# 13 - 메서드 그룹의 자연 타입 개선 (메서드 추론 개선)

"Language Feature Status"의 2번째 목록에 있는 기능입니다.

Method group natural type
; https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13#method-group-natural-type

Method group natural type improvements
; https://github.com/dotnet/csharplang/blob/main/proposals/method-group-natural-type-improvements.md

Proposed change to method group natural type #7364
; https://github.com/dotnet/csharplang/issues/7364

이것 역시 17.9 preview 2 버전에 구현됐지만, Visual Studio 2022에서도 <LangVersion>preview</LangVersion>을 설정하면 테스트가 가능합니다.




사전 지식으로 "natural type"에 대한 설명을 지난 글에 미리 정리했으니 참고하시면 도움이 될 것입니다.

C# - Method Group, Natural Type, function_type
; https://www.sysnet.pe.kr/2/0/13680

하지만, 엄밀히는 몰라도 상관없습니다. 간단하게 그냥, C# 코드에서 메서드 호출 시 실제 메서드와 연결하는 과정이 좀 더 정확해졌다고 생각하셔도 됩니다.

그리고, 이러한 판정 기준은 지속적으로 개선돼 왔습니다.

C# 6.0 - 재정의된 메서드의 선택 정확도를 향상
; https://www.sysnet.pe.kr/book/cs12_free.pdf (68 페이지)

C# 7.3 - 개선된 메서드 선택 규칙 3가지(Improved overload candidates)
; https://www.sysnet.pe.kr/2/0/11553

이제 C# 13에서는, 다음의 2가지 규칙을 더 추가해 이전보다 더 향상된 메서드 판정을 할 수 있게 된 것입니다.

  • 처음부터 method group에 모든 후보 메서드를 포함시키지 않고, 범위에 따라(scope-by-scope) 추가
  • 가능하지 않은 메서드는 후보에서 처음부터 제외해 이후 판정에 영향을 끼치지 못함, 예를 들어 1) 타입 인자가 없는 경우 제네릭 메서드는 후보에서 제외, 2) 제네릭 제약 또는 타입 인자의 수를 만족하지 않는 제네릭 메서드도 후보에서 제외

위의 규칙에서 "범위"란 메서드의 인스턴스/정적 멤버 등의 유형으로 간주하시면 됩니다. 즉, 첫 번째 규칙의 경우 우선적으로 인스턴스 메서드를 대상으로 모든 후보를 method group에 포함시켜 타입 추론을 시도하고, 만약 그 범위에 속한 메서드가 없다면 이후 범위를 확장 메서드 등으로 넓혀가며 타입 추론을 시도하는 것입니다.

이에 대한 예제가 "Proposed change to method group natural type #7364" 이슈에 나오는데요, 가령 C# 12 이하에서는 다음의 코드가 컴파일되지 않습니다.

internal class Program
{
    static void Main(string[] args)
    {
        {
            var c = new MyType();
            System.Action x = c.Call; // 컴파일 가능
            var x2 = c.Call; // 타입 추론 불가능 error CS8917: The delegate type could not be inferred.
        }

        {
            var c = new MyType();
            System.Action<object> y = c.Call; // 컴파일 가능
            var y2 = c.Call; // 타입 추론 불가능 error CS8917: The delegate type could not be inferred.
        }
    }
}

public class MyType
{
    public void Call() { }
}

public static class ExtensionType
{
    public static void Call(this MyType c, object o) { }
}

이유는 간단합니다. "Call" method group에는 현재 2개의 메서드, "void Call()"과 "ExtensionType.Call(this MyType c, object o)"가 있지만, 서로 Signature가 달라 "자연 타입"을 가질 수 없습니다. 따라서, 타입 추론이 불가능해 오류가 발생하는 것입니다.

하지만, C# 13부터는 우선 인스턴스 메서드를 method group에 포함해 타입 추론을 시도합니다. 위의 경우 첫 번째 "var x2 = c.Call"은 "void Call()"만을 포함한 method group에 대해서는 signature가 일치하므로 타입 추론을 적용해 컴파일을 합니다.

두 번째 경우도 결국엔 첫 번째 경우와 같습니다. 단지 두 번째 경우는 사용자가 "Action<object>" 타입으로 받고 싶었을 수도 있습니다. 만약 그런 경우라면, 그냥 컴파일러의 추론 대신 타입을 명시하면 그만입니다.




위의 첫 번째 규칙을 이해했으면, 또 다른 scope의 대상인 제네릭 메서드에 대한 method group 구성도 쉽게 이해할 수 있습니다. 위에서 예를 든 것과 유사한 방식으로 이렇게 2개의 메서드를 작성하고,

public class MyType
{
    public void GenericMethod<T>(MyType c) { }
}

public static class ExtensionType
{
    public static void GenericMethod<T, U>(this MyType c) { }
}

C# 12 이전에 컴파일하는 경우 var 추론에서 예외가 발생합니다.

System.Action x = new MyType().GenericMethod<int, int>;
x();

// error CS8917: The delegate type could not be inferred.
var z = new MyType().GenericMethod<int, int>;
z();

왜냐하면, GenericMethod method group에는 2개의 메서드가 포함돼 있지만 서로 다른 타입 인자의 수로 "자연 타입"이 없어 타입 추론이 불가능한 경우로 판단했기 때문입니다.

하지만, C# 13에서는 우선 arity(매개변수의 수, 제네릭의 경우 타입 인자의 수)가 맞지 않는 제네릭 메서드는 제외해 method group을 구성하기 때문에 "var z = new MyType().GenericMethod<int, int>;"의 소스코드에 대해서 2개의 타입 인자를 개발자가 명시했으므로 (1개의 타입 인자를 갖는) MyType.GenericMethod는 제외가 된 것입니다. 결국, 1개의 메서드를 가진 method group이 되고 "자연 타입"이 있으므로 var 추론을 할 수 있게 됩니다.




정리해 보면, method group이나 자연 타입과 같은 기존 C# 문법에서 쉽게 찾아볼 수 없었던 생소한 단어로 제목("Method group natural type improvements")이 구성돼 이야기가 어렵게 되었지만, 한 마디로 그냥 var 추론에 대한 정확도가 높아졌다고 이해하시면 됩니다. ^^




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







[최초 등록일: ]
[최종 수정일: 8/6/2024]

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

비밀번호

댓글 작성자
 




... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11812정성태2/11/201915722오류 유형: 511. Windows Server 2003 VM 부팅 후 로그인 시점에 0xC0000005 BSOD 발생
11811정성태2/11/201921057오류 유형: 510. 서버 운영체제에 NVIDIA GeForce Experience 실행 시 wlanapi.dll 누락 문제
11810정성태2/11/201918613.NET Framework: 808. .NET Profiler - GAC 모듈에서 GAC 비-등록 모듈을 참조하는 경우의 문제
11809정성태2/11/201920783.NET Framework: 807. ClrMD를 이용해 메모리 덤프 파일로부터 특정 인스턴스를 참조하고 있는 소유자 확인
11808정성태2/8/201922119디버깅 기술: 123. windbg - 닷넷 응용 프로그램의 메모리 누수 분석
11807정성태1/29/201920016Windows: 156. 가상 디스크의 용량을 복구 파티션으로 인해 늘리지 못하는 경우 [4]
11806정성태1/29/201919653디버깅 기술: 122. windbg - 덤프 파일로부터 PID와 환경 변수 등의 정보를 구하는 방법
11805정성태1/28/201921828.NET Framework: 806. C# - int []와 object []의 차이로 이해하는 제네릭의 필요성 [4]파일 다운로드1
11804정성태1/24/201919670Windows: 155. diskpart - remove letter 이후 재부팅 시 다시 드라이브 문자가 할당되는 경우
11803정성태1/10/201918590디버깅 기술: 121. windbg - 닷넷 Finalizer 스레드가 멈춰있는 현상
11802정성태1/7/201920243.NET Framework: 805. 두 개의 윈도우를 각각 실행하는 방법(Windows Forms, WPF)파일 다운로드1
11801정성태1/1/201921560개발 환경 구성: 427. Netsh의 네트워크 모니터링 기능 [3]
11800정성태12/28/201820629오류 유형: 509. WCF 호출 오류 메시지 - System.ServiceModel.CommunicationException: Internal Server Error
11799정성태12/19/201822419.NET Framework: 804. WPF(또는 WinForm)에서 UWP UI 구성 요소 사용하는 방법 [3]파일 다운로드1
11798정성태12/19/201821254개발 환경 구성: 426. vcpkg - "Building vcpkg.exe failed. Please ensure you have installed Visual Studio with the Desktop C++ workload and the Windows SDK for Desktop C++"
11797정성태12/19/201817244개발 환경 구성: 425. vcpkg - CMake Error: Problem with archive_write_header(): Can't create '' 빌드 오류
11796정성태12/19/201817576개발 환경 구성: 424. vcpkg - "File does not have expected hash" 오류를 무시하는 방법
11795정성태12/19/201820869Windows: 154. PowerShell - Zone 별로 DNS 레코드 유형 정보 조회 [1]
11794정성태12/16/201816932오류 유형: 508. Get-AzureWebsite : Request to a downlevel service failed.
11793정성태12/16/201819505개발 환경 구성: 423. NuGet 패키지 제작 - Native와 Managed DLL을 분리하는 방법 [1]
11792정성태12/11/201819187Graphics: 34. .NET으로 구현하는 OpenGL (11) - Per-Pixel Lighting파일 다운로드1
11791정성태12/11/201819233VS.NET IDE: 130. C/C++ 프로젝트의 시작 프로그램으로 .NET Core EXE를 지정하는 경우 닷넷 디버깅이 안 되는 문제 [1]
11790정성태12/11/201817742오류 유형: 507. Could not save daemon configuration to C:\ProgramData\Docker\config\daemon.json: Access to the path 'C:\ProgramData\Docker\config' is denied.
11789정성태12/10/201831436Windows: 153. C# - USB 장치의 연결 및 해제 알림을 위한 WM_DEVICECHANGE 메시지 처리 [2]파일 다운로드2
11788정성태12/4/201817650오류 유형: 506. SqlClient - Value was either too large or too small for an Int32.Couldn't store <2151292191> in ... Column
11787정성태11/29/201821824Graphics: 33. .NET으로 구현하는 OpenGL (9), (10) - OBJ File Format, Loading 3D Models파일 다운로드1
... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...