매개변수 => 식
의 문법으로 사용가능하며
delegate int del(int i);
del myDelegate = x => x * x;
| cs |
위의 예는 매개변수가 하나인 경우이며, 두개 이상인 경우에는 매개변수를
(int x, int y) => x+y
와 같이 ()로 묶어줘야 하며, 매개변수가 없는 경우에는 빈 ()로 표현합니다.
위의 델리게이트를 앞서 살펴봤던 익명메소드로 표현하면 어떻게 될까요?
delegate int del(int i);
del myDelegate = delegate (int x)
{
return x * x;
};
| cs |
보시다시피 람다 쪽이 훨씬 간결합니다.
문(Statement) 형식의 람다
(매개변수목록) => { //...; }
의 형식으로 {} 사이에 문장이 들어오는 형식의 람다입니다.
using System;
namespace StatementLambda
{
class Program
{
delegate void TestDelegate(string s);
static void Main(string[] args)
{
TestDelegate test = (s) =>
{
string testS = s + " " + "World";
Console.WriteLine(testS);
};
test("Hello");
}
}
}
| cs |
Func와 Action
지금까지 람다를 포함한 모든 익명메소드의 작성시 반드시 해당하는 델리게이트가 필요했습니다. 이런 번거로움을 피하기 위해 .NET 프레임워크 내에 이미 Func와 Action이라는 델리게이트가 선언되어 있습니다. Func는 결과를 반환하는 메소드, Action은 결과를 반환하지 않는 메소드를 위한 델리게이트입니다.
Func
기본적으로 매개변수가 16개까지(0~16 까지 총 17개) 델리게이트가 정의되어 있어 어지간해선 새로 만들 필요없이 사용 가능합니다.
매개변수 없을 때: Func<TResult>
Func<int> func1 = () => 10; //매개변수 없이 10 return
Console.WriteLine(func1()); // 10출력
매개변수 1개: Func<T1, Tresult>
Func<int, int> func2 = (x) => x*x;
Console.WriteLine(func2(3)); //9출력
매개변수 2개: Func<T1, T2, Tresult>
Func<int, int, int> func3 = (x, y) => x + y;
Console.WriteLine(func3(2,3)); // 5출력
using System;
namespace FuncTest
{
class Program
{
static void Main(string[] args)
{
Func<int> func1 = () => 10;
Console.WriteLine(func1());
Func<int, int> func2 = (x) => x * 2;
Console.WriteLine(func2(3));
Func<double, double, double> func3 = (x, y) => x / y;
Console.WriteLine(func3(22,7));
}
}
}
| cs |
Action
Func와 다른 것은 반환형식이 없다는 것 뿐입니다. 반환값이 없으므로 마지막 TResult에 해당하는 부분이 필요치 않습니다. 따라서 Func보다 매개변수가 하나 적습니다.
매개변수 없을 때:
Action act1 = () => Console.WriteLine("Action()");
act1();
매개변수 1개: Action<T>
int result = 0;
Action<int> act2 = (x) => result = x * x;
act2(3);
Console.WriteLine(result);
매개변수 2개: Action<T1,T2>
Action<double, double> act3 = (x, y) =>
{
double result = x / y;
Console.WriteLine(" {0} / {1} = {2}", x, y, result);
};
act3( 4.0, 15.0);
Action act1 = () => Console.WriteLine("Action()");
act1();
매개변수 1개: Action<T>
int result = 0;
Action<int> act2 = (x) => result = x * x;
act2(3);
Console.WriteLine(result);
매개변수 2개: Action<T1,T2>
Action<double, double> act3 = (x, y) =>
{
double result = x / y;
Console.WriteLine(" {0} / {1} = {2}", x, y, result);
};
act3( 4.0, 15.0);
식트리
식 트리는 노드와 에지가 나무가지 모양을 이루고 있는 자료구조입니다.(참고)
식트리에 이용되는 클래스는 System.Linq.Expressions 에 정의되어 있습니다.
클래스 | 설명 | |
---|---|---|
BinaryExpression |
이항 연산자가 있는 식을 나타냅니다.
| |
BlockExpression |
변수를 정의할 수 있는 식의 시퀀스가 포함된 블록을 나타냅니다.
| |
CatchBlock |
try 블록의 catch 문을 나타냅니다.
| |
ConditionalExpression |
조건부 연산자가 있는 식을 나타냅니다.
| |
ConstantExpression |
상수 값이 있는 식을 나타냅니다.
| |
DebugInfoExpression |
디버그 정보에 대한 시퀀스 위치를 내보내거나 지웁니다. 그러면 디버거에서 디버깅할 때 올바른 소스 코드를 강조 표시할 수 있습니다.
| |
DefaultExpression |
형식 또는 빈 식의 기본값을 나타냅니다.
| |
DynamicExpression |
동적 연산을 나타냅니다.
| |
DynamicExpressionVisitor |
동적 식 트리의 방문자 또는 재작성기를 나타냅니다.
| |
ElementInit |
IEnumerable 컬렉션의 단일 요소에 대한 이니셜라이저를 나타냅니다.
| |
Expression |
식 트리 노드를 나타내는 클래스가 파생되는 기본 클래스를 제공합니다. 또한 다양한 노드 형식을 만드는 static(Visual Basic에서는 Shared) 팩터리 메서드가 들어 있습니다. 이 클래스는 abstract 클래스입니다.
| |
Expression<TDelegate> |
강력한 형식의 람다 식을 식 트리 형태의 데이터 구조로 나타냅니다. 이 클래스는 상속될 수 없습니다.
| |
ExpressionVisitor |
식 트리의 방문자 또는 재작성기를 나타냅니다.
| |
GotoExpression |
무조건 점프를 나타냅니다. 여기에는 return 문, break 문, continue 문 및 기타 점프가 포함됩니다.
| |
IndexExpression |
속성 또는 배열의 인덱싱을 나타냅니다.
| |
InvocationExpression |
인수 식 목록에 대리자 또는 람다 식을 적용하는 식을 나타냅니다.
| |
LabelExpression |
에 배치할 수 있는 레이블을 나타냅니다 Expression 컨텍스트. 해당 제공한 값을 가져오는에 레이블로 점프, GotoExpression합니다. 그렇지 않으면 값을 받는 DefaultValue합니다. 하는 경우는 Type System.Void, 값을 제공 해야 합니다.
| |
LabelTarget |
대상을 나타내는 데 사용 되는 GotoExpression합니다.
| |
LambdaExpression |
람다 식을 설명합니다. .NET 메서드 본문과 유사한 코드 블록을 캡처합니다.
| |
ListInitExpression |
컬렉션 이니셜라이저가 있는 생성자 호출을 나타냅니다.
| |
LoopExpression |
무한 루프를 나타냅니다. "break"로 종료할 수 있습니다.
| |
MemberAssignment |
필드 또는 개체의 속성에 대한 할당 작업을 나타냅니다.
| |
MemberBinding |
기본 클래스를 제공하며, 이 기본 클래스에서 새로 만든 개체의 멤버를 초기화하는 데 사용되는 바인딩을 나타내는 클래스가 파생됩니다.
| |
MemberExpression |
필드 또는 속성에 대한 액세스를 나타냅니다.
| |
MemberInitExpression |
생성자 호출 및 하나 이상의 새 개체 멤버에 대한 초기화를 나타냅니다.
| |
MemberListBinding |
새로 만든 개체의 컬렉션 멤버 요소에 대한 초기화를 나타냅니다.
| |
MemberMemberBinding |
새로 만든 개체 멤버의 멤버에 대한 초기화를 나타냅니다.
| |
MethodCallExpression |
정적 메서드 또는 인스턴스 메서드에 대한 호출을 나타냅니다.
| |
NewArrayExpression |
새 배열 생성 및 해당 새 배열의 요소 초기화를 나타냅니다.
| |
NewExpression |
생성자 호출을 나타냅니다.
| |
ParameterExpression |
명명된 매개 변수 식을 나타냅니다.
| |
RuntimeVariablesExpression |
변수에 대 한 런타임 읽기/쓰기 권한을 제공 하는 식입니다.
| |
SwitchCase |
한 사례는 SwitchExpression합니다.
| |
SwitchExpression |
컨트롤을 전달 하 여 여러 선택 항목을 처리 하는 제어 식을 나타냅니다 SwitchCase합니다.
| |
SymbolDocumentInfo |
소스 파일에 대한 디버깅 기호 정보를 내보내는 데 필요한 정보 특히, 파일 이름 및 고유 언어 식별자를 저장합니다.
| |
TryExpression |
try/catch/finally/fault 블록을 나타냅니다.
| |
TypeBinaryExpression |
식과 형식 간의 작업을 나타냅니다.
| |
UnaryExpression |
단항 연산자가 있는 식을 나타냅니다.
|
이 기본 클래스들과 다양한 static 팩토리 메소드들을 이용하여 식트리 형태의 코드를 만드는 것이 가능합니다.
예를 들어 다음의 코드는 1+x라는 식을 트리로 표현합니다.
Expression const1 = Expression.Constant(1); //상수1
Expression param1 = Expression.Parameter(typeof(int), "x"); //매개변수 x
Expression exp = Expression.Add( const1, param1 ); // 1+x
이렇게 해서 작성된 코드는 실행 가능한 상태가 아니고 그저 데이터 상태에 머물러 있을 뿐입니다. 이 exp는 람다식으로 컴파일 되어야 실행 가능해지는데 Expression<TDelegate> 클래스를 이용하여 람다식으로 컴파일 가능합니다.
Expression const1 = Expression.Constant(1); //상수1
Expression param1 = Expression.Parameter(typeof(int), "x"); //매개변수 x
Expression exp = Expression.Add( const1, param1 ); // 1+x
Expression<Func<int, int>> lambda1 =
Expression<Func<int, int>>.Lambda<Func<int, int>>(
exp, new ParameterExpression[]{ParameterExpression)param1});
Func<int, int> compiledExp = lambda1.Compile(); //실행 가능하게
은근히 복잡하네요. 이제 1*2+(x-y)라는 식을 식 트리로 만들어 컴파일 하는 예제를 보겠습니다.
결과) 1*2+(7-8)=1
이것을 람다식을 이용하여 다시 만들어봅니다.
훨씬 간단하지만 람다식을 이용할 때는 동적으로 식트리를 만들기는 어렵다고 합니다.
식트리가 지금은 어디에 필요한지를 잘 모르겠지만, 이제 곧 알아볼 LINQ에 가면 그 필요성을 깨닫게 될 것입니다.
예를 들어 다음의 코드는 1+x라는 식을 트리로 표현합니다.
Expression const1 = Expression.Constant(1); //상수1
Expression param1 = Expression.Parameter(typeof(int), "x"); //매개변수 x
Expression exp = Expression.Add( const1, param1 ); // 1+x
이렇게 해서 작성된 코드는 실행 가능한 상태가 아니고 그저 데이터 상태에 머물러 있을 뿐입니다. 이 exp는 람다식으로 컴파일 되어야 실행 가능해지는데 Expression<TDelegate> 클래스를 이용하여 람다식으로 컴파일 가능합니다.
Expression const1 = Expression.Constant(1); //상수1
Expression param1 = Expression.Parameter(typeof(int), "x"); //매개변수 x
Expression exp = Expression.Add( const1, param1 ); // 1+x
Expression<Func<int, int>> lambda1 =
Expression<Func<int, int>>.Lambda<Func<int, int>>(
exp, new ParameterExpression[]{ParameterExpression)param1});
Func<int, int> compiledExp = lambda1.Compile(); //실행 가능하게
은근히 복잡하네요. 이제 1*2+(x-y)라는 식을 식 트리로 만들어 컴파일 하는 예제를 보겠습니다.
using System;
using System.Linq.Expressions;
namespace ExpressionTree
{
class Program
{
static void Main(string[] args)
{
// 1*2+(x-y)
Expression const1 = Expression.Constant(1);
Expression const2 = Expression.Constant(2);
Expression leftExp = Expression.Multiply(const1, const2); //1*2
Expression param1 = Expression.Parameter(typeof(int)); // x를 위한 변수
Expression param2 = Expression.Parameter(typeof(int)); // y를 위한 변수
Expression rightExp = Expression.Subtract(param1, param2); // x-y
Expression exp = Expression.Add(leftExp, rightExp); // 1*2+(x-y)
Expression<Func<int, int, int>> expression =
Expression<Func<int, int, int>>.Lambda<Func<int, int, int>>(
exp, new ParameterExpression[]
{
(ParameterExpression)param1,(ParameterExpression)param2
});
Func<int, int, int> func = expression.Compile();
// x = 7, y = 8
Console.WriteLine("1*2+(7-8)={0}", func(7, 8));
}
}
}
| cs |
결과) 1*2+(7-8)=1
이것을 람다식을 이용하여 다시 만들어봅니다.
using System;
using System.Linq.Expressions;
namespace ExpressionTree
{
class Program
{
static void Main(string[] args)
{
Expression<Func<int, int, int>> expression = (a, b) => 1 * 2 + (a - b);
Func<int, int, int> func = expression.Compile();
// x = 7, y = 8
Console.WriteLine("1*2+(7-8)={0}", func(7, 8));
}
}
}
| cs |
훨씬 간단하지만 람다식을 이용할 때는 동적으로 식트리를 만들기는 어렵다고 합니다.
식트리가 지금은 어디에 필요한지를 잘 모르겠지만, 이제 곧 알아볼 LINQ에 가면 그 필요성을 깨닫게 될 것입니다.
댓글 없음:
댓글 쓰기