posted by 방랑군 2009. 10. 7. 11:24

참조 : http://dalbong2.net/entry/C-20-iterators
         http://www.hoons.kr/board.aspx?Name=cshaptip&BoardIdx=1021&Page=1&Mode=2

1. Iterators

using System; 
using System.Collections.Generic; 
class Test 

  public static void Main() 
  { 
       foreach (string s in GetItems()) 
           Console.WriteLine(s); 
  }

  private static IEnumerable GetItems() 
  { 
       yield return "Hello yield 1"; 
       yield return "Hello yield 2"; 
       yield return "Hello yield 3"; 
       yield return "Hello yield 4"; 
       yield return "Hello yield 5"; 
  } 


"yield return" 문이 있는데, iteration의 다음 값을 반환한다고 한다. 
"yield break" 문도 있단다. 

『The yield return statement produces the next value of the iteration. 
  The yield break statement indicates that the iteration is complete.』 

GetItems() 메소드의 리턴 타입이 IEnumerable 이라는 것도 주목할 부분이다. 


참조 문서 
c# 2.0 iterators - 
http://community.bartdesmet.net/blogs/bart/archive/2006/07/06/4121.aspx

2. Partial

// MyForms1.cs
partial public class MyForms
{
  public BusinessLogic(){}

// MyForms2.cs
partial public class MyForms
{
  public PresentationLogic(){}
}

Partial 클래스는 하나의 클래스를 여러 개의 파일로 나누어 작성할 수 있는 기능이다. 이 기능은 Windows Forms이나 Web 응용 프로그램을 협업으로 개발해 왔다면 가장 크게 매력을 느낄 것이다. Partial 클래스를 이용하면 한 클래스를 여러 가지 로직(비즈니스 로직과 프리젠테이션 로직)으로 나누어 개발할 수 있다. 물론 각 로직을 각각의 클래스로 구현하는 것이 바람직했지만, Forms 클래스는 어쩔 수 없이 여러 로직이 공존할 수 밖에 없는 경우가 많았다(특히 프리젠테이션 로직은 자동으로 생성되는 코드가 대부분이다). 비단 그러한 문제가 아니더라도 여러 명의 개발자가 자신이 맡은 메쏘드를 나누어서 개발할 수 있다면, 이보다 더 좋은 협업 방법이 없을 것이다. 이는 형상 관리 측면에서도 큰 이점을 제공한다.

Partial 클래스는 기존의 클래스 앞에 partial 키워드를 추가하여 작성할 수 있다. 물론, partial 키워드를 사용한 클래스는 여러 파일에 나뉘어 존재할 수 있다.
위 소스의 MyForms 클래스는 비즈니스 로직을 구현한 MyForms1.cs 파일과 프리젠테이션 로직을 구현한 MyForms2.cs 파일로 나뉘어져 있다. 컴파일러는 이와 같이 나뉘어져 있는 파일을 조합하여 하나의 MyForms 클래스를 작성한다.

Partial 클래스를 작성할 때 다음과 같은 사항을 유의해야 한다.

1. Partial 클래스는 동일한 어셈블리/모듈(dll 또는 exe)에 존재해야 한다.
2. 한번이라도 partial로 선언된 클래스는 모두 동일하게 partial로 선언되어야 한다.

partial public class MyForms {}

public class MyForms {} // 컴파일 오류

3. Generic으로 사용될 매개 변수들이 일치해야 한다.
4. public/private/protected/internal 등을 사용할 수 있다.
5. 중첩된 클래스에 대해서도 Partial 클래스를 작성할 수 있다.
6. Partial 클래스에 적용된 특성(Attribute)들은 컴파일 시에 모두 병합된다.

3. Nullable
 만약 값 타입과 참조 타입의 큰 차이점이 무엇이냐고 묻는다면, 널(null) 값을 설정할 수 있는지의 여부일 것이다. 참조 타입은 널 값을 설정할 수 있는 반면 값 타입은 불가능하다. 이는 값 타입 입장에서 보면, 상당히 불합리한 처우가 아닐 수 없다. 왜 값 타입은 언제나 초기화가 필요한 것일까? 참조 타입은 널 값을 설정함으로써 구체적으로 객체를 생성하는 것을 뒤로 미룰 수 있지만, 값 타입은 변수를 선언함과 동시에 개발자가 원하든 원하지 않든 간에 어떤 값으로 설정되기 때문에 개발자가 할 수 있는 일이라곤 0이나 -1을 널 대신 사용하는 정도이다. 0과 -1이 해당 변수에서 특별한 의미를 지니고 있다면, 그나마도 불가능하다.

Nullable 타입은 기본 타입에 물음표(?)를 추가하여 선언한다. 예를 들어, int?와 같이 선언하면 기본 타입인 int의 Nullable 타입이 선언된다. 이 때 선언된 타입은 널인지에 대한 여부를 확인할 수 있는 기능을 지원한다는 점을 제외하면 내부적으로 기본 타입(int 형)과 동일하다. 이와 같은 기능이 가능한 이유는 바로 Generic 덕분이다.

 <리스트 3> Nullable 타입의 예

int? x;
if (x != null)
  Console.WriteLine(x.Value);
else
  Console.WriteLine("Null");

C# 언어 스펙을 보면 Nullable 타입은 다음 <리스트 4>와 같이 HasValue와 Value 속성을 노출하는 Generic 구조체이다. 이때 HasValue가 false이면 널인 경우이고 HasValue가 false일 때, Value 속성 값을 설정하면 System.InvalidOperationException 예외가 발생하도록 구현되어 있다.

 <리스트 4> Nullable Generic 클래스의 기본 구조

public struct Nullable where A : struct {
  bool HasValue { get; }
  A Value { get; }

  public Nullable(A value);
}

따라서 int? x = 123; 과 같은 코드를 작성하면 컴파일러는 Nullable x = new Nullable(123); 을 생성한다.

지금까지 Nullable 타입에 대해서 간단하게 살펴보았는데, Nullable 타입은 탄생 배경부터 여러 가지 이슈들을 동반하고 있다(예를 들면, int?와 int??의 사용). 따라서 Nullable 타입에 관한 보다 자세한 정보는 MSDN의 블로그를 통해서 확인하도록 한다.@




'IT > C#' 카테고리의 다른 글

닷넷 트랜잭션 정리  (0) 2009.09.30
C#2.0 , C# 3.0 New Features  (0) 2009.09.15
C# 3.0 개요  (0) 2009.09.11
[C# 2.0] Generics, Iterator, Anonymous, Partial  (0) 2009.08.24
[.net Framework] System.Activator 객체  (0) 2009.08.24
posted by 방랑군 2009. 9. 30. 14:20

참조 : http://resisa.tistory.com/34


이번 프로젝트를 하면서 트랜잭션에 대해서 다시 한번 생각해보게 되었고 아래의 사이트들을 통해서 좋은 정보를 얻었다.

트랜잭션에 대한 정리를 해놓은 MS 장현춘 부장님의 블로그 링크이다.
http://kingcrap.com/entry/닷넷-트랜잭션-정리

System.Transactions(TransactionScope)
대해서 정리를 해놓은 드원테크놀로지의 유경상 수석님의 블로그링크이다.
http://www.simpleisbest.net/articles/996.aspx


위을 내용을 아주 간단히 요약을 해보겠다~
먼저 트랜잭션 처리는 1. 로컬 트랜잭션(수동) 2. 분산 트랜잭션(자동)으로 나뉜다. 가지의 방법은 서로 장단점있는데 속도의 측면에서는 로컬 트랜잭션이 편리성 측면에서는 분산 트랜잭션이 좋다. 하지만 TransactionScope 사용하면 Promotion이라는 기능이 있고(TransactionScope 트랜잭션이 발생할  로컬 또는 분산 트랜잭션인지를 자동으로 처리해주는데 로컬 트랜잭션으로 실행하다가 다른 DB 접속을 하는 순간에 자동으로 분산 트랜잭션으로 전환이 되는데 이러한 것을 Promotion이라고 한다.) 이러한 Promotion MSSQL 2005, 2008에서만 지원을 한다고 한다.
이번에 내가 프로젝트에서 DB Oracle여서 TransactinoScope 사용하려고 했더니 oramts.dll 필요하다며 예외가 발생했다. Ora MTS 설치하면 TransactionScope 사용할 있었다.

Ora MTS
대해서는 아래의 링크를 보자.
http://www.simpleisbest.net/archive/2005/08/23/208.aspx

다음은 Ora MTS 다운받는 링크이다.
http://www.oracle.com/technology/software/tech/windows/ora_mts/htdocs/utilsoft.html

테스트 환경은 윈도우 서버 2003에서 Oracle 9i 클라이언트와 Ora MTS 9.2.0.7.0 버전을 사용해서 해본 결과 TransactionScope 사용할 있었다. 위의 첫번 링크(장현춘님) 가보면 비스타에서도 Ora MTS 10.2.0.4.0버전을 사용해서 분산 트랜잭션으로 MSSQL Oracle 묶어 프로젝트에서 사용하고 있다는 말이 제일 마지막에 나온다. 하지만 비스타에서 Ora MTS(버전:10.2.0.1, 10.2.0.2) 설치 오류가 나서 테스트를 해볼 수가 없었고 Ora MTS 10.2.0.4.0버전을 구할 수도 없었다. 아마 무엇인가 문제가 있어서 배포를 잠시 중단한게 아닌가 싶다.

<<
수정일자 : 1220>>
Ora MTS 10.2.0.4.0
버전이 아니라 Oracle Database 10g Release 2 (10.2.0.4.0) Patch Set 버전이다. Ora MTS 10.2.0.2.0 설치하면 된다. 또한 Ora MTS 오류가 나는 것의 원인은 Office 2007 때문이였다. 설치를 하다가 javaw.exe프로세스가 종료되었다는 메시지와 함께 설치가 종료되는데 Office 지우고 설치를 하면 설치가 된다. 이것이 설치되었다고 해서 비스타에서TransactionScope 사용해서 분산 트랜잭션을 사용할 있는 것이 아니다. Oracle Database 10g Release 2 (10.2.0.4.0) Patch Set까지 설치해야한다. Oracle Meta Link에서 이것을 다운을 받을 있는데 Meta Link 오라클과 계약을 맺어 인증번호가 있어야 사용할 있다. 패치까지 받고 테스트는 안해봤지만 아마도 것이다사용자등록을 했는데 관리자가 있어 패스워드를 받아야한다. 하나 해보기 힘들다. 차후에 패치를 받고 테스트를 마친후에 정리를 해야할꺼 같다.

저작자 표시비영리


기사 보기...

'IT > C#' 카테고리의 다른 글

C#2.0 Iterators,Partial 클래스,Nullable 타입  (0) 2009.10.07
C#2.0 , C# 3.0 New Features  (0) 2009.09.15
C# 3.0 개요  (0) 2009.09.11
[C# 2.0] Generics, Iterator, Anonymous, Partial  (0) 2009.08.24
[.net Framework] System.Activator 객체  (0) 2009.08.24
posted by 방랑군 2009. 9. 15. 17:25



참조 : http://debop.egloos.com/2299315



C# 2.0 /3.0 New Features

작성자 : 리얼웹 개발본부 배성혁

 

들어가는 말

.NET Framework 버전과 C# Specification 버전이 꼭 일치하지는않지만유사하게 버전이 올라가고 있습니다. 2009년 현재 C# 4.0 Spec Preview가 공개되었고이를 .NET Framework 4.0, VS.NET 2010 부터 사용이 가능할 것 같습니다.

 

여러분은 처음 .NETFramework 또는 C#을 공부하면서문법, API 등을 공부하고쓰고 있지만 어떤 버전을 쓰고 있는지는 잘모르셨을 것이라 생각됩니다하지만 S/W 개발쪽에 경력이쌓이게 되면, version에 따라 변화되는 기능에 대해 어느정도 무의식 속에 파악하고수용하게 됩니다.

보통 어느정도 초보딱지를 뗀 개발자라면자신이 사용하는 툴이나 라이브러리를 가장 최신의 것으로 유지하려고 하는 경향이 있습니다. Early adapter라고 하지요… 하지만신제품 개발단계가 아닌 제품의 라이프사이클을 고려하게 되면꼭최신의 제품을 사용할 수 없을 때가 더 많아지기도 합니다. – backward compatibility가있는 제품이라면 영향이 작을 수는 있지만, Microsoft는 워낙 악명이 높습니다차라리 open source 진영에서 더 신경쓰고 있는 부분입니다.

 

제품 Spec을결정하던가버전관리를 주도하는 개발자는 사용하는 제품의 새로운 버전이 출시되었을 때 Upgrade를 해야 하는지를 고려해야 합니다아예 눈/귀를 닫아서는 안되고,Library 하나라도담당 제품의 많은 장점을 줄 수 있는 기능이 있는지?, 제품 개발 혹은 Customizing 시의 공수외부 인터페이스 변화 등을 고려해서신 버전 사용을 결정해야 합니다.

 

고려대상은 사용하는 제품마다 다르고독자 판단만으로 안되는 경우도 많습니다특히나 MS 제품은 무조건 신버전을 사용하도록(지들 제품 많이 팔리도록강요하는 경우가 많습니다. (여기에 놀아나지 말아야 하지만… 가만보면 제가 가장 많이 놀아나는 것도 같고…) – 고객 중에서도최신버전을 좋아하는 사람기존 버전을 고수하는 사람 등등 참 많습니다.

 

어쨌든 새로운 제품에 대해서는 항상 새로운 기능이 무엇인지뭐가 바뀌었는지기존 제품과의 호환성은 어떤지를 파악해야 합니다새로 도입할 제품도현재 버전이 어떤기능을 가졌는지발전 단계는 어떤지앞으로의 로드맵에는 어떤 기능이 있는지를 조사해야합니다.

 

그럼 이제부터 .NET Framework 2.0/3.0  이제 4.0이 공개될 건데왠 뒷북이라 할 수도 있지만 사실 개발자들이 C# 2.0 spec조차도 잘 사용하지 못하는 경우가 대부분이어서 – 의 새로운 기능에 대해 살펴보겠습니다.

 

먼저 C# 2.0 Spec부터 살펴보죠… 개인적으로 C# 2.0의 새로운 특성 중에 제일 반겼던 것은 Generic 을 지원한다는 것입니다물론 다른 좋은 기능들도 많지만굵은 글자로 되어 있는 부분이 제게는 도움이 많이 되었던 것들입니다.

대부분 실제 많이 사용하고 있는 기능들이므로설명이나 의미는 생략하겠습니다다만 Generic에 대해서는 다음 기회에 다시 설명하도록 하겠습니다.

 

표 1. C# 2.0 New Features

항목

설명

Generic

형식 매개 변수를 사용하여, run time 시에 cat, boxing 작업에 대한 위험없이 단일 class 작성 가능

Iteration

Yield return

Yield break;

Public void GetEnumerator()

{

    for(int i=0; i< _data.Count; i++)

        yield return _data[i];
}

Partial class

public partial class Employee { public void DoWork() { … } }

public partial class Employee { public void GoToLunch() { … } }

Nullable type

System.Nullable 구조체의 Instance

int? num = null;

int count = num ?? 0;

int count = num.GetValueOrDefault(0);

Anonymous method

Delegate void Del(int x);

Del d = delegate() {

    Console.WriteLine(“delete “ + x);

};

Static class

public static class Utils {

    public static void UtilityMethod() { … }

}

Property accessor

string FullName { get; protected set; }

Fixed size buffer

Public struct MyArray { public fixed char PathName[128]; }

Friendly assembly

[assembly:InternalsVisibleTo(“cs_friend_assemblies_2”)]

 

한가지 짚고 넘어가고 싶은 것은 Anonymous method 라는기능입니다이는 java 문법에서 제공되는 기능을 차용한것으로 보이는데앞으로 설명할 C# 3.0 spec에서이를 토대로 많은 기능들을 구현할 수 있음을 알 수 있습니다.

 

다음으로는 C# 2.0 (.NET Framework 2.0 이 더옳은 표현이지만, C#이 대표 Language이므로에서 새롭게 제공하는 delegate 에 대해 알아 보겠습니다.

 

표 2. C# 2.0 중요 Delegate

Type

Definition

Action

public delegate void Action<T> ( T arg )

public delegate void Action<T1, T2> ( T1 arg1, T2 arg2  )

public delegate void Action<T1, T2, T3> ( T1 arg1, T2 arg2, T3 arg3 )

public delegate void Action<T1, T2, T3, T4> ( T1 arg1, T2 arg2, T3 arg3, T4 arg4 )

Func

public delegate TResult Func<T, TResult> ( T arg )

public delegate TResult Func<T1, T2, TResult> ( T1 arg1, T2 arg2 )

public delegate TResult Func<T1, T2, T3, TResult> ( T1 arg1, T2 arg2, T3 arg3 )

public delegate TResult Func<T1, T2, T3, T4, TResult> ( T1 arg1, T2 arg2, T3 arg3, T4 arg4 )

Predicate

public bool Predicate<T>( T obj )

 

 2.에 나온 Action,Func, Predicate라는 delegate는 일반적으로는 많이 사용하는 delegate를 미리 정의해 둔 것이라 볼 수 있습니다특징중에 Generic을 이용한 것인데, Generic을 이용하면, signature는 하나지만다양한 Type을 지원하는 단 한 개의 delegate만을 정의해서 사용할수 있는 장점이 있습니다.

Action voidfunction point (C Language 용어)라 할 수 있는데,  지정된 형식의 인자를 N를 받아 수행하고반환값이 없는 함수를 말합니다.

Func는 반환값이 있다는 차이가 있고, Predicate Func<T, bool>(T arg) 와같은 뜻입니다.

 

위의 delegate를 보시면 메소드 인자의 개수가 최대 4개인데더 많은 인자를 가지는 메소드는 개발자가 추가로 정의하여사용이 가능합니다.

 

사용 예는 다음과 같습니다.

 

Func<stringstring> convert = delegate(string s)

                          {

                                  return s.ToUpper();

                          };   // anonymous method

string name = "Dakota";

 

Console.WriteLine(convert(name));

 

ð  DAKOTA

 

convert라는 delegate를만들고이를 사용하는 방식을 설명했습니다. Anonymousmethod 를 사용하여실제 delegate를위한 메소드 원형의 class에서 정의하지 않고내부적으로이름없는” 메소드를 정의하여 사용할 수 있습니다.

 

더 자세한 내용은

ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.ko/fxref_mscorlib/html/da586d48-5345-2de1-63f2-d6208f298942.htm

ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.ko/fxref_system.core/html/a0ab867a-da51-dd82-2e1a-e87b93712102.htm

를 참고하세요.

 

C# 2.0의 새로운 특징은 이쯤에서 정리하고 C# 3.0의 새로운 기능에 대해서 알아봅시다.

우선 MSDN C# 3.0의새로운 기능에 대한 설명은 ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.ko/dv_csref/html/e5193336-6adb-471e-ab22-e3fc60e0fa52.htm에 자세히 나와 있으니 참고하세요특히 새로운 기능의 필요성다른 새로운 기능의 토대가 되는지 등등을 파악하 보시기 바랍니다.

참고로 C# 3.5의 경우에는 ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.ko/dv_fxintro/html/1b0d91e8-2d01-47a0-affc-966894de71f8.htm에 있습니다. – 3.5에서는 Language 차원에서크게 발전된 건 없습니다.

 

그럼 C# 3.0에서 새롭게 도입된 기능을 알아 봅시다.

 

표 3. C# 3.0 Language enhancements

항목

설명

Local variable type inference

지역변수와 함께 사용될 경우 var 키워드는 초기화 문의 오른쪽에 있는 식에서 변수 또는 배열 요소의 형식을 유추하도록 컴파일러에게 지시한다.

 

var i = 5;

var s = "Hello";

var a = new[] {1,2,3};

for(var x =1; x <10; x++)

{

        // some codes

}

var 키워드는 "Variant"를 의미하지 않으며 변수가 느슨한 형식이거나 런타임에 바인딩됨을 의미하는게 아닙니다.

Object initialize

생성자를 명시적으로 호출하지 않고 개체 초기화를 수행할 수 있게 한다.

var cat = new Cat {Age = 10, Name = "Sylverster"};

 

Object intializer에 익명 형식 허용 단 nullable 형식은 불가

var o = new {Title = "Book In Action", Publisher = "Manning"};

 

Collection initialize

List<int> digits = new List<int> { 01234567 };

List<int> digits2 = new List<int> { 0+112 % 3, MakeInt() };

List<Cat> cats = new List<Cat> {

                       new Cat { Age=10, Name=“Sylverster” },

                       new Cat { Age=4, Name=“Peaches” },

                       null

               };

 

Anonymous types

익명 형식은 형식을 먼저 명시적으로 정의할 필요 없이 읽기 전용 속성 집합을 단일 개체로 캡슐화하는 편리한 방법을 제공합니다형식 이름은 컴파일러에 의해 생성되고 소스 코드 수준에서 사용할 수 없습니다속성의 형식은 컴파일러에 의해 유추됩니다다음 예제에서는 Amount 및 Message라는 두 개의 속성을 사용하여 초기화되는 익명 형식을 보여 줍니다.

 

var o = new {Title = "Book In Action", Publisher = "Manning"};

Auto-implemented properties

속성중에 추가 논리가 필요없을때속성 선언을 간결하게 한다.

 

class MyClass

{

public double TotalAmount { getset; }

public string Name { getprivate set; }       // read-only

public int CustomerId { getprotected set; }  // read-only for user, writable for drived class

}

 

Extension Methods

인스턴스 메서드 구문을 사용하여 호출할 수 있는 정적 메서드를 사용하여 기존 클래스를 확장합니다.

 

public static class MyExtensions {

    public static int WordCount(this String text) {

        return str.Split(new char[] { ‘ ‘, ‘.’, ‘?’, StringSplitOperations.RemoveEmptyEntries).Length;

    }

}

String s = "New Features in C# 3.0";

int wordCount = s.WordCount();

 

Lambda expression

대리자나 식 트리에 바인딩할 수 있는 입력 매개 변수를 가진 인라인 식을 사용할 수 있게 합니다

 

Func<intint> myPower2 = x => x*x;

int x2 = myPower2(5); // x2 = 25

 

Partial methods

Abstract class의 abstract method와 유사한 효과를 나타낸다몇가지 제한사항이 있음

// define in file1.cs

partial void onNameChanged();

// define in file2.cs

partial void onNameChanged()

{

    
}

 

 

C# 3.0 기능 중에 몇 가지는 코딩 시에 작업을 수월하게 하고코드 분석을 쉽게 해 주는 문법적인 발전도 있고, Lambdaexpression과 같이 동적 표현이 가능케 해주는 획기적인 기능도 추가되었습니다 (lambdaexpression은 다음에 얘기할 LINQ에서도 설명할 예정입니다.)

 

요즘 Resharper 4.1 이상을 깔면 추천 코드를 제공해 주는데특정 타입을 “var” 예약어로 바꾸는 것을 추천하는 경우가 많습니다. “var”라는 것이 어찌보면모든 수형을 표현하는 것처럼 보이므로 COM+에서 여러 Language에서 상이한 수형을 표현하기 위한 VARIANT를 연상할 수 있는데실제 var는 동적으로 수형이 결정되는 VARIANT 와는 달리 컴파일시에 결정됩니다. C# 코드상에서만 결정이 안되었지컴파일된 IL Code 상에서는 실제 수형으로 선언되고사용되고 있음을 알수 있습니다.

 

아래 원본 C# 코드와Reflector를 통한 diassembling 된 코드를 비교해 보십시요.

“var”, “object initializer” 를 이용하게 되면아래와 같이 약간의 변형을 거쳐지만원하는 결과가 되는 것은 마찮가지 입니다.

// C# 코드

public virtual Config CreateConfig(string section, string name, string value)

{

        var config = new Config(name, value)

                               {

                                       Application = AdminContext.Current.Application,

                                       Enterprise = AdminContext.Current.Enterprise,

                                       Section = section,

                                       IsEnabled = true

                               };

 

        Session.SaveOrUpdate(config);

        return config;

}

// Deassembler code
public virtual Config CreateConfig(string section, string name, string value)
{
    Config <>g__initLocal7 = new Config(name, value);
    <>g__initLocal7.Application = AdminContext.Current.Application;
    <>g__initLocal7.Enterprise = AdminContext.Current.Enterprise;
    <>g__initLocal7.Section = section;
    <>g__initLocal7.IsEnabled = true;
    Config config = <>g__initLocal7;
    this.Session.SaveOrUpdate(config);
    return config;

}




Deassembled code에서 보듯이 var Config 수형으로 선언되어 있습니다확실히 VARIANT 수형과는 다른 것입니다. (C# 4.0 spec에 보면 dynamic language 기능이포함되는데이때는 기존 variant와 비슷한 수형이 나올것입니다.)

 




다음으로 .NET Framework 3.0 부터 아주 새로운 개념이 추가 되었는데LINQ (Language INtegrated Query) 입니다.

이 부분에 대한 자세한 설명은 MSDN에 있으니 참고하시고다른 글로서 설명을 드리겠습니다.

 

이 글에서는 LINQ의 기본적인 문법을 설명하고위의 C# 3.0 에서 추가된 특징이 어떻게 활용되는지 (반대로 LINQ 라는 개념을 구현하기 위해 C# 3.0의 새로운 기능들이 만들어졌다라고 볼 수 있습니다.)

 

LINQ를 사용하는데는 크게 두가지 방식이 있습니다.

 

1.     Query Expression ( SQL 문과 비슷 )

2.     Query Operator (Extension Methods 사용)

 

우선 2가지 방식의 예를 봅시다.

 

1.     Query Expression

 

Query Expression은 우리가 잘 알고 있는 Database 질의 형식과 유사하다 SQL 문법과 유사하게 사용할 수 있고기본적으로 메모리에 적재된 Object들로부터 질의를 수행하고결과를 받을 수 있다. (물론 LINQ to SQL, LINQ to XML 등 다양한 질의 대상이 추가될 수 있다)

 

표 4. Query Expression Format

 

from [ type ] id in source

join [ type ] id in source on expr equals expr [ into id ] ]

from [ type ] id in source | let id = expr | where condition }

orderby ordering, ordering, … ]

select expr | group expr by key

[ into id query ]

 

 

Query Expression 예를 들어 보자.

 

코드 1. Query Expression Sample


예제 코드는 시스템에 수행중인 모든 프로세스 중에 메모리가 20Mb 이상을 사용하는 프로세스를 찾아사용하는 메모리 양을 기준으로 정렬을 수행하고그 프로세스의 속성 중에 Id, Process Name 만으로 새로운 anonymous type을 생성해서 반환하도록 한다.

 

기존 코드로 이 작업을 수행하려면, Filtering, Sorting, 새로운 Class 정의 등 상당히 많은 코드를 구현해야 하지만예제 코드를 사용하면 (질의 문법에 약간이라도 익숙하다면직관적이고코드도 상당히 간단해 진다.

 

 

2.     Query Operator

Query operator query expression과는 달리메소드 호출을 이용하여 질의를 수행하므로기존 C# Language 문법과 유사하다.다만 C#의 새로운 특징인 (사실 새로운 기능이 아니더라도 가능하게 할 수는 있다. – Nhibernate ICriteria, DetachedCriteria가 그런 경우이다.) extension methods 를 이용하여코드를 일목요연하게 표현 할 수 있다.


 코드 2. Query Operator Sample

 

Query expression과 같은 작업을 수행하는 코드지만기존 C# 코드와 큰 차이가 없다다만 Lambda expression에 대한 공부는 좀 해야겠지요

 

두 예제 모두 붉은 글자로 코드상에서 사용된 C# 3.0의 새로운 특징을 명시했습니다많은 특징들을 LINQ가 이용하므로여러분 중LINQ를 쓰시고자 한다면, C# 3.0의 새로운 특징들을 먼저 공부해야 합니다물론 이러한 새로운 특징들이 꼭 LINQ 기술을 쓸 때만 유용한 것은 아니고전통적인 코드상에서도 상당한 잇점을 제공합니다.

 

그럼 이 두 가지 방식 중에 어떤 걸 이용해야 할까요?

답은 당신 맘대로” 입니다.

 

그럼 넌저요 Query operator 방식을 더 선호합니다.

이유는?

뭐 여러가지가 있겠지만가장 먼저 생각나는 건 메소드 사용이 익숙하고이해하기에도 더 쉽거든요.

또다른 큰 이유는 Query Operator는 확장이 가능하지만, Query expression은 예약어 방식이라, MS 말고는 확장이 곤란합니다.

 

 5.에서는 Query operator와 expression 간의 대응관계를 표시하고 있는데실제 예약어 방식인 query expression에서는 제공하지 않는 부분이 많습니다다만 query operator의 메소드 명이 제게는 직관적이지 않은 부분은 불만입니다.

 

 

 

표 5. Query Operator & Expression

Operator

Expression

Operator

Expression

All

N/A

OrderBy

Orderby …, …

Any

N/A

OrderByDescending

Orderby …, … descending

Average

N/A

Select

Select

Cast

From int I in numbers

SelectMany

Multiple from clauses

Count

N/A

Skip

N/A

Distinct

N/A

SkipWhile

N/A

GroupBy

Group … by … [ into …]

Sum

N/A

GroupJoin

Join … in … on … equals … into …

Take

N/A

Join

Join … in … on … equals …

TakeWhile

N/A

LongCount

N/A

ThenBy

Orderby …,…

Max

N/A

ThenByDescending

Orderby …,… descending

Min

N/A

Where

Where

 

 

 

 

 

이제까지 C# Language의 버전별 새로운 특징을 살펴봤고마지막에는 LINQ에 대한 간략한 소개를 했습니다앞서 설명했지만새로운 버전으로 Upgrade시에는 새로운 기능이 뭐가 있고어떤 장점이 있는지 파악하고 선택적으로 사용하는 것이 우선입니다특히나 C# Language 는 가장 기본이 되는 특성이므로꼭 알아둬야 하고자신의 것으로 만들 필요가 있습니다.

'IT > C#' 카테고리의 다른 글

C#2.0 Iterators,Partial 클래스,Nullable 타입  (0) 2009.10.07
닷넷 트랜잭션 정리  (0) 2009.09.30
C# 3.0 개요  (0) 2009.09.11
[C# 2.0] Generics, Iterator, Anonymous, Partial  (0) 2009.08.24
[.net Framework] System.Activator 객체  (0) 2009.08.24
posted by 방랑군 2009. 9. 11. 16:17

C# 3.0 개요

Anders Hejlsberg, Mads Torgersen
March 2007

적용 대상 :
Visual C# 3.0
개요 : C# 3.0 기술 개요입니다.높은 순서로 함수 스타일의 클래스 라이브러리 작성과 사용을 지원하는 C# 2.0 기반의 언어확장을 소개합니다.

목차

소개
26.1 암시적으로 형식화된 로컬 변수
26.2 확장 메서드
26.2.1 확장 메서드 선언
26.2.2 사용 가능한 확장 메서드
26.2.3 확장 메서드 호출
26.3 lambda 식
26.3.1 익명 메서드와 lambda 식변환
26.3.2 대리자(delegate) 작성식
26.3.3 형식 유추
26.3.3.1 제 1 단계
26.3.3.2 제 2 단계
26.3.3.3 입력 형식
26.3.3.4 출력 형식
26.3.3.5 종속성
26.3.3.6 출력 형식 유추
26.3.3.7 명시적 인수 형식 유추
26.3.3.8 정확한 유추
26.3.3.9 낮은 바인딩 유추
26.3.3.10 고정
26.3.3.11 유추되는 반환 형식
26.3.3.12 메서드 그룹의 변환 형식 유추
26.3.3.13 식직합의 최적 공통 형식
26.3.4 오버로드 해결
26.4 개체 이니셜라이저와 컬렉션 이니셜라이저
26.4.1. 개체 이니셜라이저
26.4.2 컬렉션 이니셜라이저
26.5 익명형식
26.6 암시적으로 형식화된 배열
26.7 쿼리 식
26.7.1 쿼리 식 변환
26.7.1.1 연속된 select 절과 groupby 절
26.7.1.2 범위 변수의 명시적 형식
26.7.1.3 역쿼리식
26.7.1.4 from 절, let 절, where 절, join 절, orderby 절
26.7.1.5 select 절
26.7.1.6 groupby 절
26.7.1.7 투명한 식별자(dentifiers)
26.7.2 쿼리 식 패턴
26.8 식 트리
26.8.1 오버로드 해결
26.9 자동적으로 구현된 속성

소개

이 기사에는 Visual C# 3.0에 적용된 몇가지 업데이트 내용이 포함되어 있습니다. 종합적인 사양은 언어 출시 때에는 제공될 예정입니다.
C# 3.0 에서는 높은 순서의 함수 스타일의 클래스 라이브러리 작성과 사용을 지원하는 C# 2.0 을 기반으로 몇가지의 언어 확장이 도입되었습니다. 이러한 언어 확장으로 관계형 데이터베이스나 XML 등의 분야의 쿼리 언어와 동등의 표현력을 가지는 합성 API 구축이 가능합니다. 확장 기능은 다음과 같습니다.
  • 암시적으로 형식화된 로컬 변수 : 로컬 변수 초기화에 사용되는 식에서 로컬 변수의 형식을 유추할 수 있습니다.
  • 확장 메서드 : 기존의 형식이나 구축된 형식을 추가 메서드로 확장할 수 있습니다.
  • lambda 식: 익명 메서드 진화형으로, 고도의 형식 유추 및 대리자(delegate) 형식이나 식트리 변환을 제공합니다.
  • 개체 이니셜라이저:  개체 구축과 초기화가 간단합니다.
  • 익명형식 : 개체 이니셜라이저에서 자동적으로 유추되어 작성되는 튜플형입니다.
  • 암시적으로 형식화된 배열 : 배열 이니셜라이저에서 배열의 요소 형식을 유추하는 배열 작성과 초기화 형식입니다.
  • 쿼리 식 :  SQL 나 XQuery 등의 관계형 쿼리 언어 및 계층 쿼리 언어를 닮은 언어에 통합된 쿼리 용무의 구문을 제공합니다.
  • 식 트리 :  lambda 식을 코드 (대리자(delegate) 형식)가 아닌 데이터 (식 트리)로서 표현할 수 있습니다.
이 기사에서는 이러한 기능의 기술 개요를 제공합니다. 이 기사에서는「C# 언어 사양 Version 1.2」(§1 ~ §18)과 「C# 버전 2.0 사양」(§19 ~ §25)를 다루며, Visual Studio 2005 설치 디렉터리 다음과 같은 "VC#\Specifications\1041\" 디렉터리에서 이용 가능합니다.

26.1 암시적으로 형식화된 로컬 변수

암시적으로 형식화된 로컬 변수 선언에서는 선언된 로컬 변수의 형식은 그 변수 초기화에 사용되는 식에서 유추됩니다.로컬 변수 선언으로 형식으로서 var 를 지정하고 , var 라는 이름 형식이 범위 안에 없는 경우, 그 선언은 암시적으로 형식화된 로컬 변수 선언입니다.다음은 예를 나타냅니다.
var i = 5;
 var s = "Hello";
var d = 1.0;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();
상기의 암시적으로 형식화된 로컬 변수 선언은 다음에 나타나는 명시적으로 형식화된 선언과 완전히 동등합니다.
int i = 5;
string s = "Hello";
double d = 1.0;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();
암시적으로 형식화된 로컬 변수 선언의 로컬 변수 선언자에게는 다음과 같은 제한이 있습니다.
  • 선언자에게 이니셜라이저가 포함되어있어야 합니다.
  • 이니셜라이저는 식이어야 합니다.
  • 이니셜라이저식에는 컴파일시 형식을 지정할 필요가 있어, 이 형식은 null 형 이외의 형식이어야 합니다.
  • 로컬 변수 선언에 복수의 선언자를 포함할 수 없습니다.
  • 선언한 변수 자체를 이니셜라이저로 참조할 수 없습니다.
암시적으로 형식화된 로컬 변수 선언이 잘못된 예입니다.
var x;               // 오류.형식 유추에 사용하는 이니셜라이저가 없다.
var y = {1, 2, 3};   // 오류.컬렉션 이니셜라이저는 허가되지 않다.
var z = null;        // 오류.null 형은 허가되지 않다.
var u = x => x + 1;  // 오류.lambda 식으로 형식이 지정되지 않다.
var v = v++;         // 오류.변수 자체를 이니셜라이저로 참조할 수 없다.
로컬 변수 선언으로 형식으로서 var 를 지정해, var 라는 이름 형식이 범위 안에 있는 경우, 하위 호환성의 이유에서 그 선언은 그 형식을 참조합니다.다만, var 라는 이름 형식은 "형식 이름은 대문자로 시작한다" 는  규약에 위반하므로, 이러한 상황이 발생할 일은 거의 없을 것입니다.
for 구문 (§8.8.3)의 for-initializer 및 using 구문 (§8.13)의 resource-acquisition은 암시적으로 형식화된 로컬 변수 선언으로 할 수 있습니다. foreach 구문 (§8.8.4)의 반복 변수는 암시적으로 형식화된 로컬 변수로서 선언할 수 있습니다.이 경우, 반복 변수의 형식은 열거된 컬렉션 요소 형식으로 유추됩니다.다음은 예를 나타냅니다.
int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers) Console.WriteLine(n);
이 예에서는 n 형식은 numbers 요소 형식인 int 로 유추됩니다.
암시적으로 형식화된 로컬 변수 선언을 포함할 수 있는 것은 local-variable-declaration,for-initializer,resource-acquisition 및 foreach-statement 뿐입니다.

26.2 확장 메서드

확장 메서드는 인스턴스 메서드 구문을 사용하여 호출할 수 있는 정적 메서드입니다. 실질적으로는 확장 메서드를 사용하면, 기존의 형식이나 구축된 형식을 추가 메서드로 확장할 수 있습니다.
   확장 메서드는 인스턴스 메서드에 비해, 찾아내기 어렵고, 기능도 한정되어 있습니다. 이 때문에 확장 메서드 사용은 가능한 한 삼가해 인스턴스 메서드가 적합하지 않는 경우나 사용할 수 없는 경우에게만 사용하는 것을 추천합니다.속성, 이벤트, 연산자 등, 다른 종류의 확장 멤버에 대해서는 검토중이지만, 현재로서는 지원 되지 않습니다.

26.2.1 확장 메서드 선언

확장 메서드는 메서드의 첫번째 매개 변수에 수식자로서 키워드 this 를 지정하여 선언합니다. 확장 메서드는 non-generic 상자로 되지 않은 정적 클래스에서만 선언 가능합니다. 2개의 확장 메서드를 선언하는 정적 클래스의 예입니다.
namespace Acme.Utilities
{
   public static class Extensions
   {
      public static int ToInt32(this string s) {
         return Int32.Parse(s);
      }
      public static T[] Slice<T>(this T[] source, int index, int count) {
         if (index < 0 || count < 0 || source.Length ? index < count)
            throw new ArgumentException();
         T[] result = new T[count];
         Array.Copy(source, index, result, 0, count);
         return result;
      }
   }
}
확장 메서드의 첫번째의 매개 변수에는 this 이외의 수식자를 지정할 수 없습니다.또, 이 매개 변수의 형식을 포인터형으로 할 수 없습니다.
확장 메서드에는 일반적인 정적 메서드 기능이 모두 갖춰지고 있습니다.또, 확장 메서드는 가져오기 하면, 인스턴스 메서드 구문을 사용하여 호출할 수 있습니다.

26.2.2 사용 가능한 확장 메서드

확장 메서드는 정적 클래스내에서 선언하는지, 네임 스페이스 안에서 using-namespace-directive (§9.3.2)를 사용하여 가져오기 하면, 네임 스페이스 안에서 사용할 수 있습니다. 따라서, using-namespace-directive를 사용하면, 가져오기 한 네임 스페이스에 포함되는 형식이 가져오기 될 뿐만 아니라, 가져오기 한 네임 스페이스 안의 모든 정적 클래스의 모든 확장 메서드가 가져오기 됩니다.
실질적으로는 사용 가능한 확장 메서드는 첫번째의 매개 변수로 지정한 형식의 추가 메서드로서 다루어져 일반적인 인스턴스 메서드보다 우선도는 낮아집니다.예를 들어, 다음과 같이 using-namespace-directive를 사용하여 위의 예의 Acme.Utilities 네임 스페이스를 가져오기 했다고 합니다.
using Acme.Utilities;
이 경우, 다음과 같이 인스턴스 메서드 구문을 사용하여 정적 클래스 Extensions 내의 확장 메서드를 호출하는 것이 가능합니다.
string s = "1234";
int i = s.ToInt32();               // Extensions.ToInt32(s)와 같다
int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] a = digits.Slice(4, 3);      // Extensions.Slice(digits, 4, 3)과 같다

26.2.3 확장 메서드 호출

여기에서는 확장 메서드 호출에 관한 상세한 규칙에 대해 설명합니다.다음에 나타나는 형식 메서드 호출 (§7.5.5.1)
expr . identifier ( )
expr . identifier ( args )
expr . identifier < typeargs > ( )
expr . identifier < typeargs > ( args )
의 각각에서는 일반적인 호출 처리에 의해 적절한 인스턴스 메서드가 검색 되지 않았던 경우 (구체적으로는 호출 후보가 되는 메서드 그룹가 비어 있는 경우), 구문을 확장 메서드 호출로 처리하도록 시도됩니다.우선, 메서드 호출은 각각 다음과 같은 한쪽에 고쳐 쓸 수 있습니다.
identifier ( expr )
identifier ( expr , args )
identifier < typeargs > ( expr )
identifier < typeargs > ( expr , args )
지속적으로 고쳐 쓸 수 있었던 형식은 identifier 해결 방법 이외는 정적 메서드 호출로 처리됩니다. identifier가 지정된 이름을 가지는 네임 스페이스 안의 사용 가능하고 접근 가능한 모든 확장 메서드로 구성되는 메서드 그룹에 대해서, 고쳐 쓸 수 있었던 메서드 호출의 연속된처리가 시도됩니다.이것은 외부을 둘러싸는 가장 가까운 네임 스페이스 선언에서 시작되어, 외부을 둘러싸는 각 네임 스페이스 선언으로 계속되고 그것을 포함한 컴파일 유닛으로 끝납니다.이 세트에서 적절하지 않은 메서드 (§7.4.2.1) 및 첫번째의 인수에서 첫번째의 매개 변수에의 암시적인, ID 변환, 참조의 변환 또는 박스화 변환이 존재하지 않는 메서드를 모두 제외합니다.이러한 후보 메서드, 비어 있지 않은 세트를 가져오는 최초의 메서드 그룹이 고쳐 쓸 수 있었던 메서드 호출용으로 선택되는 그룹이며, 최적인 확장 메서드를 후보세트에서 선택하기 위해 일반적인 오버로드 해결 (§7.4.2)이 적용됩니다.모든 시도가 완료해도 후보 메서드 그룹가 비어 있는 경우는 컴파일시 오류가 발생합니다.
상기의 규칙은 인스턴스 메서드가 확장 메서드보다 우선되어 안쪽의 네임 스페이스 선언으로 사용 가능한 확장 메서드가 외부의 네임 스페이스 선언으로 사용 가능한 확장 메서드보다 우선되는 것을 의미합니다.예입니다.
public static class E
{
   public static void F(this object obj, int i) { }
   public static void F(this object obj, string s) { }
}
class A { }
class B
{
   public void F(int i) { }
}
class C
{
   public void F(object obj) { }
}
class X
{
   static void Test(A a, B b, C c) {
      a.F(1);            // E.F(object, int)
      a.F("hello");      // E.F(object, string)
      b.F(1);            // B.F(int)
      b.F("hello");      // E.F(object, string)
      c.F(1);            // C.F(object)
      c.F("hello");      // C.F(object)
   }
}
이 예에서는 B 메서드는 첫번째의 확장 메서드보다 우선되어 C 메서드는 어느 쪽의 확장 메서드보다 우선됩니다.

26.3 lambda 식

C# 2.0 으로 도입된 익명 메서드를 사용하면, 대리자(delegate) 값이 예상되는 개소에서 코드 블록을 "인라인(in-line)" 기술할 수 있습니다.익명 메서드를 사용하면, 함수형 프로그램 언어의 표현력이 대부분이 제공되지만 익명 메서드 구문에는 그 구문이 약간 장황하고 명령적입니다. lambda 식을 사용하면, 보다 간결하고 기능적인 구문으로 익명 메서드를 기술할 수 있습니다.
lambda 식은 매개 변수 리스트예  계속 되는 => 토큰 및 거기에 계속 되는 식 또는 구문 블록으로 기술합니다.
expression:
    assignment
    non-assignment-expression
    
non-assignment-expression:
    conditional-expression
    lambda-expression
    query-expression
    
lambda-expression:
    (   lambda-parameter-listopt   )   =>   lambda-expression-body
    implicitly-typed-lambda-parameter   =>   lambda-expression-body
    
lambda-parameter-list:
    explicitly-typed-lambda-parameter-list
    implicitly-typed-lambda-parameter-list
    explicitly-typed-lambda-parameter-list
    explicitly-typed-lambda-parameter
    explicitly-typed-lambda-parameter-list   ,   explicitly-typed-lambda-parameter

explicitly-typed-lambda-parameter:
    parameter-modifieropt   type   identifier
    implicitly-typed-lambda-parameter-list
    implicitly-typed-lambda-parameter
    implicitly-typed-lambda-parameter-list   ,   implicitly-typed-lambda-parameter

implicitly-typed-lambda-parameter:
    identifier
    
lambda-expression-body:
    expression
    block
=> 연산자의 우선 순위는 대입 (=)과 같습니다.또, 이 연산자는 오른쪽 결합입니다.
lambda 식 매개 변수는 명시적으로 형식 지정하는 일도 암시적으로 형식 지정할 수도 있습니다.명시적으로 형식화된 매개 변수 리스트에서는 각 매개 변수의 형식은 명시적으로 지정됩니다.암시적으로 형식화된 매개 변수 리스트에서는 매개 변수의 형식은 lambda 식이 사용되는 문맥에서 유추됩니다.구체적으로는 lambda 식이 호환성이 있는 대리자(delegate) 형식에 변환될 때, 그 대리자(delegate) 형식에 의해 매개 변수의 형식이 제공됩니다 (§26.3.1).
매개 변수가암시적으로 형식화된 것 하나뿐인 lambda 식에서는 매개 변수 리스트의 괄호를 생략 할 수 있습니다.즉,
( param ) => expr
 lambda 식은 다음과 같이 축약할 수 있습니다.
param => expr
lambda 식 예를 다음에 몇가지 보여줍니다.
x => x + 1                     // 암시적인 형식 지정.expression body가 식.
x => { return x + 1; }         // 암시적인 형식 지정.expression body가 구문.
(int x) => x + 1               // 명시적 형식 지정.expression body가 식.
(int x) => { return x + 1; }   // 명시적 형식 지정.expression body가 구문.
(x, y) => x * y               // 복수의 매개 변수.
() => Console.WriteLine()      // 매개 변수 없음.
「C# 버전 2.0 사양」 §21 에 기재되어 있는 익명 메서드 사양은 lambda 식에서도 대부분 적용됩니다.. lambda 식은 다음과 같은 점을 제외하고, 기능적으로는 익명 메서드를 닮아 있습니다.
  • 익명 메서드에서는 매개 변수 리스트를 완전하게 생략하고, 모든 매개 변수 리스트의 대리자(delegate) 형식으로 변환할 수 있도록 가능합니다.
  • lambda 식에서는 매개 변수의 형식을 생략 해 유추시킬 수 있지만, 익명 메서드에서는 매개 변수의 형식을 명시적으로 지정해야 합니다.
  • lambda 식 expression body에는 식도 구문 블록도 지정할 수 있지만, 익명 메서드 expression body로 지정할 수 있는 것은 구문 블록만입니다.
  • expression body가 식의 lambda 식은 식 트리로 변환할 수 있습니다 (§26.8).

26.3.1 익명 메서드와 lambda 식변환

   이 섹션은 §21.3 에 대신합니다.
anonymous-method-expression lambda 식은 특별한 변환 규칙을 가지는 값으로 해서 분류됩니다.이러한 값에는 형식이 없습니다만, 호환성이 있는 대리자(delegate) 형식에 암시적으로 변환 가능합니다.구체적으로는 다음과 같은 조건에 해당하는 경우에 대리자(delegate) 형식 D 는 익명 메서드 또는 lambda 식 L과 호환성이 있습니다.
  • D 와 L 의 매개 변수 수가 같습니다.
  • L 가 anonymous-method-signature를 포함하지 않는 익명 메서드인 경우, D 의 매개 변수가out 매개 변수 수식자가 지정된 매개 변수 이외이면, D 는 0 개 이상의 어떠한 형식의 매개 변수에서도 가질 수 있습니다.
  • L 가 명시적으로 형식화된 매개 변수 리스트를 가지는 경우, D 의 각 매개 변수의 형식 및 수식자는 L 의 대응하는 매개 변수와 같습니다.
  • L 가 암시적으로 형식화된 매개 변수 리스트를 가지는 lambda 식 경우, D 는 ref 매개 변수 또는 out 매개 변수를 가지지 않습니다.
  • D 의 반환 형식이 void 이며, L expression body가 식의 경우, L 의 각 매개 변수에 D 의 대응하는 매개 변수의 형식이 지정되어 있으면, L expression body는 statement-expression (§8.6)으로서 허가된 유효한 식 (§7 참조)입니다.
  • D 의 반환 형식이 void 이며, L expression body가 구문 블록의 경우, L 의 각 매개 변수에 D 의 대응하는 매개 변수의 형식이 지정되어 있으면, L expression body는 return 구문이 식을 지정하고 있지 않는 유효한 구문 블록 (§8.2 참조)입니다.
  • D 의 반환 형식이 void 이외며, L expression body가 식의 경우, L 의 각 매개 변수에 D 의 대응하는 매개 변수의 형식이 지정되어 있으면, L expression body는 D 의 반환 형식에 암시적으로 변환 가능한 유효한 식 (§7 참조)입니다.
  • D 의 반환 형식이 void 이외의, L expression body가 구문 블록의 경우, L 의 각 매개 변수에 D 의 대응하는 매개 변수의 형식이 지정되어 있으면, L expression body는 각 return 구문이 D 의 반환 형식에 암시적으로 변환 가능한 식을 지정하는 도달 불가능한 끝점(endpoint)를 가지는 유효한 구문 블록 (§8.2 참조)입니다.
다음의 예에서는 형식 A 인수를 받아, 형식 R 값을 돌려주는 함수를 나타내는 제네릭 대리자(delegate) 형식 Func<A,R> 를 사용합니다.
delegate R Func<A,R>(A arg);
다음의 대입식
Func<int,int> f1 = x => x + 1;         // OK
Func<int,double> f2 = x => x + 1;      // OK
Func<double,int> f3 = x => x + 1;      // 오류
그럼, 각 lambda 식 매개 변수와 반환 형식은 lambda 식 대입처의 변수의 형식에서 결정됩니다.첫번째의 대입식에서는 lambda 식이 대리자(delegate) 형식 Func<int,int> 에 정상적으로 변환됩니다.x 에 형식 int 가 지정되어 있는 경우, x + 1 은 형식 int 에 암시적으로 변환 가능한 유효한 식이기때문입니다.같이 2 번째의 대입식에서는 lambda 식이 대리자(delegate) 형식 Func<int,double> 에 정상적으로 변환됩니다.이것은 x + 1 의 결과 (형식 int)는 형식 double 에 암시적으로 변환 가능하기 때문에입니다.그러나, 3 번째의 대입식에서는 컴파일시 오류가 됩니다.이것은 x 에 형식 double 가 지정되어 있는 경우, x + 1 의 결과 (형식 double)는 형식 int 에 암시적으로 변환할 수 없기 때문입니다.

26.3.2 대리자(delegate) 작성식

   이 섹션은 §21.10 에 대신합니다.
대리자(delegate) 작성식 (§7.5.10.3)은 확장되어 인수에는 메서드 그룹으로서 분류되는 식, 익명 메서드나 lambda 식으로서 분류되는 식 또는 대리자(delegate) 형식의 값을 지정할 수 있게 되었습니다.
new D(E)라는 형식 (D 는 delegate-type, E 는 expression)의 delegate-creation-expression 컴파일시 처리는 다음의 순서로 행해집니다.
  • E 가 메서드 그룹의 경우는 E 에서 D 에의 메서드 그룹의 변환 (§21.9)이 존재할 필요가 있어, 대리자(delegate) 작성식은 그 변환과 같은 방법으로 처리됩니다.
  • E 가 익명 메서드 또는 lambda 식 경우는 E 에서 D 에의 익명 메서드 또는 lambda 식변환 (§26.3.1)(이)가 존재할 필요가 있어, 대리자(delegate) 작성식은 그 변환과 같은 방법으로 처리됩니다.
  • E 가 대리자(delegate) 형식의 값의 경우는 E 메서드 서명이 D 와 일치하고 있을 (§21.9) 필요가 있어, 결과는 E 와 같을 호출해 리스트를 참조하는 형식 D 가 새롭게 작성된 대리자(delegate)에의 참조입니다.E 가 D 와 일치하지 않는 경우는 컴파일시 오류가 발생합니다.

26.3.3 형식 유추

   이 섹션은 §20.6.4 에 대신합니다.
형식 인수를 지정하지 않고 제네릭 메서드를 호출하면, 형식 유추 처리에 의해, 그 호출의 형식 인수의 유추가 시도됩니다.형식 유추가 존재하는 것에 의해, 제네릭 메서드 호출로, 보다 편리한 구문을 사용할 수 있게 되어, 프로그래머는 중복 하는 형식 정보를 지정하지 않아도 됩니다.예를 들어, 다음과 같이 메서드를 선언했다고 합니다.
class Chooser
{
   static Random rand = new Random();
   public static T Choose<T>(T first, T second) {
      return (rand.Next(2) == 0)? first: second;
   }
}
이 경우, 형식 인수를 명시적으로 지정하지 않아도 다음과 같이 Choose 메서드를 호출할 수 있습니다.
int i = Chooser.Choose(5, 213);               // Choose<int> 를 호출한다
string s = Chooser.Choose("foo", "bar");      // Choose<string> 를 호출한다
형식 유추에 의해, 메서드 인수에서 형식 인수가 int, string 이라고 결정됩니다.
형식 유추는 메서드 호출 (§20.9.7) 컴파일시 처리의 일환으로서 실행되어 호출의 오버로드 해결 순서 전에 행해집니다.메서드 호출로 특정의 메서드 그룹을 지정해, 메서드 호출의 일부로서 형식 인수를 지정하지 않는 경우, 그 메서드 그룹의 각 제네릭 메서드에는 형식 유추가 적용됩니다.형식 유추가 성공했을 경우, 그 후의 오버로드 해결에서는 인수의 형식을 결정하는데, 유추된 형식 인수가 사용됩니다.오버로드 해결로, 호출하는 메서드로서 제네릭 메서드가 선택되었을 경우, 유추된 형식 인수가 그 호출의 실제의 형식 인수로서 사용됩니다.특정의 메서드 형식 유추가 실패했을 경우는 그 메서드는 오버로드 해결에 관여하지 않습니다.형식 유추가 실패한 것 만으로는 컴파일시 오류는 발생하지 않습니다만, 많은 경우, 형식 유추의 실패에 의해 오버로드 해결로 적절한 메서드가 발견되지 않고, 컴파일시 오류가 발생합니다.
지정한 인수의 수가 메서드 매개 변수의 수와 다른 경우, 유추는 즉시 실패합니다.그 이외의 경우는 제네릭 메서드가 다음과 같은 서명을 가진다고 합니다.
Tr M<X1...Xn>(T1 x1 ... Tm xm)
M(e1...em) 형식 메서드 호출에서는 형식 유추의 역할은 형식 매개 변수 X1...Xn 의 각각 대해 일의의 형식 인수 S1...Sn 를 호출 M<S1...Sn>(e1...em)가 유효하게 되도록 합니다.
유추 처리중에 각 형 매개 변수 Xi 는 특정의 형식 Si 에 고정되는지, 관련지을 수 있었던 범위세트를 가져 고정되지 않습니다.범위의 각각은 어느A 형식 T 입니다.처음은 각 형변수 Xi 는 범위의 빈 세트를 가져 고정되지 않습니다.
형식 유추는 단계적에 행해집니다.각 단계에서는 전 단계의 결과에 근거해 보다 많은 형식 변수의 형식 인수의 유추가 시행됩니다.제 1 단계에서는 범위에 대한 최초의 유추가 몇개인가 행해져 제 2 단계에서는 형식 변수가 특정의 형식에 고정되어 범위가 한층 더 유추됩니다.제 2 단계는 경우에 따라서는 몇번이나 반복되어야 합니다.
   이 이후, 이 기사내에서 "대리자(delegate) 형식"  표현을 사용했을 경우, 이것에는 Expression<D>  형식 (D 는 대리자(delegate) 형식)의 형식도 포함됩니다. Expression<D> 인수와 반환 형식은 D 와 같습니다.
   형식 유추는 제네릭 메서드 호출시인 만큼 행해지는 것은 아닙니다.메서드 그룹의 변환 형식 유추에 대해서는 §26.3.3.12 가 연속된식에 대한 최적인 공통의 형식의 알아내는 방법에 대해서는 §26.3.3.13 이 설명합니다.

26.3.3.1 제 1 단계

메서드 인수 ei 의 각각 붙고,
  • ei 가 lambda 식, 익명 메서드 또는 메서드 그룹의 경우는 Ti 형의 ei 에서 명시적 인수 형식 유추 (§26.3.3.7)(을)를 합니다.
  • ei 가 lambda 식, 익명 메서드, 메서드 그룹의 경우는 Ti 형의 ei 에서 출력 형식 유추 (§26.3.3.6)를 합니다.

26.3.3.2 제 2 단계

Xj 에 "종속성" (§26.3.3.5) 하지 않고 고정되지 않은 형식 변수 Xi 가 모두 고정됩니다 (§26.3.3.10).
이러한 형식 변수가 존재하지 않는 경우, 고정되지 않은 형식 변수 Xi 가운데, 다음의 양쪽 모두에 해당하는 것은 모두 고정됩니다.
  • Xi 에 종속성하는 형식 변수 Xj 가 적어도 1 존재합니다.
  • Xi 는 비어 있지 않은 범위세트를 가지고 있습니다.
이러한 형식 변수가 존재하지 않고, 고정되지 않은 형식 변수가 아직 존재하는 경우는 형식 유추는 실패합니다.고정되지 않은 형식 변수가 그 이상 존재하지 않는 경우는 형식 유추는 성공합니다.그 이외의 경우는 대응하는 인수의 형식 Ti 를 가져, 고정되지 않은 형식 변수 Xj 가 출력 형식 (§26.3.3.4)에는 포함되지만 입력 형식 (§26.3.3.3)에는 포함되지 않은 인수 ei 모두에게 대해서, Ti 형의 ei 에 대한, 출력 형식 유추 (§26.3.3.6)를 합니다.가 제 2 단계가 반복해집니다.

26.3.3.3 입력 형식

e 가 메서드 그룹 또는 암시적으로 형식화된 lambda 식, T 가 대리자(delegate) 형식의 경우는 T 의 인수의 형식은 모두, T 형의 e 입력 형식입니다.

26.3.3.4 출력 형식

e 가 메서드 그룹, 익명 메서드, 구문 Lambda 또는 식 Lambda이며, T 가 대리자(delegate) 형식의 경우는 T 의 반환 형식은 T 형의 e 출력 형식입니다.

26.3.3.5 종속성

Tk 형의 어느A 인수 ek 에 대해서, Tk 형의 ek 입력 형식에 Xj , Tk 형의 ek 의 출력 형식에 Xi 가 있는 경우, 고정되지 않은 형식 변수 Xi 는 고정되지 않은 형식 변수 Xj 에 "직접 종속성" 합니다.
Xj 가 Xi 에 직접 종속성하는지, Xi 가 Xk 에 직접 종속성하여 Xk 가 Xj 에 종속성하는 경우, Xj 는 Xi 에 "종속성" 합니다. 따라서,"종속성하는" 은" 직접 종속하는"  전이적(transitive)이지만, 반사폐포가 아닙니다.

26.3.3.6 출력 형식 유추

T 형식 e 에서 다음과 같은 방법으로 출력 형식 유추를 합니다.
  • e 가유추되는 반환 형식이 U (§26.3.3.11)의 Lambda 또는 익명 메서드여, T 가반환 형식이 Tb 대리자(delegate) 형식인 경우, U 에서 Tb 에 대해서 낮은 바인딩 유추 (§26.3.3.9)를 합니다.
  • 상기에 해당하지 않고, e 가 메서드 그룹이며, T 가매개 변수의 형식이 T1...Tk 의 대리자(delegate) 형식이며, 형식 T1...Tk 의 e 오버로드 해결에 의해, 반환 형식이 U 의 메서드가 1 을   U 에서 Tb 에 대해서 낮은 바인딩 유추를 합니다.
  • 상기의 어느 쪽에도 해당하지 않고, e 가 U 형식의 경우, U 에서 T 에 대해서 낮은 바인딩 유추를 합니다.
  • 상기의 어느 것에도 해당하지 않는 경우, 유추는 행해지지 않습니다.

26.3.3.7 명시적 인수 형식 유추

T 형식 e에서 다음과 같은 방법으로 명시적 인수 형식 유추를 합니다.
  • e 가인수의 형식이 U1...Uk 의 명시적으로 형식화된 lambda 식또는 익명 메서드로, T 가매개 변수의 형식이 V1...Vk 의 대리자(delegate) 형식의 경우, 각 Ui 에 대해서, Ui 에서 대응하는 Vi 에 대한 정확한 유추 (§26.3.3.8)을 합니다.

26.3.3.8 정확한 유추

형식 U 에서 형식 V 에 대한 정확한 유추는 다음과 같이 행해집니다.
  • V 가 고정되지 않은 Xi 의 한개인 경우, Xi 의 범위세트에 U 가 추가됩니다.
  • 상기에 해당하지 않고, U 가 배열형 Ue[...] 가 V 가 같은 차원의 배열형 Ve[...]의 경우, Ue 에서 Ve 에 대한 정확한 유추를 합니다.
  • 상기의 어느 쪽에도 해당하지 않고, V 가 구축된 형식 C<V1...Vk> 로, U 가 구축된 형식 C<U1...Uk> 의 경우, 각 Ui 에서 대응하는 Vi 에 대한 정확한 유추를 합니다.
  • 상기의 어느 것에도 해당하지 않는 경우, 유추는 행해지지 않습니다.

26.3.3.9 낮은 바인딩 유추

형식 U 에서 형식 V 에 대한 낮은 바인딩 유추는 다음과 같이 해 행해집니다.
  • V 가고정되지 않은 Xi 의 한개인 경우, Xi 의 범위세트에 U 가 추가됩니다.
  • 상기에 해당하지 않고, U 가 배열형 Ue[...] 이 V 가 같은 차원의 배열형 Ve[...] 의 경우 또는 U 가 1 차원 배열형 Ue[] 로 V 가 IEnumerable<Ve>, ICollection<Ve>, IList<Ve> 의 몇개의 경우,
    • Ue 가 참조형인 것을 알 수 있고 있는 경우는 Ue 에서 Ve 에 대한 낮은 바인딩 유추를 합니다.
    • 그 이외의 경우는 Ue 에서 Ve 에 대한 정확한 유추를 합니다.
  • 상기의 어느 쪽에도 해당하지 않고, V 가 구축된 형식 C<V1...Vk> 이며, U 에서 C<U1...Uk> 에의 표준 암묵 변환이 존재하는 형식의 일의세트 U1...Uk 가 존재하는 경우는 각 Ui 에서 대응하는 Vi 에 대해서 정확한 유추를 합니다.
  • 상기의 어느 것에도 해당하지 않는 경우, 유추는 행해지지 않습니다.

26.3.3.10 고정

범위세트를 가지는 고정되지 않은 형식 변수 Xi 는 다음과 같이 고정됩니다.
  • "후보의 형식" Uj 세트는 처음은 Xi  범위세트에 포함되는 모든 형식직합입니다.
  • Xi 의 각 범위를 순서에 조사합니다.X 의 각 범위 U 에 대해서, U 에서 표준 암묵 변환이 존재 "하지 않는" 형식 Uj 는 모두 후보 세트에서 제외됩니다.
  • 남아 있는 후보의 형식 Uj 안에 다른 모든 후보의 형식에의 표준 암묵 변환이 존재하는 형식 V 가 존재하는 경우는 Xi 는 V 에 고정됩니다.
  • 그 이외의 경우는 형식 유추는 실패합니다.

26.3.3.11 유추되는 반환 형식

형식 유추 및 오버로드 해결을 위해 lambda 식 또는 익명 메서드인 e 유추되는 반환 형식은 다음과 같이 결정됩니다.
  • e expression body가 식의 경우는 그 식의 형식이 e 유추되는 반환 형식입니다.
  • e expression body가 구문 블록의 경우는 그 블록의 return 구문의 식직합에 최적인 공통의 형식이 있어, 그 형식이 null 형이 아니면, 그 형식이 e 유추되는 반환 형식입니다.
  • 상기의 어느 쪽에도 해당하지 않는 경우는 e 반환 형식은 유추할 수 없습니다.
lambda 식이 관련하는 형식 유추의 예로서 다음과 같은 System.Linq.Enumerable 클래스에서 선언된 Select 확장 메서드를 생각해 봅시다.
namespace System.Linq
{
   public static class Enumerable
   {
      public static IEnumerable<TResult> Select<TSource,TResult>(
         this IEnumerable<TSource> source,
         Func<TSource,TResult> selector)
      {
         foreach (TSource element in source) yield return selector(element);
      }
   }
}
using 절을 사용하여 System.Linq 네임 스페이스를 가져오기 했다고 합니다. string 형의 Name 속성을 가지는 클래스 Customer 가 있다면, 다음과 같이 Select 메서드를 사용하여 고객 리스트의 이름을 선택할 수 있습니다.
List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);
Select 의 확장 메서드 호출 (§26.2.3)은 다음과 같이 정적 메서드 호출을 고쳐 쓰는 것으로 처리됩니다.
IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);
형식 인수를 명시적으로 지정하지 않았기 때문에 형식 유추를 사용하여 형식 인수가 유추됩니다.우선, customers 인수가 source 매개 변수에 관련지을 수 있어 T 는 Customer 이다고 유추됩니다. 앞에서 말한 lambda 식 형식 유추 처리를 사용하고, c 의 형식은 Customer 이다고 유추되어 식 c.Name 가 selector 매개 변수의 반환 형식에 관련지을 수 있고, S 는 string 으로 유추됩니다.따라서, 호출은 이하와 동등합니다.
Sequence.Select<Customer,string>(customers, (Customer c) => c.Name)
또, 결과의 형식은 IEnumerable<string> 입니다.
다음의 예는 lambda 식 형식 유추에 의해, 형식 정보가 제네릭 메서드 호출의 인수간에 어떻게 건네줄 수 있는지를 나타냅니다.다음과 같은 메서드가 있다고 합니다.
static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2) {
   return f2(f1(value));
}
이 경우, 다음의 호출
double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds);
의 형식 유추는 다음과 같이 행해집니다.우선, 인수 "1:15:30" 가 value 매개 변수에 관련지을 수 있어 X 는 string 이다고 유추됩니다.첫번째의 lambda 식 매개 변수 s 의 형식은 string 이다고 유추되어 식 TimeSpan.Parse(s)가 f1 의 반환 형식에 관련지을 수 있고, Y 는 System.TimeSpan 라고 유추됩니다. 마지막으로, 2 번째의 lambda 식 매개 변수 t 의 형식은 System.TimeSpan 이다고 유추되어 식 t.TotalSeconds 가 f2 의 반환 형식에 관련지을 수 있고, Z 는 double 라고 유추됩니다.따라서, 호출의 결과의 형식은 double 입니다.

26.3.3.12 메서드 그룹의 변환 형식 유추

제네릭 메서드 호출과 같이 제네릭 메서드를 포함한 메서드 그룹 M 가 특정의 대리자(delegate) 형식 D 에 대입되는 경우에도, 형식 유추가 적용될 필요가 있습니다. 
Tr M<X1...Xn>(T1 x1 ... Tm xm)
대리자(delegate) 형식 D 에 대입된 메서드 그룹 M 가 있다면, 형식 유추의 역할은 다음 식 D 에 대입 가능하게 되도록, 
M<S1...Sn>
형식 인수 S1...Sn 를 알아내는 것입니다.
제네릭 메서드 호출로의 형식 유추의 알고리즘과는 달라, 이 경우는 인수의 expressions은 존재하지 않고, 어느A 인수의 types 뿐입니다.특히, lambda 식이 없기 때문에 복수 단계의 유추는 불필요합니다.
대신에 모든 Xi 는 고정되지 않다고 보여져 D 의 인수의 형식 Uj 각각에서 M 의 대응하는 매개 변수의 형식 Tj 에 대한 낮은 바인딩 유추를 합니다.어느 Xi 에 관해서도 범위가 발견되지 않는 경우는 형식 유추는 실패합니다.그 이외의 경우는 모든 Xi 는 대응하는 Si (형식 유추의 결과)에 고정됩니다.

26.3.3.13 식직합의 최적 공통 형식

경우에 따라서는 식직합에 대한 공통의 형식을 유추해야 합니다.특히, 암시적으로 형식화된 배열의 요소 형식 및 익명 메서드나 구문 Lambda의 반환 형식은 이 방법으로 밝혀 냅니다.
직감적으로는 e1...em 라는 연속된식이 있다면, 이 유추는 인수에 ei 를 지정해 다음의 메서드
Tr M<X>(X x1 ... X xm)
를 호출하는 것과 동등하다고 생각할 수 있습니다.
엄밀하게는 유추는 고정되지 않은 형식 변수 X 에서 시작한후, X 형의 ei 각각에서 출력 형식 유추를 해 마지막으로, X 가 고정되어 결과적으로 얻을 수 있던 형식 S 가식의 공통의 형식이 됩니다.

26.3.4 오버로드 해결

특정의 상황에 있어서는 인수 리스트내의 lambda 식이 오버로드 해결에 영향을 줍니다.정확한 규칙에 대해서는 §7.4.2.3 을 참조해 주세요.
다음의 예는 Lambda가 오버로드 해결에게 주는 영향을 나타냅니다.
class ItemList<T>: List<T>
{
   public int Sum(Func<T,int> selector) {
      int sum = 0;
      foreach (T item in this) sum += selector(item);
      return sum;
   }
   public double Sum(Func<T,double> selector) {
      double sum = 0;
      foreach (T item in this) sum += selector(item);
      return sum;
   }
}
ItemList<T> 클래스에는 2 개의 Sum 메서드가 있습니다.어느 쪽의 메서드도 selector 인수를 받습니다.이 인수는 합계하는 값을 리스트 항목에서 추출합니다.추출된 값은 int 의 경우와 double 경우가 있어, 같이 합계 결과도 int 또는 double 입니다.
Sum 메서드는 예를 들어, 주문의 명세행의 리스트에서 합계를 계산하는데 사용할 수 있습니다.
class Detail
{
   public int UnitCount;
   public double UnitPrice;
   ...
}
void ComputeSums() {
   ItemList<Detail> orderDetails = GetOrderDetails(...);
   int totalUnits = orderDetails.Sum(d => d.UnitCount);
   double orderTotal = orderDetails.Sum(d => d.UnitPrice * d.UnitCount);
   ...
}
lambda 식d => d.UnitCount 는 Func<Detail,int> 와 Func<Detail,double> 의 양쪽 모두와 호환성이 있으므로, 첫번째의 orderDetails.Sum 의 호출은 어느 쪽의 Sum 메서드에도 적용할 수 있습니다.그러나, 오버로드 해결에서는 첫번째의 Sum 메서드가 선택됩니다.Func<Detail,int> 에의 변환이 Func<Detail,double> 에의 변환보다 적절하기 때문에입니다.
lambda 식d => d.UnitPrice * d.UnitCount 에 의해 생성되는 것이 double 형의 값이므로, 2 번째의 orderDetails.Sum 호출은 2 번째의 Sum 메서드 밖에 적용할 수 없습니다.따라서, 이 호출에서는 오버로드 해결에 의해 2 번째의 Sum 메서드가 선택됩니다.

26.4 개체 이니셜라이저와 컬렉션 이니셜라이저

개체 작성식 (§7.5.10.1)에는 신규 작성된 개체의 멤버 또는 신규 작성된 컬렉션의 요소를 초기화하는 개체 이니셜라이저 또는 컬렉션 이니셜라이저가 포함되는 경우가 있습니다.
object-creation-expression:
    new   type   (   argument-listopt   )   object-or-collection-initializeropt 
    new   type   object-or-collection-initializer
    
object-or-collection-initializer:
    object-initializer
    collection-initializer
개체 작성식에서는 개체 이니셜라이저 또는 컬렉션 이니셜라이저가 포함되는 경우는 생성자의 인수 리스트를 생략 할 수 있습니다.생성자의 인수 리스트  생략 하는 것은 비어있슨 인수 리스트를 지정하여 동등합니다.
개체 이니셜라이저 또는 컬렉션 이니셜라이저가 포함되는 개체 작성식을 실행하려면 , 우선 인스턴스 생성자를 호출해, 다음에 개체 이니셜라이저 또는 컬렉션 이니셜라이저에 의해 지정된 멤버 또는 요소의 초기화를 실행합니다.
개체 이니셜라이저 또는 컬렉션 이니셜라이저에서는 초기화중의 개체 인스턴스를 참조할 수 없습니다.
개체 이니셜라이저와 컬렉션 이니셜라이저를 제네릭으로 정상적으로 분석 하려면 ,§20.6.5 에 기재되어 있는 애매함을 해소하는 토큰의 리스트를 } 토큰에 의해 증강해야 합니다.

26.4.한개체 이니셜라이저

개체 이니셜라이저에서는 개체의 1 이상의 필드나 속성의 값을 지정합니다.
object-initializer:
    {   member-initializer-listopt   }
    {   member-initializer-list   ,   }

member-initializer-list:
    member-initializer
    member-initializer-list   ,   member-initializer

member-initializer:
    identifier   =   initializer-value

initializer-value:
    expression
    object-or-collection-initializer
개체 이니셜라이저는{ 토큰과 } 토큰으로 둘러싸여 콤마로 단락지어진, 연속된멤버 이니셜라이저로 구성됩니다.각 멤버 이니셜라이저에서는 초기화중의 개체의 접근 가능한 필드 또는 속성의 이름을 지정해, 그 후에 등호와 식, 혹은 개체 이니셜라이저 또는 컬렉션 이니셜라이저의 한편을 지정해야 합니다. 개체 이니셜라이저에게, 같은 필드 또는 속성의 멤버 이니셜라이저를 복수 포함하면, 오류가 됩니다.개체 이니셜라이저에서는 초기화중의 신규 작성된 개체를 참조할 수 없습니다.
등호의 뒤에 식이 지정되어 있는 멤버 이니셜라이저는 필드 또는 속성에의 대입 (§7.13.1)과 같은 방법으로 처리됩니다.
등호의 뒤에 개체 이니셜라이저가 지정되어 있는 멤버 이니셜라이저는 "상자가 된 개체 이니셜라이저", 즉 인젝션 개체를 초기화합니다. 필드나 속성에 새로운 값을 대입하는 것이 아니라, 상자가 된 개체 이니셜라이저로의 대입은 필드나 속성의 멤버에의 대입으로서 다루어집니다.상자가 된 개체 이니셜라이저는 값 형식의 속성이나, 독해 전용으로 값 형식의 필드에는 적용할 수 없습니다.
등호의 뒤에 컬렉션 이니셜라이저가 지정되어 있는 멤버 이니셜라이저는 짜넣어 컬렉션을 초기화합니다.필드나 속성에 새로운 컬렉션을 대입하는 것이 아니라, 이니셜라이저로 지정한 요소가필드나 속성에 의해 참조되는 컬렉션에 추가됩니다.필드나 속성은 §26.4.2 이 설명하는 요건을 채우는 컬렉션 형식이어야 합니다.
다음의 클래스는 2 개의 좌표를 가지는 점을 나타냅니다.
public class Point
{
   int x, y;
   public int X { get { return x; } set { x = value; } }
   public int Y { get { return y; } set { y = value; } }
}
Point 인스턴스는 다음과 같이 작성해 초기화할 수 있습니다.
var a = new Point { X = 0, Y = 1 };
이것은 다음과 같은 효과를 가집니다.
var __a = new Point();
__a.X = 0;
__a.Y = 1; 
var a = __a;
__a 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 임시 변수입니다.다음의 클래스는 2 개의 점에서 작성되는 사각형을 나타냅니다.
public class Rectangle
{
   Point p1, p2;
   public Point P1 { get { return p1; } set { p1 = value; } }
   public Point P2 { get { return p2; } set { p2 = value; } }
}
Rectangle 인스턴스는 다음과 같이 작성해 초기화할 수 있습니다.
var r = new Rectangle {
   P1 = new Point { X = 0, Y = 1 },
   P2 = new Point { X = 2, Y = 3 }
};
이것은 다음과 같은 효과를 가집니다.
var __r = new Rectangle();
var __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
__r.P1 = __p1;
var __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
__r.P2 = __p2; 
var r = __r;
__r, __p1, __p2 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 임시 변수입니다.
Rectangle 생성자가 다음과 같이 2 개의 편입의 Point 인스턴스를 할당합니다.
public class Rectangle
{
   Point p1 = new Point();
   Point p2 = new Point();
   public Point P1 { get { return p1; } }
   public Point P2 { get { return p2; } }
}
이 경우, 새로운 인스턴스를 할당하는 대신에 다음의 구문을 사용하고, 편입의 Point 인스턴스를 초기화할 수 있습니다.
var r = new Rectangle {
   P1 = { X = 0, Y = 1 },
   P2 = { X = 2, Y = 3 }
};
이것은 다음과 같은 효과를 가집니다.
var __r = new Rectangle();
__r.P1.X = 0;
__r.P1.Y = 1;
__r.P2.X = 2;
__r.P2.Y = 3;
var r = __r;

26.4.2 컬렉션 이니셜라이저

컬렉션 이니셜라이저에서는 컬렉션의 요소를 지정합니다.
collection-initializer:
    {   element-initializer-list   }
    {   element-initializer-list   ,   }

element-initializer-list:
    element-initializer
    element-initializer-list   ,   element-initializer

element-initializer:
    non-assignment-expression
    {   expression-list   }
컬렉션 이니셜라이저는{ 토큰과 } 토큰으로 둘러싸여 콤마로 단락지어진, 연속된요소 이니셜라이저로 구성됩니다.각 요소 이니셜라이저에서는 초기화중의 컬렉션 개체에 추가하는 요소를 지정합니다.또, 각 요소 이니셜라이저는{ 토큰과 } 토큰으로 둘러싸여 콤마로 단락지어진, 식의 리스트로 구성됩니다.식이 한개만의 요소 이니셜라이저는 중괄호 없이 기술할 수 있지만, 그처럼 기술했을 경우는 대입식으로 할 수 없습니다.멤버 이니셜라이저와의 헷갈림을 피하기 위해서 입니다. non-assignment-expression 작성에 대해서는 §26.3 에서 설명했습니다.
컬렉션 이니셜라이저를 포함한 개체 작성식의 예입니다.
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
컬렉션 이니셜라이저가 적용된 컬렉션 개체는 System.Collections.IEnumerable 를 구현 하는 형식이어야 합니다.그렇지 않은 경우는 컴파일시 오류가 발생합니다.컬렉션 이니셜라이저는 지정된 각 요소에 대해 차례로, 요소 이니셜라이저의 식 리스트를 사용하여 대상 개체에 대해서 Add 메서드를 호출해, 각 호출에 대해서 일반적인 오버로드 해결을 적용합니다.
다음의 클래스는 이름과 전화 번호 리스트로 구성된 연락처를 나타냅니다.
public class Contact
{
   string name;
   List<string> phoneNumbers = new List<string>();
   public string Name { get { return name; } set { name = value; } }
   public List<string> PhoneNumbers { get { return phoneNumbers; } }
}
List<Contact> 는 다음과 같이 작성해 초기화할 수 있습니다.
var contacts = new List<Contact> {
   new Contact {
      Name = "Chris Smith",
      PhoneNumbers = { "206-555-0101", "425-882-8080" }
   },
   new Contact {
      Name = "Bob Harris",
      PhoneNumbers = { "650-555-0199" }
   }
};
이것은 다음과 같은 효과를 가집니다.
var contacts = new List<Contact>();
var __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
contacts.Add(__c1);
var __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
contacts.Add(__c2);
__c1 와 __c2 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 임시 변수입니다.

26.5 익명형식

C# 3.0 에서는 익명형식의 개체를 작성하기 위해 new 연산자를 이름이 없는 개체 이니셜라이저와 함께 사용할 수 있습니다.
primary-no-array-creation-expression:
    ...
    anonymous-object-creation-expression

anonymous-object-creation-expression:
    new   anonymous-object-initializer

anonymous-object-initializer:
    {   member-declarator-listopt   }
    {   member-declarator-list   ,   }

member-declarator-list:
    member-declarator
    member-declarator-list   ,   member-declarator

member-declarator:
    simple-name
    member-access
    identifier   =   expression
이름이 없는 개체 이니셜라이저에서는 익명형식을 선언해, 그 형식의 인스턴스를 돌려줍니다.익명형식은 object 에서 직접 상속하는 무명의 클래스형입니다.익명형식의 멤버는 그 형식의 인스턴스의 작성에 사용되는 개체 이니셜라이저에서 유추되는 일련의 읽기 속성입니다.구체적으로는
new { p1 = e1 , p2 = e2 , ... pn = en }
이름이 없는 개체 이니셜라이저에서는 다음과 같은 형식의 익명형식이 선언됩니다.
class __Anonymous1
{
   private T1 f1 ;
   private T2 f2 ;
   ...
   private Tn fn ;
   public T1 p1 { get { return f1 ; } set { f1 = value ; } }
   public T2 p2 { get { return f2 ; } set { f2 = value ; } }
   ...
   public T1 p1 { get { return f1 ; } set { f1 = value ; } }
}
각 Tx 는 대응하는 식 ex 의 형식입니다.이름이 없는 개체 이니셜라이저안의 식이 null 형이나 unsafe 형의 경우, 컴파일시 오류가 발생합니다.
익명형식의 이름은 컴파일러에 의해 자동적으로 생성되어 프로그램 텍스트내에서 참조할 수 없습니다.
같은 프로그램내의 같은 이름으로 컴파일시 형식도 같은 연속된속성을 같은 순서로 지정하는 이름이 없는 개체 이니셜라이저 2 는 같은 익명형식의 인스턴스를 생성합니다 (리플렉션 등 특정의 상황에서는 속성의 순서가 중요해서, 이 정의에는 속성의 순서가 포함됩니다).
다음은 예를 나타냅니다.
var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;
이 예에서는 p1 와 p2 는 같은 익명형식이므로, 마지막 행의 대입은 허가됩니다.
모든 속성이 동일한 경우만, 같은 익명형식의 2 개의 인스턴스가 동일해지도록, 익명형식의 Equals 메서드와 GetHashcode 메서드는 속성의 Equals 와 GetHashcode 라는 관점에서 정의됩니다..
멤버 선언자는 간이명 (§7.5.2) 또는 멤버 접근 (§7.5.4)의 형식으로 축약할 수 있습니다.이것은 "프로젝션 이니셜라이저" 로 불려 같은 이름을 사용한 속성의 선언과 속성에의 대입의 축약법입니다.구체적으로 보면,
identifier                        expr . identifier
그렇다고 하는 형식의 멤버 선언자는 각각, 이하와 완전히 동등합니다.
identifer = identifier               identifier = expr . identifier
따라서, 프로젝션 이니셜라이저에서는 identifier는 값 뿐만이 아니라, 값의 대입처의 필드나 속성도 선택합니다.직감적으로는 프로젝션 이니셜라이저는 값 뿐만이 아니라 값의 이름도 예측합니다.

26.6 암시적으로 형식화된 배열

배열 작성식 (§7.5.10.2)구문은 확장되어 암시적으로 형식화된 배열 작성식이 지원됩닌다.
array-creation-expression:
    ...
    new   [   ]   array-initializer
암시적으로 형식화된 배열 작성식에서는 배열 인스턴스의 형식은 배열 이니셜라이저로 지정된 요소에서 유추됩니다.구체적으로는 배열 이니셜라이저내의 식의 형식에 의해 형성되는 세트에는 세트에 포함되는 각 형에서 암시적으로 변환 가능한 형식이 한개만 포함되어 있을 필요가 있어, 그 형식이 null 형이 아닌 경우, 그 형식의 배열이 작성됩니다.형식이 한개만 유추할 수 없었던 경우나, 유추된 형식이 null 형의 경우는 컴파일시 오류가 발생합니다.
암시적으로 형식화된 배열 작성식의 예입니다.
var a = new[] { 1, 10, 100, 1000 };            // int[]
var b = new[] { 1, 1.5, 2, 2.5 };            // double[]
var c = new[] { "hello", null, "world” };      // string[]
var d = new[] { 1, "one", 2, "two" };         // 오류
마지막 식에서는 컴파일시 오류가 발생합니다. int 와 string 에서는 어느 쪽에도 암시적으로 변환할 수 없기 때문입니다.이 경우는 명시적으로 형식화된 배열 작성식을 사용하여야 합니다.예를 들어, 형식을 object[] 와 지정합니다.또는 요소의 한개를 공통의 기본형에 캐스트 하는 방법도 있습니다.
암시적으로 형식화된 배열 작성식을 이름이 없는 개체 이니셜라이저와 조합하고, 익명형식으로서 형식화된 데이터 구조를 작성할 수 있습니다.다음은 예를 나타냅니다.
var contacts = new[] {
   new {
      Name = "Chris Smith",
      PhoneNumbers = new[] { "206-555-0101", "425-882-8080" }
   },
   new {
      Name = "Bob Harris",
      PhoneNumbers = new[] { "650-555-0199" }
   }
};

26.7 쿼리 식

쿼리 식은 SQL 나 XQuery 등의 관계형 쿼리 언어 및 계층 쿼리 언어를 닮은 언어에 통합된 쿼리 용무의 구문을 제공합니다.
query-expression:
    from-clause   query-body

from-clause:
    from   typeopt   identifier   in   expression

query-body:
    query-body-clausesopt   select-or-group-clause   query-continuationopt

query-body-clauses:
    query-body-clause
    query-body-clauses   query-body-clause

query-body-clause:
    from-clause
    let-clause
    where-clause
    join-clause
    join-into-clause
    orderby-clause

let-clause:
    let   identifier   =   expression

where-clause:
    where   boolean-expression

join-clause:
    join   typeopt   identifier   in   expression   on   expression   equals   expression 

join-into-clause:
    join   typeopt   identifier   in   expression   on   expression   equals   expression   into   identifier

orderby-clause:
    orderby   orderings

orderings:
    ordering
    orderings   ,   ordering

ordering:
    expression    ordering-directionopt

ordering-direction:
    ascending
    descending

select-or-group-clause:
    select-clause
    group-clause

select-clause:
    select   expression

group-clause:
    group   expression   by   expression

query-continuation:
    into   identifier   query-body
query-expression non-assignment-expression (§26.3  정의 참조)에 분류됩니다.
쿼리 식은 from 절로 시작되어, select 절 또는 group 절로 끝납니다.최초의 from 절의 후에는 from 절, let 절, where 절 또는 join 절을 0 개 이상 계속할 수 있습니다.각 from 절은 특정의 순서로 사용하는 범위 변수를 제기하는 제네레이터입니다.각 let 절에서는 값을 계산해, 그 값을 나타내는 식별자(dentifiers)를 제기합니다.각 where 절은 결과에서 항목을 제외하는 필터입니다.각 join 절에서는 소스 순서의 지정된 키가 다른 순서의 키라고 비교되어 일치하는 페어를 나타냅니다.각 orderby 절에서는 지정한 조건에 따라서 항목을 늘어놓아 바꿉니다.마지막 select 절 또는 group 절에서는 범위 변수의 관점에서 결과의 형식을 지정합니다.마지막으로, into 절은 한개의 쿼리의 결과를 그 다음의 쿼리로의 제네레이터로서 취급하여, 쿼리를 "접합하는" 것에 사용할 수 있습니다.

쿼리 식에서의 애매함

쿼리 식에는 새로운 문맥 키워드 (특정의 문맥으로 특별한 의미를 가지는 식별자(dentifiers))가 몇개인가 포함되어 있습니다.이러한 새로운 문맥 키워드에는 from, join, on, equals, into, let, orderby, ascending, descending, select, group 및 by 가 있습니다.쿼리 식 내부에 기록되어 있는 일에서 이러한 식별자(dentifiers)를 키워드나 간이명이라고 해도 사용하며 생기는 애매함을 피하기 위해 이러한 식별자(dentifiers)는 쿼리 식 내부에 기록되어 있는 일에서는 항상 키워드라고 보여집니다.
따라서, 쿼리 식은 fromidentifier가 시작되어, 그 후에 ;,=,, 이외의 토큰이 계속 되는 모든 식입니다.
쿼리 식 내부에 기록되어 있는 일에서 이러한 단어를 식별자(dentifiers)로서 사용하려면, 프리픽스(Prefix) @ 를 붙이는방법이 있습니다 (§2.4.2).

26.7.1 쿼리 식 변환

C# 3.0 언어에서는 쿼리 식의 정확한 실행 시멘틱스는 지정되지 않습니다.C# 3.0 에서는 쿼리 식은 "쿼리 식 패턴" 에 따르는 메서드 호출에 변환됩니다.구체적으로는 쿼리 식은 §26.7.2 에서 설명하도록, 특정의 서명 및 결과의 형식을 가질 것으로 예상되는 Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupBy 및 Cast 라는 이름의 메서드 호출에 변환됩니다.이러한 메서드는 쿼리 안의 개체의 인스턴스 메서드에서도, 개체의 외부에 있는 확장 메서드에서도 괜찮습니다.이러한 메서드는 쿼리의 실제의 실행을 구현 합니다.
쿼리 식에서 메서드 호출에의 변환은 형식 바인드나 오버로드 해결이 실행되기 전에 행해지는 구문 매핑입니다.변환은 구문적으로 올바른 것이 보장 되지만 변환에 의해 의미적으로 올바른 C# 코드가 생성되는 것은 보장 되지 않습니다.쿼리 식이 변환되면, 그 결과적으로 실행되는 메서드 호출은 일반적인 메서드 호출로 처리되지만 이것에 의해 이번은 오류가 발견되는 경우가 있습니다.예를 들어, 메서드가 존재하지 않는 경우, 인수의 형식이 잘못되어 있는 경우, 메서드가 제네릭으로 형식 유추가 실패했을 경우 등입니다.
쿼리 식은 그 이상의 분해가 불가능하게 될 때까지, 변환을 반복해 실시하여, 처리됩니다.변환은 우선도순에  각 섹션은 전의 섹션의 변환이 완전하게 실행된 것을 전제로 합니다.
특정의 종류의 변환에서는* 에 나타내지는 투명한 식별자(dentifiers)에 의해 범위 변수가 삽입됩니다.투명한 식별자(dentifiers)의 특수한 성질에 대해서는 §26.7.1.7 이 설명합니다.

26.7.1.1 연속된 select 절과 groupby 절


from ... into x ...
연속된 쿼리 식은 다음과 같이 변환됩니다.
from x in ( from ...) ...
이 이후의 섹션에서의 변환은 쿼리에 into 가 없는 것을 전제로 합니다.
다음의 예
from c in customers
group c by c.Country into g
select new { Country = g.Key, CustCount = g.Count() }
는 다음과 같이 변환됩니다.
from g in
   from c in customers
   group c by c.Country
select new { Country = g.Key, CustCount = g.Count() }
이것은 최종적으로는 다음과 같이 변환됩니다.
customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Count() })

26.7.1.2 범위 변수의 명시적 형식

범위 변수의 형식을 명시적으로 지정하는 from 절을 다음에 나타냅니다.
from T x in e
이것은 다음과 같이 변환됩니다.
from x in ( e ) . Cast < T > ( )
범위 변수의 형식을 명시적으로 지정하는 join 절을 다음에 나타냅니다.
join T x in e on k1 equals k2
이것은 다음과 같이 변환됩니다.
join x in ( e ) . Cast < T > ( ) on k1 equals k2
이 이후의 섹션에서의 변환은 쿼리에 범위 변수의 명시적 형식이 없는 것을 전제로 합니다.
다음의 예는
from Customer c in customers
where c.City == "London"
select c
 다음과 같이 변환됩니다.
from c in customers.Cast<Customer>()
where c.City == "London"
select c
이것은 최종적으로는 다음과 같이 변환됩니다.
customers.
Cast<Customer>().
Where(c => c.City == "London")
범위 변수의 명시적 형식은 제네릭 IEnumerable<T> 인터페이스는 아니고 non-generic IEnumerable 인터페이스를 구현 하는 컬렉션을 쿼리 하는데는 편리합니다.위의 예에서는 customers 가 ArrayList 형의 경우는 이것에 해당합니다.

26.7.1.3 역쿼리식

다음의 형식의 쿼리 식이 있다고 합니다.
from x in e select x
이것은 다음과 같이 변환됩니다.
( e ) . Select ( x => x )
다음의 예는 
from c in customers
select c
다음과 같이 변환됩니다.
customers.Select(c => c)
역쿼리식은 자명대로, 소스의 요소를 선택하는 쿼리 식입니다.변환의 뒤의 단계에서는 다른 변환 스텝에 의해 제공된 역쿼리는 소스에 옮겨놓는 것으로 삭제됩니다.다만, 쿼리 식의 결과가 소스 개체 자체가 되지 않게 하는 것은 중요합니다.쿼리 식의 결과가 소스 개체 자체의 경우, 소스의 형식과 ID 가 쿼리 바탕으로 대해 시작되어 버리기때문입니다.거기서, 이 스텝에서는 소스에 대해서 Select 를 명시적으로 호출하여, 소스 코드에 직접 기술된 역쿼리를 보호합니다.이러한 메서드가 절대로 소스 개체 자체를 돌려주지 않게 하는 것은 Select 나 다른 쿼리 연산자의 구현 사람의 책임이 됩니다.

26.7.1.4 from 절, let 절, where 절, join 절, orderby 절

2 번째의 from 절의 후에 select 절이 계속 되는 쿼리 식을 다음에 나타냅니다.
from x1 in e1
from x2 in e2
select v
이것은 다음과 같이 변환됩니다.
( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )
2 번째의 from 절의 후에 select 절 이외의 것이 계속 되는 쿼리 식을 다음에 나타냅니다.
from x1 in e1
from x2 in e2
...
이것은 다음과 같이 변환됩니다.
from * in ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 } )
...
let 절이 있는 쿼리 식을 다음에 나타냅니다.
from x in e
let y = f
...
이것은 다음과 같이 변환됩니다.
from * in ( e ) . Select ( x => new { x , y = f } )
...
where 절이 있는 쿼리 식을 다음에 나타냅니다.
from x in e
where f
...
이것은 다음과 같이 변환됩니다.
from x in ( e ) . Where ( x => f )
...
into 가 없는 join 절이 select 절이 후에 계속 되는 쿼리 식을 다음에 나타냅니다.
from x1 in e1
join x2 in e2 on k1 equals k2
select v
이것은 다음과 같이 변환됩니다.
( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v )
into 가 없는 join 절이 select 절 이외의 것이 후에 계속 되는 쿼리 식을 다음에 나타냅니다.
from x1 in e1
join x2 in e2 on k1 equals k2 
...
이것은 다음과 같이 변환됩니다.
from * in ( e1 ) . Join(
   e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 })
...
into 가 있는 join 절이  select 절이 후에 계속 되는 쿼리 식을 다음에 나타냅니다.
from x1 in e1
join x2 in e2 on k1 equals k2 into g
select v
이것은 다음과 같이 변환됩니다.
( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
into 가 있는 join 절, select 절 이외의 것이 후에 계속 되는 쿼리 식을 다음에 나타냅니다.
from x1 in e1
join x2 in e2 on k1 equals k2 into g
...
이것은 다음과 같이 변환됩니다.
from * in ( e1 ) . GroupJoin(
   e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
...
orderby 절이 있는 쿼리 식을 다음에 나타냅니다.
from x in e
orderby k1 , k2 , ... , kn
...
이것은 다음과 같이 변환됩니다.
from x in ( e ) . 
OrderBy ( x => k1 ) . 
ThenBy ( x => k2 ) .
 ... . 
ThenBy ( x => kn )
...
ordering 절로 descending 방향 인디케이터(indicator)를 지정하면, OrderByDescending 또는 ThenByDescending 의 호출이 대신에 생성됩니다.
이 이후가 계속 되는 변환은 각 쿼리식에 let 절, where 절, join 절, orderby 절은 존재하지 않고, 최초의 from 절이 복수는 존재하지 않는 것을 전제로 합니다.
다음의 예는
from c in customers
from o in c.Orders
select new { c.Name, o.OrderID, o.Total }
 다음과 같이 변환됩니다.
customers.
SelectMany(c => c.Orders,
    (c,o) => new { c.Name, o.OrderID, o.Total }
)
다음의 예는
from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }
다음과 같이 변환됩니다.
from * in customers.
   SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }
이것은 최종적으로는 다음과 같이 변환됩니다.
customers.
SelectMany(c => c.Orders, (c,o) => new { c, o }).
OrderByDescending(x => x.o.Total).
Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })
x 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 컴파일러에 의해 생성된 식별자(dentifiers)입니다.
다음의 예는 
from o in orders
let t = o.Details.Sum(d => d.UnitPrice * d.Quantity)
where t >= 1000
select new { o.OrderID, Total = t }
다음과 같이 변환됩니다.
from * in orders.
   Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
where t >= 1000 
select new { o.OrderID, Total = t }
이것은 최종적으로는 다음과 같이 변환됩니다.
orders.
Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }).
Where(x => x.t >= 1000).
Select(x => new { x.o.OrderID, Total = x.t })
x 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 컴파일러에 의해 생성된 식별자(dentifiers)입니다.
다음의 예는 
from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total }
다음과 같이 변환됩니다.
customers.Join(orders, c => c.CustomerID, o => o.CustomerID,
   (c, o) => new { c.Name, o.OrderDate, o.Total })
다음의 예는 
from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }
다음과 같이 변환됩니다.
from * in customers.
   GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,
      (c, co) => new { c, co })
let n = co.Count()
where n >= 10 
select new { c.Name, OrderCount = n }
이것은 최종적으로는 다음과 같이 변환됩니다.
customers.
GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,
   (c, co) => new { c, co }).
Select(x => new { x, n = x.co.Count() }).
Where(y => y.n >= 10).
Select(y => new { y.x.c.Name, OrderCount = y.n)
x 와 y 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 컴파일러에 의해 생성된 식별자(dentifiers)입니다.
다음의 예는 
from o in orders
orderby o.Customer.Name, o.Total descending
select o
최종적으로는 다음과 같이 변환됩니다.
orders.
OrderBy(o => o.Customer.Name).
ThenByDescending(o => o.Total)

26.7.1.5 select 절

다음의 형식의 쿼리 식이 있다고 합니다.
from x in e select v
이것은 다음과 같이 변환됩니다.
( e ) . Select ( x => v )
다만, v 가 식별자(dentifiers) x 의 경우는 변환은 단지 다음과 같이 됩니다.
( e )
예를 들어, 다음의 예는 
from c in customers.Where(c => c.City == "London")
select c
 단지 다음과 같이 변환됩니다.
customers.Where(c => c.City == "London")

26.7.1.6 groupby 절

다음의 형식의 쿼리 식이 있다고 합니다.
from x in e group v by k
이것은 다음과 같이 변환됩니다.
( e ) . GroupBy ( x => k , x => v )
다만, v 가 식별자(dentifiers) x 의 경우는 변환은 다음과 같이 됩니다.
( e ) . GroupBy ( x => k )
다음의 예는
from c in customers
group c.Name by c.Country
다음과 같이 변환됩니다.
customers.
GroupBy(c => c.Country, c => c.Name)

26.7.1.7 투명한 식별자(dentifiers)

특정의 종류의 변환에서는* 이 나타내지는 투명한 식별자(dentifiers)에 의해 범위 변수가 삽입됩니다.투명한 식별자(dentifiers)는 정식적 언어 기능이 아니고, 단지 쿼리 식 변환 프로세스에서의 중간 스텝으로서 존재합니다.
쿼리의 변환에 의해 투명한 식별자(dentifiers)가 삽입되면, 그 후의 변환 스텝으로 투명한 식별자(dentifiers)가 lambda 식및 이름이 없는 개체 이니셜라이저에게 반영됩니다.이러한 문맥으로, 투명한 식별자(dentifiers)는 다음과 같이 동작합니다.
  • 투명한 식별자(dentifiers)가 lambda 식 매개 변수로서 존재하는 경우, 관련하는 익명형식의 멤버는 자동적으로 lambda 식expression body안의 범위에 들어갑니다.
  • 투명한 식별자(dentifiers)를 가지는 멤버가 범위 안에 있는 경우, 그 멤버도 범위 안에 있는 것이 됩니다.
  • 투명한 식별자(dentifiers)가 이름이 없는 개체 이니셜라이저의 멤버 선언자로서 존재하는 경우, 투명한 식별자(dentifiers)를 가지는 멤버가 제공됩니다.
다음의 예는
from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.Total }
다음과 같이 변환됩니다.
from * in
   from c in customers
   from o in c.Orders
   select new { c, o }
orderby o.Total descending
select new { c.Name, o.Total }
이것은 또한 다음과 같이 변환됩니다.
customers.
SelectMany(c => c.Orders.Select(o => new { c, o })).
OrderByDescending(* => o.Total).
Select(* => new { c.Name, o.Total })
이것은 투명한 식별자(dentifiers)가 소거되면, 이하와 동등합니다.
customers.
SelectMany(c => c.Orders.Select(o => new { c, o })).
OrderByDescending(x => x.o.Total).
Select(x => new { x.c.Name, x.o.Total })
x 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 컴파일러에 의해 생성된 식별자(dentifiers)입니다.
다음의 예는
from c in customers
join o in orders on c.CustomerID equals o.CustomerID
join d in details on o.OrderID equals d.OrderID
join p in products on d.ProductID equals p.ProductID
select new { c.Name, o.OrderDate, p.ProductName }
다음과 같이 변환됩니다.
from * in
   from * in
      from * in
         from c in customers
         join o in orders o c.CustomerID equals o.CustomerID
         select new { c, o }
      join d in details on o.OrderID equals d.OrderID
      select new { *, d }
   join p in products on d.ProductID equals p.ProductID
   select new { *, p }
select new { c.Name, o.OrderDate, p.ProductName }
이것은 또한 다음과 같이 분해됩니다.
customers.
Join(orders, c => c.CustomerID, o => o.CustomerID,
   (c, o) => new { c, o }).
Join(details, * => o.OrderID, d => d.OrderID,
   (*, d) => new { *, d }).
Join(products, * => d.ProductID, p => p.ProductID,
   (*, p) => new { *, p }).
Select(* => new { c.Name, o.OrderDate, p.ProductName })
이것은 최종적으로는 다음과 같이 변환됩니다.
customers.
Join(orders, c => c.CustomerID, o => o.CustomerID,
   (c, o) => new { c, o }).
Join(details, x => x.o.OrderID, d => d.OrderID,
   (x, d) => new { x, d }).
Join(products, y => y.d.ProductID, p => p.ProductID,
   (y, p) => new { y, p }).
Select(z => new { z.y.x.c.Name, z.y.x.o.OrderDate, z.p.ProductName })
x, y, z 는 이러한 방법을 사용하지 않으면 은폐 되어 접근 할 수 없는 컴파일러에 의해 생성된 식별자(dentifiers)입니다.

26.7.2 쿼리 식 패턴

쿼리 식 패턴에서는 쿼리 식을 지원 하기 위해 형식이 구현 할 수 있는 메서드 패턴이 규정됩니다..쿼리 식은 구문 매핑에 의해 메서드 호출에 변환되므로, 쿼리 식 패턴의 구현 방법에 관해서, 형식은 매우 유연합니다.예를 들어, 인스턴스 메서드도 확장 메서드도 호출 구문이 같아서, 패턴의 메서드는 인스턴스 메서드와 확장 메서드의 어느 쪽이라고 해도 구현 할 수 있습니다.또, lambda 식은 대리자(delegate)에도 식 트리에도 변환 가능해서, 메서드는 대리자(delegate)와 식 트리의 어느쪽이나 요청 할 수 있습니다.
쿼리 식 패턴을 지원하는 제네릭형 C<T> 의 추천 되는 형식을 다음에 나타냅니다.매개 변수와 결과의 형식과의 적절한 관계를 나타내기 위해 제네릭형을 사용하지만, non-generic형 패턴을 구현 하는 것도 가능합니다.
delegate R Func<T1,R>(T1 arg1);
delegate R Func<T1,T2,R>(T1 arg1, T2 arg2);
class C
{
   public C<T> Cast<T>();
}
class C<T>
{
   public C<T> Where(Func<T,bool> predicate);
   public C<U> Select<U>(Func<T,U> selector);
   public C<U> SelectMany<U,V>(Func<T,C<U>> selector,
      Func<T,U,V> resultSelector);
   public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
      Func<U,K> innerKeySelector, Func<T,U,V> resultSelector);
   public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
      Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector);
   public O<T> OrderBy<K>(Func<T,K> keySelector);
   public O<T> OrderByDescending<K>(Func<T,K> keySelector);
   public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector);
   public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector,
      Func<T,E> elementSelector);
}
class O<T> : C<T>
{
   public O<T> ThenBy<K>(Func<T,K> keySelector);
   public O<T> ThenByDescending<K>(Func<T,K> keySelector);
}
class G<K,T> : C<T>
{
   public K Key { get; }
}
상기의 메서드에서는 제네릭 대리자(delegate) 형식의 Func<T1, R> 와 Func<T1, T2, R> 를 사용하지만 , 매개 변수나 결과의 형식의 관계가 같으면, 다른 대리자(delegate) 형식 또는 식 트리형도 이와 같이 사용할 수 있습니다.
C<T> 와 O<T> 와의 추천 되는 관계에서는 ThenBy 메서드와 ThenByDescending 메서드는 OrderBy 또는 OrderByDescending 의 결과에 대해서만 사용 가능하다는 것에 주의해 주세요. 또, GroupBy 의 결과의 추천 되는 형식에도 주의해 주세요. 각각의 내부 순서는 추가의 Key 속성을 가집니다.
표준 쿼리 연산자 (다른 시방서로 설명)를 사용하면, System.Collections.Generic.IEnumerable<T> 인터페이스를 구현하는 모든 형식에 쿼리 연산자 패턴을 구현 할 수 있습니다.

26.8 식 트리

식 트리를 사용하면, lambda 식을 실행 가능 코드는 아니고 데이터 구조로서 표현할 수 있습니다.대리자(delegate) 형식 D 에 변환 가능한 lambda 식은 System.Query.Expression<D> 형식 트리에도 변환 가능합니다.lambda 식을 대리자(delegate) 형식으로 변환하면, 실행 가능 코드가 생성되어 대리자(delegate)에 의해 그 코드가 참조되지만 식 트리형으로 변환하면, 식 트리의 인스턴스를 작성하는 코드가 생성됩니다.식 트리는 lambda 식 효율적인 메모리내 데이터 표현으로식의 구조를 알기 쉽고 명백하게 합니다.
다음과 같은 예는 실행 가능 코드로서의 lambda 식와 식 트리로서의 lambda 식을 나타냅니다. Func<int,int> 에의 변환이 존재하므로, Expression<Func<int,int>> 에의 변환도 존재합니다.
Func<int,int> f = x => x + 1;                  // 코드
Expression<Func<int,int>> e = x => x + 1;      // 데이터
이러한 대입에 따라서, 대리자(delegate) f 는 x + 1 을 돌려주는 메서드를 참조해, 식 트리 e 는 식 x + 1 을 표현하는 데이터 구조를 참조합니다.

26.8.1 오버로드 해결

오버로드 해결을 위해 Expression<D> 형에 관해서 특별한 규칙이 있습니다.구체적으로는 적절함의 정의에 다음과 같은 규칙이 더해집니다.
  • D1 가 D2 보다 적절하며  Expression<D1>는 Expression<D2> 보다 적절하고, Expression<D1> 가 Expression<D2> 보다 적절한 것은 그 경우에 한정합니다.
   Expression<D> 와 대리자(delegate) 형식과의 사이에는 적절함의 규칙은 없습니다.

26.9 자동적으로 구현된 속성

많은 경우, 속성은 다음과 같은 예와 같이 지원 필드 간단한 사용에 의해 구현 됩니다.
public Class Point {
   private int x;
   private int y;
   public int X { get { return x; } set { x = value; } }
   public int Y { get { return y; } set { y = value; } }
}
자동적으로 구현된 (자동 구현) 속성을 사용하면, 이 패턴이 자동화됩니다.구체적으로는 비추상 속성 선언으로 세미콜론 접근자 expression body를 보관 유지할 수 있습니다.양쪽 모두의 접근자가 존재할 필요가 있어, 양쪽 모두에 세미콜론 expression body가 필요합니다만, 각각 다른 필요 옵션 수식자를 지정할 수 있습니다.속성을 이와 같이 지정하면, 속성의 지원 필드가 자동적으로 생성되어 그 지원 필드에의 독해나 기입을 실시하는 접근자가 구현 됩니다. 지원 필드 이름은 컴파일러에 의해 생성되어 사용자는 접근 할 수 없습니다.
다음과 같은 선언은 위의 예와 동등합니다.
 public Class Point {
   public int X { get; set; }
   public int Y { get; set; }
}
지원 필드에는 접근 할 수 없기 때문에 지원 필드에의 독해나 기입은 속성 접근자를 통해서 실시해야 합니다.즉, 독해 전용이나 써 전용의 자동 구현 속성은 의미가 없기 때문에 허가되지 않는 것을 의미합니다.다만, 각 접근자에 다른 접근 수준을 설정하는 것은 가능합니다.따라서, 다음과 같이 개인 지원 필드가 가지는 독해 전용 속성 효과를 가져올수 있습니다.
Public class ReadOnlyPoint {
   public int X { get; private set; }
   public int Y { get; private set; }
   public ReadOnlyPoint(int x, int y) { X = x; Y = y; }
}
이 제한은 자동 구현의 속성에 의한, 구조 형식의 명시적 대입은 구조의 표준 생성자를 사용하여서만 실현될 수 있는 것도 의미합니다.이것은 속성 자체에의 대입을 실시하려면, 구조가 명시적으로 대입되어 있어야 합니다.

'IT > C#' 카테고리의 다른 글

C#2.0 Iterators,Partial 클래스,Nullable 타입  (0) 2009.10.07
닷넷 트랜잭션 정리  (0) 2009.09.30
C#2.0 , C# 3.0 New Features  (0) 2009.09.15
[C# 2.0] Generics, Iterator, Anonymous, Partial  (0) 2009.08.24
[.net Framework] System.Activator 객체  (0) 2009.08.24
posted by 방랑군 2009. 8. 24. 11:30
..
 Generics 간단 예제.

001 using System;

002 using System.Collections.Generic;

003 using System.Text;

004

005 namespace DataGenericsType

006 {

007  public struct Point<T>

008  {

009    public T X;

010    public T Y;

011  }

012

013  class Program

014  {

015    static void Main(string[] args)

016    {

017         // 일반정수좌표

018         Point<int> intPoint;

019         intPoint.X = 1;

020         intPoint.Y = 2;

021         Console.WriteLine("정수형 Point 좌표: X({0}), Y좌표({1})", intPoint.X, intPoint.Y);

022         Console.WriteLine("");

023

024          // 부동소수점 정밀도가 필요한 차트좌표

025          Point<double> doublePoint;

026          doublePoint.X = 1.2;

027          doublePoint.Y = 3.4;

028          Console.WriteLine("부동 소수점 Point 좌표: X({0}), Y좌표({1})", doublePoint.X, doublePoint.Y);

029          Console.WriteLine("");

030    }

031  }

032 }

 

'IT > C#' 카테고리의 다른 글

C#2.0 Iterators,Partial 클래스,Nullable 타입  (0) 2009.10.07
닷넷 트랜잭션 정리  (0) 2009.09.30
C#2.0 , C# 3.0 New Features  (0) 2009.09.15
C# 3.0 개요  (0) 2009.09.11
[.net Framework] System.Activator 객체  (0) 2009.08.24
posted by 방랑군 2009. 8. 24. 11:11

System.Activator

:

'IT > C#' 카테고리의 다른 글

C#2.0 Iterators,Partial 클래스,Nullable 타입  (0) 2009.10.07
닷넷 트랜잭션 정리  (0) 2009.09.30
C#2.0 , C# 3.0 New Features  (0) 2009.09.15
C# 3.0 개요  (0) 2009.09.11
[C# 2.0] Generics, Iterator, Anonymous, Partial  (0) 2009.08.24