성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[tree soap] 아차! f는 기억이 나는데, m은 ㅜㅜ 감사합니다!!! ^...
[정성태] 'm'은 decimal 타입의 숫자에 붙는 접미사입니다. ...
[정성태] https://lxr.sourceforge.io/ http...
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# 10 - (11) Lambda 개선</h1> <p> C# 10부터 3가지 부분에서 람다 사용법에 대한 개선이 이뤄졌습니다.<br /> <br /> <ol> <li>람다 식에 특성 사용</li> <li>반환 타입 지정 허용</li> <li>람다 식에 대한 추론 향상</li> </ol> <br /> 하나씩 살펴볼까요? ^^<br /> <br /> 이미 C# 9.0부터 로컬 함수에 특성을 허용함으로써 다음과 같은 표현이 가능했는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; GetName(); <span style='color: blue; font-weight: bold'>[Obsolete]</span> string GetName() => "Anders"; </pre> <br /> 이제는 람다 식 자체에도 특성을 지정할 수 있게 되었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Diagnostics; using System.Runtime.InteropServices; var list = new List<string> { "Anders", "Kevin" }; // 람다 식에 특성 허용 list.ForEach(<span style='color: blue; font-weight: bold'>[DebuggerHidden]</span> (elem) => Console.WriteLine(elem)); // 매개 변수에도 특성 허용 list.ForEach((<span style='color: blue; font-weight: bold'>[CustomAttr]</span> elem) => Console.WriteLine(elem)); Func<int> f1 =<span style='color: blue; font-weight: bold'>[return: MarshalAs(UnmanagedType.Bool)]</span> () => 1; Func<int> f2 =<span style='color: blue; font-weight: bold'>[return: CustomAttrAttribute]</span> () => { return 1; }; Func<int, int> f3 =<span style='color: blue; font-weight: bold'>[return: CustomAttrAttribute]</span> (arg) => { return arg + 1; }; // 특성을 적용하는 경우, 인자에 대한 괄호를 지정하지 않으면 컴파일 에러 // Error CS8916 Attributes on lambda expressions require a parenthesized parameter list. // Func<int, int> f4 =[return: CustomAttrAttribute] <span style='color: blue; font-weight: bold'>arg</span> => arg + 1;; // Func<int, int> f4 =[return: CustomAttrAttribute] static <span style='color: blue; font-weight: bold'>arg</span> => { return arg + 1; }; // AttributeTargets.Method가 지정된 특성은 람다 식에 적용 가능하지만, // Conditional 특성은 예외적으로 람다 식에 적용하면 컴파일 에러 // error CS0577: The Conditional attribute is not valid on 'lambda expression' because it is a constructor, destructor, operator, <span style='color: blue; font-weight: bold'>lambda expression</span>, or explicit interface implementation // Action<int, int> f4 = (<span style='color: blue; font-weight: bold'>[Conditional("DEBUG")]</span> static (x, y) => Debug.Assert(x == y)); public class CustomAttrAttribute : Attribute { } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 그다음, 반환 타입에 대한 명시적인 지정을 허용하면서 다음의 표현들이 가능하게 바뀌었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 람다 식에 반환값 명시 { Func<int, short> f1 = <span style='color: blue; font-weight: bold'>short</span> (x) => 1; // 특성과 마찬가지로 반환 타입을 지정한 경우, 매개변수에 대한 괄호를 지정하지 않으면 컴파일 에러 // error CS1525: Invalid expression term 'short' // Func<int, short> f = <span style='color: blue; font-weight: bold'>short x</span> => 1; MethodRefDelegate f2 = (<span style='color: blue; font-weight: bold'>ref int</span> (ref int x) => ref x); // ref 반환 타입의 경우 괄호가 없으면 컴파일 오류 // error CS8171: Cannot initialize a by-value variable with a reference // MethodRefDelegate f = ref int (ref int x) => ref x; int number = 5; f2(ref number) = 10; Console.WriteLine($"number == {number}"); Action<int> f3 = static <span style='color: blue; font-weight: bold'>void</span> (_) => { }; } public delegate ref int MethodRefDelegate(ref int arg); public class MyType<T> { public void Print() { Func<T> f = <span style='color: blue; font-weight: bold'>T</span> () => default; Console.WriteLine($"T Result: {f()}"); } } </pre> <br /> <strike>참고로 위에서 ref 반환 타입의 경우 괄호를 (현재는?) 명시해야만 컴파일이 되는데요, 이에 대해 문서에서 다음과 같이 언급하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > The parser <span style='color: blue; font-weight: bold'>should allow</span> ref return types in assignment without parentheses. Delegate d1 = <span style='color: blue; font-weight: bold'>(</span>ref int () => x<span style='color: blue; font-weight: bold'>)</span>; // ok // Visual Studio 2022 preview 3.1에서는 컴파일 오류 Delegate d2 = ref int () => x; // ok </pre> <br /> 아마도 마지막 버전에서는 개선이 될지 지켜봐야겠습니다. ^^<br /></strike> (<strong>2022-07-12 업데이트</strong>: 현재 괄호 없이 컴파일 가능)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그리고, 마침내 람다 식에 대해서도 타입 추론이 적용되었습니다. 사실, 위에서 설명한 특성 지정이나 반환값 명시는 다소 특수한 경우여서 그다지 쓸 일이 많지는 않은데요, 반면 람다 식의 타입 추론은 꽤나 자주 쓸 수 있는 기능입니다.<br /> <br /> 이로 인해, 위에서 지금까지 실습했던 내용들에 대해 모두 "var"를 이용해 변수로 받는 것이 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 타입 추론 { var f1 =[return: MarshalAs(UnmanagedType.Bool)] () => 1; var f2 =[return: CustomAttrAttribute] () => { return 1; }; var f3 = short (int x) => 1; var f4 = (ref int (ref int x) => ref x); var f5 =[return: CustomAttrAttribute] (int arg) => { return arg + 1; }; } </pre> <br /> 물론, 위의 추론이 가능하려면 컴파일러에게 충분한 정보를 제공해야 합니다. 가령, 다음의 경우는 각각 모호한 부분이 있어 컴파일 오류가 발생하지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 컴파일 오류 - error CS8917: The delegate type could not be inferred. var f1 = () => default; // 반환 타입의 모호 var f2 = (x) => { }; // 매개 변수 타입의 모호 var f3 = x => x; // 반환 및 매개 변수 타입의 모호 </pre> <br /> 사용자가 그에 따른 정보를 명시해 주면 정상적으로 추론이 이뤄지므로 컴파일이 잘 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var f1 = <span style='color: blue; font-weight: bold'>int</span> () => default; // 반환 타입 명시 var f2 = (<span style='color: blue; font-weight: bold'>int</span> x) => { }; // 매개 변수 타입 명시 var f3 = (<span style='color: blue; font-weight: bold'>int</span> x) => x; // 매개 변수의 타입이 명시됨으로써 반환 타입까지 추론 </pre> <br /> 그러니까, 따지고 보면 C# 10에 추가된 "람다 식의 반환 타입 지정 허용"은 결국 타입 추론을 마무리하기 위한 목적에서도 필요했을 기능입니다.<br /> <br /> <strike>그런데, 이것 역시 아직 Preview 단계여서 그런지 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/lambda-improvements'>문서의 내용과 맞지 않는 부분</a>들이 좀 있습니다. 가령, 다음의 코드는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var fs = new[] { (string s) => s.Length, (string s) => int.Parse(s) } // Func<string, int>[] </pre> <br /> 분위기로 보아 배열 요소가 Func<string, int> 타입으로 추론될 것이므로 컴파일이 될 것처럼 나오지만 실제로는 오류가 발생합니다. 그래서 다음과 같이 (아직은?) 명시를 해야만 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var fs = new <span style='color: blue; font-weight: bold'>Func<string, int>[]</span> { (string s) => s.Length, (string s) => int.Parse(s) }; </pre> </strike> <br /> 또한 아래의 예제 코드도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public static class MyEtension { static void F1() { } public static void F1<T>(this T t) { } static void F2(this string s) { } public static void Write() { var f6 = F1; // F1 메서드와 F1<T> 중 어느 것을 선택해야 할지 알 수 없으므로 당연히 컴파일 오류 // error CS8917: The delegate type could not be inferred. // 문서에서는 System.Action으로 추론한다고 되어 있지만 실제로는 컴파일 오류 // error CS8917: The delegate type could not be inferred. var f7 = "".F1; // 정상적으로 System.Action<string> 형식으로 추론 var f8 = F2; } } </pre> <br /> 문서와는 다르게 f7에 대해 컴파일 오류가 발생합니다. <br /> <br /> 이와 함께 타입 추론이 개선되어야 한다는 식의 언급도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Method type inference <span style='color: blue; font-weight: bold'>should make</span> an exact inference from an explicit lambda return type. static void F<T>(Func<T, T> f) { ... } F(int (i) => i); // Func<int, int> </pre> <br /> 즉, 위의 코드에서 F(...) 호출의 람다식이 Func<int, int>로 추론되어야 한다고 설명은 하지만 아직도 이 코드는 "error CS0411: The type arguments for method 'F<T>(Func<T, T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly."라는 컴파일 오류가 발생합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 마지막으로, 결국 람다 식의 타입 추론이 가능하기 때문에, 명시적인 인스턴스를 생성하지 않고 직접 호출하는 것도 가능하므로 이에 대해 다음과 같은 "may be" 언급을 하고 있습니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Lambda expressions <span style='color: blue; font-weight: bold'>may be invoked</span> directly. The compiler will generate a call to the underlying method without generating a delegate instance or synthesizing a delegate type. Directly invoked lambda expressions do not require explicit parameter types. </div><br /> <br /> 그래서 이렇게 호출하는 것도 가능할 거라고 하는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int zero = ((int x) => x)(0); // ok int one = (x => x)(1); // ok </pre> <br /> 아직은 아닙니다. ^^<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1856&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 10 - (1) 구조체를 생성하는 record struct (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/record-structs'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4334'>Static Abstract Members In Interfaces C# 10 Preview)</a> ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12790'>https://www.sysnet.pe.kr/2/0/12790</a> C# 10 - (2) 전역 네임스페이스 선언 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/globalusingdirective'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/3428'>Global Using Directive</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12792'>https://www.sysnet.pe.kr/2/0/12792</a> C# 10 - (3) 개선된 변수 초기화 판정 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/improved-definite-assignment'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4465'>Improved Definite Assignment</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12793'>https://www.sysnet.pe.kr/2/0/12793</a> C# 10 - (4) 상수 문자열에 포맷 식 사용 가능 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/improved-interpolated-strings'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/2951'>Constant Interpolated Strings</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12796'>https://www.sysnet.pe.kr/2/0/12796</a> C# 10 - (5) 속성 패턴의 개선 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/extended-property-patterns'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4394'>Extended property patterns</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12799'>https://www.sysnet.pe.kr/2/0/12799</a> C# 10 - (6) record class 타입의 ToString 메서드를 sealed 처리 허용 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4174'>Sealed record ToString</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12801'>https://www.sysnet.pe.kr/2/0/12801</a> C# 10 - (7) Source Generator V2 APIs (<a target='tab' href='https://github.com/dotnet/roslyn/issues/51257'>Source Generator V2 APIs</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12804'>https://www.sysnet.pe.kr/2/0/12804</a> C# 10 - (8) 분해 구문에서 기존 변수의 재사용 가능 (공식 문서, <a target='tab' href='https://github.com/dotnet/csharplang/issues/125'>Mix declarations and variables in deconstruction</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12805'>https://www.sysnet.pe.kr/2/0/12805</a> C# 10 - (9) 비동기 메서드가 사용할 AsyncMethodBuilder 선택 가능 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/async-method-builders'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/1407'>Async method builder override</a>); ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12807'>https://www.sysnet.pe.kr/2/0/12807</a> C# 10 - (10) 개선된 #line 지시자 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/enhanced-line-directives'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4747'>Enhanced #line directive</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12812'>https://www.sysnet.pe.kr/2/0/12812</a> C# 10 - (11) Lambda 개선 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/lambda-improvements'>공식 문서 1</a>, <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/lambda-attributes'>공식 문서 2</a>, <a target='tab' href='https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md'>Lambda improvements</a>) ; https://www.sysnet.pe.kr/2/0/12813 C# 10 - (12) 문자열 보간 성능 개선 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/improved-interpolated-strings'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/4487'>Interpolated string improvements</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12826'>https://www.sysnet.pe.kr/2/0/12826</a> C# 10 - (13) 단일 파일 내에 적용되는 namespace 선언 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/file-scoped-namespaces'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/137'>File-scoped namespace</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12828'>https://www.sysnet.pe.kr/2/0/12828</a> C# 10 - (14) 구조체 타입에 기본 생성자 정의 가능 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/parameterless-struct-constructors'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/99'>Parameterless struct constructors</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12829'>https://www.sysnet.pe.kr/2/0/12829</a> C# 10 - (15) CallerArgumentExpression 특성 추가 (<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/caller-argument-expression'>공식 문서</a>, <a target='tab' href='https://github.com/dotnet/csharplang/issues/287'>Caller expression attribute</a>) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12835'>https://www.sysnet.pe.kr/2/0/12835</a> Language Feature Status ; <a target='tab' href='https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md'>https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md</a> </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1049
(왼쪽의 숫자를 입력해야 합니다.)