2022. 1. 20. 00:00ㆍCSharp/Advance
C# 10 에서 lambda 개선 사항
Attribute
속성이 있는 람다 허용
속성은 람다 식 및 람다 매개 변수에 추가할 수 있다.
메서드 특성과 매개변수 특성 간의 모호성을 피하기 위해
특성이 있는 람다 표현식은 괄호로 묶인 매개변수 목록을 사용해야 한다.
매개변수 유형은 필요하지 않다.
f = [A] () => { }; // [A] lambda
f = [return:A] x => x; // syntax error at '=>'
f = [return:A] (x) => x; // [A] lambda
f = [A] static x => x; // syntax error at '=>'
f = ([A] x) => x; // [A] x
f = ([A] ref int x) => x; // [A] x
동일한 속성 목록 내에서 쉼표로 구분하거나 별도의 속성 목록으로 여러 속성을 지정할 수 있다.
var f = [A1, A2][A3] () => { }; // ok
var g = ([A1][A2, A3] int x) => x; // ok
delegate 구문으로 선언된 익명 메서드 에는 특성이 지원되지 않는다.
var f = [A] delegate { return 1; }; // syntax error at 'delegate'
var g = delegate ([A] int x) { return x; }; // syntax error at '['
Explicit return type
명시적 반환 유형이 있는 람다 허용
C# 10부터는 람다 식의 반환 형식을 입력 매개 변수 앞에 지정할 수 있다.
명시적 반환 형식을 지정하는 경우 입력 매개 변수를 괄호로 묶어야 한다.
var choose = (bool b) => b ? 1 : "two"; // ERROR: Can't infer return type
var choose = object (bool b) => b ? 1 : "two"; // Func<bool, object>
delegate 구문으로 선언된 익명 메서드 에는 명시적 반환 형식이 지원되지 않는다.
f = delegate int { return 1; }; // syntax error
f = delegate int (int x) { return x; }; // syntax error
Natural (function) type
람다 및 메서드 그룹에 대한 자연 스러운 대리자 형식 유추
람다 식에서 대리자 형식을 유추할 수 있다.
var parse = (string s) => int.Parse(s);
컴파일러는 parse가 Func<string, int>일 것이라 유추할 수 있다.
람다 식이 자연 형식을 갖는다면 System.Object 또는 System.Delegate과 같은 덜 명시적인 형식에 할당할 수 있다.
object parse = (string s) => int.Parse(s); // Func<string, int>
Delegate parse = (string s) => int.Parse(s); // Func<string, int>
정확히 하나의 오버로드를 갖는 메서드 그룹(즉 매개 변수 목록이 없는 메서드 이름)은 자연 형식을 갖는다.
var read = Console.Read; // Just one overload; Func<int> inferred
var write = Console.Write; // ERROR: Multiple overloads, can't choose
System.Linq.Expressions.LambdaExpression 또는 System.Linq.Expressions.Expression에 람다 식을 할당했고 람다가 자연 대리자 형식을 갖는다면, 식은 System.Linq.Expressions.Expression<TDelegate> 자연 형식을 갖고 자연 대리자 형식은 형식 매개 변수의 인수로 사용된다.
LambdaExpression parseExpr = (string s) => int.Parse(s); // Expression<Func<string, int>>
Expression parseExpr = (string s) => int.Parse(s); // Expression<Func<string, int>>
자연형식을 갖지 않는 람다식
var parse = s => int.Parse(s); // ERROR: Not enough type info in the lambda
컴파일러가 s 의 형태를 유추할 수 없으므로 다음과 같이 해야 한다.
Func<string, int> parse = s => int.Parse(s);
또는 아래와 같이 해도 된다. 컴파일러는 parse가 Func<string, int>일 것이라 유추할 수 있다.
var parse = (string s)=>int.Parse(s);
람다 식에서 외부 변수 및 변수 범위 캡처
람다 식의 변수 범위에는 다음과 같은 규칙이 적용된다.
- 캡처된 변수는 해당 변수를 참조하는 대리자가 가비지 수집 대상이 될 때까지 가비지 수집되지 않는다.
- 람다 식에서 사용된 변수는 바깥쪽 메서드가 볼 수 없다.
- 람다 식은 바깥쪽 메서드의 in, ref 또는 out 매개 변수를 직접 캡처할 수 없다.
- 람다 식의 return 문에 의해서는 바깥쪽 메서드가 반환되지 않는다.
- 해당 점프 문의 대상이 람다 식 블록 바깥에 있는 경우 람다 식은 goto, break 또는 continue 문을 포함할 수 없다. 대상이 블록 내에 있는 경우 람다 식 블록 외부에 점프 문을 사용해도 오류가 발생한다.
public static class VariableScopeWithLambdas
{
public class VariableCaptureGame
{
internal Action<int> updateCapturedLocalVariable;
internal Func<int, bool> isEqualToCapturedLocalVariable;
public void Run(int input)
{
int j = 0;
// 바깥쪽 j 를 update 함
// j capture
UpdateCapturedLocalVariable = x =>
{
j = x;
bool result = j > input;
Console.WriteLine($"{j} is greater than {input}: {result}");
};
// j 사용
// j capture
IsEqualToCapturedLocalVariable = x => x == j;
// run 내부에서 lambda 문인 UpdateCapturedLocalVariable 를 호출하여
// j 의 값을 10으로 설정함
// Run 함수를 벗어나면 일반 적으로 int j 는 초기화 되므로
// UpdateCapturedLocalVariable 람다 문의 캡쳐된 j 나
// IsEqualToCapturedLocalVariable 의 캡쳐된 j 도
// 초기화 될것이라 생각할수 있으나 초기화 되지 않고 캡쳐한 값을 유지한다.
// 즉 가비지시 수집되지 않는다.
Console.WriteLine($"Local variable before lambda invocation: {j}");
UpdateCapturedLocalVariable(10);
Console.WriteLine($"Local variable after lambda invocation: {j}");
}
}
public static void Main()
{
var game = new VariableCaptureGame();
int gameInput = 5;
game.Run(gameInput);
int jTry = 10;
bool result = game.isEqualToCapturedLocalVariable(jTry);
Console.WriteLine($"Captured local variable is equal to {jTry}: {result}");
int anotherJ = 3;
game.updateCapturedLocalVariable(anotherJ);
bool equalToAnother = game.isEqualToCapturedLocalVariable(anotherJ);
Console.WriteLine($"Another lambda observes a new value of captured variable: {equalToAnother}");
}
// Output:
// Local variable before lambda invocation: 0
// 10 is greater than 5: True
// Local variable after lambda invocation: 10
// Captured local variable is equal to 10: True
// 3 is greater than 5: False
// Another lambda observes a new value of captured variable: True
}
Syntax
lambda_expression
: modifier* identifier '=>' (block | expression)
| attribute_list* modifier* type? lambda_parameters '=>' (block | expression)
;
lambda_parameters
: lambda_parameter
| '(' (lambda_parameter (',' lambda_parameter)*)? ')'
;
lambda_parameter
: identifier
| attribute_list* modifier* type? identifier equals_value_clause?
;
관련영상
'CSharp > Advance' 카테고리의 다른 글
제네릭 컬렉션 (Generic Collection) (0) | 2022.01.24 |
---|---|
Record (0) | 2022.01.21 |
Action, Func, Lambda (0) | 2022.01.19 |
Event (이벤트) (0) | 2022.01.18 |
Delegate (대리자) (0) | 2022.01.17 |