달력

2

« 2025/2 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
2016. 6. 6. 17:36

C# 컨텍스트 키워드 프로그래밍/C#2016. 6. 6. 17:36

add

 

1. add 컨텍스트 키워드(the add contextual keyword)는 클라이언트 코드가 사용자의 이벤트를 구독(subscribe)할 때 호출되는 사용자 지정 이벤트 접근자(a custom event accessor)를 정의하는데 사용됩니다.

2. 사용자 지정 add 접근자를 제공하는 경우에는 remove 접근자도 제공해야 합니다.

3. add 접근자 를 제공(supply)하는 경우에는 remove 접근자도 제공해야 합니다.

4. 다음 예제에서는 사용자 지어 add 및 remove 접근자가 있는 이벤트를 보여 줍니다.

5. 전체 예제를 보려면 방법: 인터페이스 이벤트 구현을 참조하십시오.

 

class Events : IDrawingObject
{
    event EventHandler PreDrawEvent;

    event EventHandler IDrawingObject.OnDraw
    {
        add
        {
            lock (PreDrawEvent)
            {
                PreDrawEvent += value;
            }
        }
        remove
        {
            lock (PreDrawEvent)
            {
                PreDrawEvent -= value;
            }
        }
    }

}

 

6. 일반적으로 자체 사용자 지정 이벤트 접근자를 제공할 필요는 없습니다.

7. 대부분의 경우 이벤트를 선언할 때 컴파일러에서 자동으로 생성되는 접근자로도 충분합니다.

 

 

 


 

extern alias

 

1. 정규화된 형식 이름이 동일하고 버전만 다른 두 개의 어셈블리를 참조해야 할 수도 있습니다.

2. 예를 들어 동일한 응용 프로그램에서 어셈블리의 버전을 여러 개 사용해야 할 수도 있습니다

3. 외부 어셈블리 별칭을 사용하면 각 어셈블리의 네임스페이스가 별칭으로 명명되어 루트 수준 네임스페이스 안에 래핑(wrapped)되므로 동일한 파일에서 여러 어셈블리 버전을 사용할 수 있습니다.

System_CAPS_note참고

extern 키워드는 메서드 한정자로도 사용되어 비관리 코드로 작성된 메서드를 선언합니다.

4. 정규화된 형식 이름이 동일한 두 개의 어셈블리를 참조하려면 다음과 같이 명령 프롬프트에서 별칭을 지정해야 합니다.

 

/r:GridV1=grid.dll

 

/r:GridV2=grid20.dll

 

5. 이렇게 하면 외부 별칭 GridV1 및 GridV2가 만들어집니다.

6. 이러한 별칭을 한 프로그램에서 사용하려면 extern 키워드를 사용하여 별칭을 참조합니다.

 

extern alias GridV1;

 

extern alias GridV2;

 

7. 각 외부 별칭 선언에서는 전역 네임스페이스와 같은 수준(하위 수준은 제외)에 있는 추가 루트 수준 네임스페이스를 선언(introduce)합니다.

8. 따라서(Thus) 정규화된 이름을 사용하지 않고도 적절한 네임스페이스 별칭을 루트로 하여 모호성 문제 없이(without ambiguity) 각 어셈블리의 형식을 참조할 수 있습니다.

9. 앞의 예제에서 GridV1::Grid는 Grid.dll의 그리드 컨트롤이며 GridV2::Grid는 grid20.dll의 그리드 컨트롤입니다.

 

 

 


 

ascending


1. ascending 컨텍스트 키워드는 쿼리 식의 orderby 절(the orderby clause)에서 사용되어 정렬 순서(the sort order)를 오름차순(from smallest to largest)으로 지정합니다. 

2. ascending은 기본 정렬 순서이므로 지정할 필요가 없습니다.

3. 다음 예제에서는 orderby 절에서 ascending을 사용하는 방법을 보여 줍니다.


IEnumerable<string> sortAscendingQuery =
    from vegetable in vegetables
    orderby vegetable ascending
    select vegetable;





async


1. async 한정자를 사용하여 메서드, 람다 식, 무명 메서드를 비동기(asynchronous)로 지정합니다.

2. 메서드 또는 식에 이 한정자를 사용하면 비동기 메서드라고 합니다.


public async Task<int> ExampleMethodAsync()
{
    // . . . .
}


3. 비동기 프로그래밍이 처음이거나 비동기 메서드가 await 키워드를 사용하여 호출자의 스레드를 차단(block)하지 않고 장기 실행 작업(potentially long-running work)을 수행할 수 있는 방법을 모르는 경우 Async 및 Await를 사용한 비동기 프로그래밍의 지침을 읽어야 합니다.


string contents = await contentsTask;


4. 첫번째 await 식에 도달할 때까지 메서드는 동기적으로 실행되고 대기 중 작업(awaited task)이 완료될 때까지 메서드는 중단됩니다.

5. 다음 단원의 예제에서처럼 그 동안에는 제어가 메서드 호출자에게 반환됩니다.

6. async 키워드에서 수정하는 메서드에 await 식 또는 문이 없는 경우 해당 메서드가 동기적으로 실행됩니다.

7. async 키워드로 한정된 메서드가 await 식 또는 문을 가지고 있지 않다면 해당 메서드는 동기적으로 실행됩니다.

8. await가 포함되지 않은 모든 비동기 메서드에서는 오류가 발생할 수 있으므로 컴파일러 경고가 나타납니다.

9. 컴파일러 경고(수준 1) CS4014를 참조하십시오.

10. async 키워드는 메서드, 람다 식 또는 무명 메서드를 한정할 때만 키워드로 사용됩니다.

11. 모든 다른 컨텍스트에서는(In all other contexts) 식별자로 해석됩니다.

12. 다음 예제에서는 비동기 이벤트 처리기 StartButton_Click과 비동기 메서드 ExampleMethodAsync 간의 제어 흐름과 구조를 보여 줍니다.

13. 비동기 메서드의 결과는 다운로드한 웹 사이트의 길이입니다.

14. 이 코드는 Visual Studio 2013에서 만든 WPF(Windows Presentation Foundation) 앱 또는 Windows 스토어 앱에 적합합니다(is suitable for).

15. 앱을 설정하는 방법(setting up the apple)은 코드 주석을 참조하십시오.


// You can run this code in Visual Studio 2013 as a WPF app or a Windows Store app.
// You need a button (StartButton) and a textbox (ResultsTextBox).
// Remember to set the names and handler so that you have something like this:
// <Button Content="Button" HorizontalAlignment="Left" Margin="88,77,0,0" VerticalAlignment="Top" Width="75"
//         Click="StartButton_Click" Name="StartButton"/>
// <TextBox HorizontalAlignment="Left" Height="137" Margin="88,140,0,0" TextWrapping="Wrap" 
//          Text="TextBox" VerticalAlignment="Top" Width="310" Name="ResultsTextBox"/>

// To run the code as a WPF app:
//    paste this code into the MainWindow class in MainWindow.xaml.cs,
//    add a reference to System.Net.Http, and
//    add a using directive for System.Net.Http.

// To run the code as a Windows Store app:
//    paste this code into the MainPage class in MainPage.xaml.cs, and
//    add using directives for System.Net.Http and System.Threading.Tasks.

private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // ExampleMethodAsync returns a Task<int>, which means that the method
    // eventually produces an int result. However, ExampleMethodAsync returns
    // the Task<int> value as soon as it reaches an await.
    ResultsTextBox.Text += "\n";
    try
    {
        int length = await ExampleMethodAsync();
        // Note that you could put "await ExampleMethodAsync()" in the next line where
        // "length" is, but due to when '+=' fetches the value of ResultsTextBox, you
        // would not see the global side effect of ExampleMethodAsync setting the text.
        ResultsTextBox.Text += String.Format("Length: {0}\n", length);
    }
    catch (Exception)
    {
        // Process the exception if one occurs.
    }
}

public async Task<int> ExampleMethodAsync()
{
    var httpClient = new HttpClient();
    int exampleInt = (await httpClient.GetStringAsync("http://msdn.microsoft.com")).Length;
    ResultsTextBox.Text += "Preparing to finish ExampleMethodAsync.\n";
    // After the following return statement, any method that's awaiting
    // ExampleMethodAsync (in this case, StartButton_Click) can get the 
    // integer result.
    return exampleInt;
}
// Output:
// Preparing to finish ExampleMethodAsync.
// Length: 53292


System_CAPS_important중요

작업 및 작업 완료를 기다리는 동안 실행되는 코드에 대한 자세한 내용은 Async 및 Await를 사용한 비동기 프로그래밍(C# 및 Visual Basic)을 참조하십시오. 비슷한 요소를 사용하는 전체 WPF 예제를 보려면 연습: Async 및 Await를 사용하여 웹에 액세스(C# 및 Visual Basic)을 참조하십시오.  개발자 코드 샘플에서 연습 코드를 다운로드할 수 있습니다.  

16. 비동기 메서드의 반환 형식은 Task, Task<TResult> 또는 void일 수 있습니다.

17. 이 메서드는 모든 ref 또는 out 매개 변수를 선언할 수 없지만, 이러한 매개 변수가 있는 메서드를 호출할 수는 있습니다.

18. 메서드의 return 문에서 TResult 형식의 피연산자를 지정할 경우 비동기 메서드의 반환 형식으로 Task<TResult>를 지정합니다.

19. 메서드가 완료 되었을 때 의미 있는 값이 반환되지 않을 경우 Task를 사용합니다.

20. 즉, 이 메서드를 호출하면 Task가 반환되지만 Task가 완료되면 Task를 기다리는 모든 await 식이 void가 됩니다.

21. void 반환 형식은 주로 해당 반환 형식이 필요한 이벤트 처리기를 정의할 때 사용합니다.

22. void 반환 비동기 메서드의 호출자는 기다릴 수 없으므로 메서드가 throw하는 예외를 catch할 수 없습니다.

23. 자세한 내용과 예제를 보려면 비동기 반환 형식을 참조하십시오.





await


1. await 연산자는 비동기 메서드의 작업에 적용되어 대기 중인 작업이 완료될 때까지 메서드의 실행을 일시 중단합니다.

2. 작업은 진행 중인 작업(ongoing work)을 나타냅니다.

3. await가 사용되는 비동기 메서드는 async 키워드로 한정해야 합니다.

4. async 한정자를 사용하여 정의하고 일반적으로 하나 이상의 await 식을 포함하는 이러한 메서드를 비동기 메서드라고 합니다.

System_CAPS_note참고

async 및 await 키워드는 Visual Studio 2012에서 도입되었습니다. 비동기 프로그래밍에 대한 소개는 Async 및 Await를 사용한 비동기 프로그래밍(C# 및 Visual Basic)을 참조하세요.

5. 일반적으로 await 연산자가 적용되는 작업은 작업 기반 비동기 패턴(영문)을 구현하는 메서드를 호출하여 얻은 반환 값입니다.

6. 예를 들어 Task 또는 Task<TResult> 형식의 값이 포함됩니다.

7. 다음 코드에서 HttpClient 메서드 GetByteArrayAsync는 Task<byte[]>, getContentsTask를 반환합니다.

8. 작업은 작업이 완료될 때 실제 바이트 배열을 생성하기 위한 약속입니다.

9. await 연산자는 getContentsTask에 적용되어 getContentsTask가 완료될 때가지 SumPageSizesAsync의 실행을 일시 중단합니다.

10. 동시에(in the meantime) 컨트롤은 SumPageSizesAsync 호출자에게 반환됩니다.

11. getContentsTask가 완료되면 await 식이 바이트 배열로 계산됩니다.


private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}


System_CAPS_important중요

전체 예제는 연습: Async 및 Await를 사용하여 웹에 액세스(C# 및 Visual Basic)을 참조하세요. 샘플은 Microsoft 웹 사이트의 개발자 코드 샘플(영문)에서 다운로드할 수 있습니다. 예제는 AsyncWalkthrough_HttpClient 프로젝트에 있습니다.

12. 이전 예제와 같이 await가 Task<TResult>를 반환하는 메서드 호출의 결과에 적용되는 경우 await식의 형식은 TResult입니다.

13. await가 Task를 반환하는 메서드 호출의 결과에 적용되는 경우 await식의 형식은 void입니다.

14. 다음 예제에서 차이점을 보여 줍니다(illustrate).


// Keyword await used with a method that returns a Task<TResult>.
TResult result = await AsyncMethodThatReturnsTaskTResult();

// Keyword await used with a method that returns a Task.
await AsyncMethodThatReturnsTask();


15. await 식은 해당 식이 실행되고 있는 스레드를 차단하지 않습니다.

16. 대신 컴파일러가 대기 중인 작업에서 연속된 작업으로 비동기 메서드의 나머지 부분을 등록(sign up)하도록 합니다.

17. 그런 다음 컨트롤이 비동기 메서드 호출자에게 반환됩니다.

18. 작업이 완료되면 해당 연속 작업이 호출되고 중단된 비동기 메서드의 실행이 다시 시작됩니다.

19. await 식은 바로 바깥쪽의 메서드(an immediately enclosing method), 람다 식 또는 async 한정자로 표시되는 무명 메서드의 본문에서만 발생할 수 있습니다.

20. await라는 용어(the term)는 해당 컨텍스트에서만 키워드 역할을 합니다(serve as).

21. 다른 컨텍스트에서는(Elsewhere) 식별자로 해석됩니다(interprete).

22. 메서드, 람다 식, 무명 메서드 내에서 await 식은 동기 함수의 본문, 쿼리 식, lock 문의 블록 또는 안전하지 않은 컨텍스트에서 발생할 수 없습니다.

23. 대부분의 비동기 메서드는 Task 또는 Task<TResult>를 반환합니다.

24. 반환된 작업의 속성은 작업의 완료 여부, 비동기 메서드가 예외를 발생시켰는지 취소되었는지 여부 및 최종 결과 등 해당 상태(status) 및 기록(history)에 대한 정보를 전달합니다.

25. await 연산자는 해당 속성에 액세스합니다.

26. 예외를 발생시키는 작업 반환 비동기 메서드를 기다릴 경우 await 연산자는 예외를 다시 throw합니다.

27. 취소된 작업 반환 비동기 메서드를 기다릴 경우 await 연산자는 OperationCanceledException을 다시 throw합니다.

28. 오류가 발생한 상태(in a faulted state)의 단일 작업에는 여러 예외가 반영될 수 있습니다

29. 예를 들어 작업은 Task.WhenAll 호출의 결과일 수 있습니다.

30. 이러한 작업을 기다릴 경우 await 작업에서 예외 중 하나만 다시 throw합니다.

31. 그러나 다시 throw되는 예외를 예측할 수 없습니다.

32. 비동기 메서드에서 오류 처리에 대한 예제는 try-catch를 참조하세요.

33. 다음 윈도우 폼 예제는 비동기 메서드 WaitAsynchronouslyAsync에서의 await 사용법을 보여줍니다.

34. 해당 메서드의 동작을 WaitSynchronously의 동작과 대조합니다.

35. await 연산자가 작업에 적용되지 않으면 해당 정의에서 async 한정자를 사용하고 본문에서 Thread.Sleep을 호출하더라도 WaitSynchronously가 동기적으로 실행됩니다.


private async void button1_Click(object sender, EventArgs e) { // 비동기적으로 작동되는 메서드 호출 string result = await WaitAsynchronouslyAsync(); // 동기적으로 작동되는 메서드 호출 //string result = await WaitSynchronously (); // 결과 표시 textBox1.Text += result; } // 다음 메서드는 비동기적으로 실행됩니다. // UI 스레드는 delay 동안 차단되지 않습니다. // Task.Delay가 실행되는 동안 Form1 윈도우는 움직이거나 리사이즈할 수 있습니다. public async Task<string> WaitAsynchronouslyAsync() { await Task.Delay(10000); return "Finished"; } // async 한정자의 사용에도 불구하고 다음 메서드는 동기적으로 실행됩니다. // UI 스레드가 차단되었기 때문에 Thread.Sleep이 실행되는 동안 Form1 윈도우를 이동하거나 리사이즈할 수 없습니다. public async Task<string> WaitSynchronously() { // Add a using directive for System.Threading. Thread.Sleep(10000); return "Finished"; }





decending


1. decending 컨텍스트 키워드는 쿼리 식의 orderby 절에서 사용되어 정렬 순서를 내림차순으로 지정합니다.

2. 다음 예제에서는 orderby 절에서 decending을 사용하는 방법을 보여 줍니다.


IEnumerable<string> sortDescendingQuery =
    from vegetable in vegetables
    orderby vegetable descending
    select vegetable;





dynamic


1. dynamic 형식을 사용하면 컴파일 타임 형식 검사를 무시하는(bypass) 작업을 수행할 수 있습니다.

2. 대신 이러한 작업은 런타임에 확인됩니다(resolve).

3. dynamic 형식은 Office Automation API와 같은 COM API, IronPython 라이브러리와 같은 동적 API 및 HTML DOM(문서 개체 모델)에 대한 액세스를 간소화합니다.

4. 형식 dynamic은 대부분의 환경에서(in most circumstances) 형식 object 같이 동작합니다.

5. 그러나 형식 dynamic의 식이 포함된 작업은 컴파일러에서 형식이 확인되지 않습니다.

6. 컴파일러는 작업에 대한 정보를 함께 패키지하고 해당 정보는 나중에 런타임에서 작업을 평가하는 데 사용됩니다.

7. 프로세스의 일부로 형식 dynamic 변수는 object 형식 변수로 컴파일됩니다.

8. 따라서 형식 dynamic은 런타임이 아닌 컴파일 타임에만 존재합니다.

9. 다음 예제는 형식 dynamic의 변수를 형식 object의 변수와 대조합니다.

10. 컴파일 시간에 각 변수 형식을 확인하려면 WriteLine 문에서 dyn 또는 obj 위에 마우스 포인터를 올려 놓습니다.

11. IntelliSense는 dyn에 대해 동적을, obj에 대해 개체를 보여줍니다.


class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

12. WriteLine 문은 dyn 및 obj 런타임 형식을 표시합니다.

13. 이 시점에서는 모두 동일한 형식 정수입니다.

14. 다음 출력이 생성됩니다.


System.Int32


System.Int32


15. 컴파일 시간에 dyn 및 obj 사이의 차이를 확인하려면 앞의 에제에서 선언과 WriteLine 문 사이에 다음 두 줄을 추가합니다.


dyn = dyn + 3;
obj = obj + 3;


16. 식 obj + 3에 정수와 객체를 추가혀려고 시도한 경우 컴파일러 오류가 보고됩니다.

17. 그러나 dyn + 3에 대해서는 오류가 보고되지 않습니다.

18. dyn이 포함된 식은 dyn의 형식이 dynamic이기 때문에 컴파일할 때 확인되지 않습니다.

19. dynamic 키워드는 다음과 같은 경우에 직접 또는 생성된 형식의 구성 요소로 나타날 수 있습니다.

20. 선언에서 속성, 필드, 인덱서, 매개 변수, 반환 값, 지역 변수 또는 형식 제약 조건(constraint)의 입력으로 사용합니다.

21. 다음 클래스 정의는 여러 다른 선언에서 dynamic을 사용합니다.


class ExampleClass
{
    // A dynamic field.
    static dynamic field;

    // A dynamic property.
    dynamic prop { get; set; }

    // A dynamic return type and a dynamic parameter type.
    public dynamic exampleMethod(dynamic d)
    {
        // A dynamic local variable.
        dynamic local = "Local variable";
        int two = 2;

        if (d is int)
        {
            return local;
        }
        else
        {
            return two;
        }
    }
}


22. 명시적 형식 변환에서 변환의 대상 형식으로 사용합니다.


static void convertToDynamic()
{
    dynamic d;
    int i = 20;
    d = (dynamic)i;
    Console.WriteLine(d);

    string s = "Example string.";
    d = (dynamic)s;
    Console.WriteLine(d);

    DateTime dt = DateTime.Today;
    d = (dynamic)dt;
    Console.WriteLine(d);

}
// Results:
// 20
// Example string.
// 2/17/2009 9:12:00 AM


23. 모든 컨텍스트에서 형식은 is 연산자 또는 as 연산자의 오른쪽에 있는 것 같은 값 또는 생성된 형식의 일부로 typeof에 대한 인수로 사용됩니다.

24. 예를 들어, dynamic을 다음 식에 사용할 수 있습니다.


int i = 8;
dynamic d;
// With the is operator.
// The dynamic type behaves like object. The following
// expression returns true unless someVar has the value null.
if (someVar is dynamic) { }

// With the as operator.
d = i as dynamic;

// With typeof, as part of a constructed type.
Console.WriteLine(typeof(List<dynamic>));

// The following statement causes a compiler error.
//Console.WriteLine(typeof(dynamic));


25. 다음 예제의 여러 선언에서 dynamic을 사용합니다.

26. Main 메서드는 또한 컴파일 타임 형식 검사를 런타임 형식 검사와 대조합니다.


using System;

namespace DynamicExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            ExampleClass ec = new ExampleClass();
            Console.WriteLine(ec.exampleMethod(10));
            Console.WriteLine(ec.exampleMethod("value"));

            // The following line causes a compiler error because exampleMethod
            // takes only one argument.
            //Console.WriteLine(ec.exampleMethod(10, 4));

            dynamic dynamic_ec = new ExampleClass();
            Console.WriteLine(dynamic_ec.exampleMethod(10));

            // Because dynamic_ec is dynamic, the following call to exampleMethod
            // with two arguments does not produce an error at compile time.
            // However, itdoes cause a run-time error. 
            //Console.WriteLine(dynamic_ec.exampleMethod(10, 4));
        }
    }

    class ExampleClass
    {
        static dynamic field;
        dynamic prop { get; set; }

        public dynamic exampleMethod(dynamic d)
        {
            dynamic local = "Local variable";
            int two = 2;

            if (d is int)
            {
                return local;
            }
            else
            {
                return two;
            }
        }
    }
}
// Results:
// Local variable
// 2
// Local variable


27. 자세한 내용과 예제를 보려면 dynamic 형식 사용을 참조하십시오.





from


1. 쿼리 식은 from 절로 시작해야 합니다.

2. 또한(Additionally) 쿼리 식은 마찬가지로 from 절로 시작하는 하위 쿼리를 포함할 수 있습니다.

3. from 절은 다음을 지정합니다.

    1) 쿼리 또는 하위 쿼리가 실행되는 데이터 소스

    2) 소스 시퀀스의 각 요소를 나타내는 지역 범위 변수(a local range variable)

4. 범위 변수와 데이터 소스는 둘 다 강력한 형식(is strongly typed)입니다.

5. from 절에서 참조되는 데이터 소스는 IEnumerable 또는 IEnumerable<T> 형식이나 IQueryable<T>과 같은 파생 형식이어야 합니다.

6. 다음 예제에서 numbers는 데이터 소스이고 num은 범위 변수입니다.

7. var 키워드가 사용되어도 두 변수는 모두 강력한 형식입니다.


class LowNums
{
    static void Main()
    {   
        // A simple data source.
        int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

        // Create the query.
        // lowNums is an IEnumerable<int>
        var lowNums = from num in numbers
            where num < 5
            select num;

        // Execute the query.
        foreach (int i in lowNums)
        {
            Console.Write(i + " ");
        }
    }        
}
// Output: 4 1 3 2 0


8. 컴파일러는 데이터 소스가 IEnumerable<T>을 구현할 경우 범위 변수의 형식을 유추합니다.

9. 예를 들어, 소스의 형식이 IEnumerable<Customer>일 경우 범위 변수는 Customer로 유추됩니다(infer).

10. 소스가 ArrayList와 같은 제네릭이 아닌 IEnumerable 형식일 경우에만 형식을 명시적으로 지정하면 됩니다.

11. 자세한 내용은 방법: LINQ를 사용하여 ArrayList 쿼리를 참조하십시오.

12. 위의 예제에서 num은 int 형식으로 유추됩니다.

13. 범위 변수가 강력한 형식이므로 범위 변수에서 메서드를 호출하거나 다른 작업에서 범위 변수를 사용할 수 있습니다.

14. 예를 들어, select num을 작성하는 대신에 select num.ToString()을 작성하여 쿼리 식에서 정수 대신 문자열 시퀀스를 반환하게 할 수 있습니다.

15. 또는 select n + 10을 작성하여 식에서 14, 11, 13, 12, 10 시퀀스를 반환하게 할 수 있습니다.

16. 자세한 내용은 select 절을 참조하십시오.

17. 범위 변수가 소스의 데이터를 실제로 저장하지 않는다는 매우 중요한 차이점을 제외하면 범위 변수는 foreach 문의 반복 변수와 같습니다.

18. 범위 변수는 단순히 쿼리 실행 시에 발생하는 작업을 쿼리에서 설명할 수 있게 하는 구문상의 편리함(a syntactic convenience)을 제공합니다.

19. 자세한 내용은 LINQ 쿼리 소개를 참조하십시오.

20. 경우에 따라 소스 시퀀스의 각 요소 자체가 시퀀스이거나 시퀀스를 포함할 수 있습니다.

21. 예를 들어, 시퀀스의 각 학생 개체에 테스트 점수 목록이 포함된 IEnumerable<Student>가 데이터 소스일 수 있습니다.

22. 각 Student 요소 내에 내부 목록(the inner list)에 액세스하려면 복합 from 절(compound from clauses)을 사용합니다.

23. 이 기술(the technique)은 중첩된 foreach 문을 사용하는 것과 같습니다.

24. where 또는 orderby 절을 둘 중 하나의 from 절에 추가하여 결과를 필터링할 수 있습니다.

25. 다음 예제에서는 테스트 점수를 나타내는 정수의 내부 List를 각각 포함하는 Student 개체의 시퀀스를  보여 줍니다.

26. 내부 목록에 액세스 하려면 복합 from 절을 사용합니다.

27. 필요한 경우 두 개의 from 절 사이에 다른 절을 삽입할 수 있습니다.


class CompoundFrom
{
    // The element type of the data source.
    public class Student
    {
        public string LastName { get; set; }
        public List<int> Scores {get; set;}
    }

    static void Main()
    {

        // Use a collection initializer to create the data source. Note that 
        // each element in the list contains an inner sequence of scores.
        List<Student> students = new List<Student>
        {
           new Student {LastName="Omelchenko", Scores= new List<int> {97, 72, 81, 60}},
           new Student {LastName="O'Donnell", Scores= new List<int> {75, 84, 91, 39}},
           new Student {LastName="Mortensen", Scores= new List<int> {88, 94, 65, 85}},
           new Student {LastName="Garcia", Scores= new List<int> {97, 89, 85, 82}},
           new Student {LastName="Beebe", Scores= new List<int> {35, 72, 91, 70}} 
        };        

        // Use a compound from to access the inner sequence within each element.
        // Note the similarity to a nested foreach statement.
        var scoreQuery = from student in students
                         from score in student.Scores
                            where score > 90
                            select new { Last = student.LastName, score };

        // Execute the queries.
        Console.WriteLine("scoreQuery:");
        // Rest the mouse pointer on scoreQuery in the following line to 
        // see its type. The type is IEnumerable<'a>, where 'a is an 
        // anonymous type defined as new {string Last, int score}. That is,
        // each instance of this anonymous type has two members, a string 
        // (Last) and an int (score).
        foreach (var student in scoreQuery)
        {
            Console.WriteLine("{0} Score: {1}", student.Last, student.score);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }       
}
/*
scoreQuery:
Omelchenko Score: 97
O'Donnell Score: 91
Mortensen Score: 94
Garcia Score: 97
Beebe Score: 91
*/


28. 복합 from 절은 단일 데이터 소스의 내부 컬렉션에 액세스하는 데 사용됩니다. 그러나 독립 데이터 소스의 추가 쿼리를 생성하는 여러 from 절이 쿼리에 포함될 수도 있습니다.

29. 그러나 독립 데이터 소스의 추가 쿼리(supplemental queries)를 생성하는 여러 from 절이 쿼리에 포함될 수도 있습니다.

30. 이 기술을 사용하면 join 절로 수행할 수 없는 특정 유형의 조인 작업을 수행할 수 있습니다.

31. 다음 예제에서는 두 개의 from 절을 사용하여 두 데이터 소스의 완전한 크로스 조인을 구성하는 방법을 보여 줍니다.


class CompoundFrom2
{
    static void Main()
    {
        char[] upperCase = { 'A', 'B', 'C' };
        char[] lowerCase = { 'x', 'y', 'z' };

        // The type of joinQuery1 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery1 =
            from upper in upperCase
            from lower in lowerCase
            select new { upper, lower };

        // The type of joinQuery2 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery2 =
            from lower in lowerCase
            where lower != 'x'
            from upper in upperCase
            select new { lower, upper };


        // Execute the queries.
        Console.WriteLine("Cross join:");
        // Rest the mouse pointer on joinQuery1 to verify its type.
        foreach (var pair in joinQuery1)
        {
            Console.WriteLine("{0} is matched to {1}", pair.upper, pair.lower);
        }

        Console.WriteLine("Filtered non-equijoin:");
        // Rest the mouse pointer over joinQuery2 to verify its type.
        foreach (var pair in joinQuery2)
        {
            Console.WriteLine("{0} is matched to {1}", pair.lower, pair.upper);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
        Cross join:
        A is matched to x
        A is matched to y
        A is matched to z
        B is matched to x
        B is matched to y
        B is matched to z
        C is matched to x
        C is matched to y
        C is matched to z
        Filtered non-equijoin:
        y is matched to A
        y is matched to B
        y is matched to C
        z is matched to A
        z is matched to B
        z is matched to C
        */


32. 여러 from 절을 사용하는 조인 작업에 대한 자세한 내용은 방법: 사용자 지정 조인 작업 수행을 참조하십시오.





get


1. get 키워드는 속성 값 또는 인덱서 요소를 검색(retrieve)하는 접근자 메서드(an accessor method)를 속성 또는 인덱서에 정의합니다.

2. 자세한 내용은 속성, 자동으로 구현된 속성인덱서를 참조하십시오.

3. 다음은 Secnods라는 속성의 get 접근자 예제입니다.


class TimePeriod
{
    private double _seconds;
    public double Seconds
    {
        get { return _seconds; }
        set { _seconds = value; }
    }
}


4. 다음은 자동으로 구현된 속성의 get 접근자에 대한 예제입니다.


class TimePeriod2
{
    public double Hours { get; set; }
}





global


1. global 컨텍스트 키워드가 :: 연산자 앞에 배치되는 경우 이 키워드는 모든 C# 프로그램의 기본 네임스페이스이며 다른 경우에는 명명되지 않는 전역 네임스페이스를 가리킵니다.

2. 자세한 내용은 법: 전역 네임스페이스 별칭 사용을 참조하십시오.

3. 다음 예제에서는 global 컨텍스트 키워드를 사용하여 클래스 TestApp가 전역 네임스페이스에서 정의되도록 지정하는 방법을 보여 줍니다.


class TestClass : global::TestApp { }





group


1. group 절은 그룹의 키 값과 일치하는 하나 이상의 항목을 포함하는 IGrouping<TKey, TElement> 개체 시퀀스를 반환합니다.

2. 예를 들어 각 문자열의 첫글자에 따라 문자열 시퀀스를 그룹화할 수 있습니다.

3. 이 경우 첫 글자가 키가 되고 키의 형식은 char이며 각 IGrouping<TKey, TElement> 개체의 Key 속성에 저장됩니다.

4. 컴파일러가 키의 형식을 유추(infer)합니다.

5. 다음 예제와 같이 group 절을 사용하여 쿼리 식을 끝낼 수 있습니다.


// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
    from student in students
    group student by student.Last[0];


6. 각 그룹에서 추가 쿼리 작업(additional query operations)을 수행(perform)하려면 into 컨텍스트 키워드를 사용하여 임시 식별자(a temporary identifier)를 지정할 수 있습니다.

7. into를 사용하는 경우 다음 발췌(excerpt) 내용과 같이 쿼리를 계속하여 select 문이나 다른 group 절로 끝내야 합니다.


// Group students by the first letter of their last name
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery2 =
    from student in students
    group student by student.Last[0] into g
    orderby g.Key
    select g;


8. into를 사용하거나 사용하지 않고 group을 사용하는 방법을 보여 주는 보다 완전한 예제는 이 항목의 예제 단원을 참조하십시오.

9. group 쿼리의 의해 생성된 IGrouping<TKey, TElement> 개체는 기본적으로(essentially) 목록의 목록이므로 foreach 루프를 사용하여 각 그룹의 항목에 액세스해야 합니다.

10. 바깥쪽 루프(the outer loop)는 그룹 키를 반복하고 안쪽 루프는 그룹 자체의 각 항목을 반복합니다.

11. 그룹에 키(key)는 포함될 수 있지만 요소(elements)는 포함될 수 없습니다.

12. 이전 코드 예제에서 쿼리를 실행하는 foreach 루프는 다음과 같습니다.


// Iterate group items with a nested foreach. This IGrouping encapsulates
// a sequence of Student objects, and a Key of type char.
// For convenience, var can also be used in the foreach statement.
foreach (IGrouping<char, Student> studentGroup in studentQuery2)
{
     Console.WriteLine(studentGroup.Key);
     // Explicit type for student could also be used here.
     foreach (var student in studentGroup)
     {
         Console.WriteLine("   {0}, {1}", student.Last, student.First);
     }
 }


13. 그룹 키는 문자열, 기본 제공 숫자 형식 또는 사용자 정의 명명된 형식이나 익명 형식과 같은 임의의 형식일 수 있습니다.

14. 앞의 코드 예제에서는 char를 사용했습니다.

15. 전체 성(last name) 등의 문자열 키를 대신 지정할 수도 있습니다.


// Same as previous example except we use the entire last name as a key.
// Query variable is an IEnumerable<IGrouping<string, Student>>
 var studentQuery3 =
     from student in students
     group student by student.Last;


16. 다음 예제에서는 키를 부울 값을 사용하여 결과를 두 개의 그룹으로 나누는 방법을 보여 줍니다.

17. group 절의 하위 식(sub-expression)에서 값이 생성됩니다.


class GroupSample1
{
    // The element type of the data source.
    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public List<int> Scores;
    }

    public static List<Student> GetStudents()
    {
        // Use a collection initializer to create the data source. Note that each element
        //  in the list contains an inner sequence of scores.
        List<Student> students = new List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {97, 89, 85, 82}} 
        };

        return students;

    }

    static void Main()
    {
        // Obtain the data source.
        List<Student> students = GetStudents();

        // Group by true or false.
        // Query variable is an IEnumerable<IGrouping<bool, Student>>
        var booleanGroupQuery =
            from student in students
            group student by student.Scores.Average() >= 80; //pass or fail!

        // Execute the query and access items in each group
        foreach (var studentGroup in booleanGroupQuery)
        {
            Console.WriteLine(studentGroup.Key == true ? "High averages" : "Low averages");
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
  Low averages
   Omelchenko, Svetlana:77.5
   O'Donnell, Claire:72.25
   Garcia, Cesar:75.5
  High averages
   Mortensen, Sven:93.5
   Garcia, Debra:88.25
*/


18. 다음 예제에서는 식을 사용하여 백분위수 범위(a percentile range)를 나타내는 숫자 그룹 키를 만듭니다.

19. group 절에서 메서드를 두 번 호출할 필요가 없도록 메서드 호출 결과를 저장할 편리한 위치로 let을 사용합니다.

20. 또한 "0으로 나누기(divide by zero)" 예외를 방지하기 위해 이 코드는 group 절에서 학생의 평균이 0이 아닌지 확인합니다.

21. 쿼리 식에서 메서드를 안전하게 사용하는 방법에 대한 자세한 내용은 방법: 쿼리 식의 예외 처리를 참조하십시오.


class GroupSample2
{
    // The element type of the data source.
    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public List<int> Scores;
    }

    public static List<Student> GetStudents()
    {
        // Use a collection initializer to create the data source. Note that each element
        //  in the list contains an inner sequence of scores.
        List<Student> students = new List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {97, 89, 85, 82}} 
        };

        return students;

    }

    // This method groups students into percentile ranges based on their
    // grade average. The Average method returns a double, so to produce a whole
    // number it is necessary to cast to int before dividing by 10. 
    static void Main()
    {
        // Obtain the data source.
        List<Student> students = GetStudents();

        // Write the query.
        var studentQuery =
            from student in students
            let avg = (int)student.Scores.Average()
            group student by (avg == 0 ? 0 : avg / 10) into g
            orderby g.Key
            select g;            

        // Execute the query.
        foreach (var studentGroup in studentQuery)
        {
            int temp = studentGroup.Key * 10;
            Console.WriteLine("Students with an average between {0} and {1}", temp, temp + 10);
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
     Students with an average between 70 and 80
       Omelchenko, Svetlana:77.5
       O'Donnell, Claire:72.25
       Garcia, Cesar:75.5
     Students with an average between 80 and 90
       Garcia, Debra:88.25
     Students with an average between 90 and 100
       Mortensen, Sven:93.5
 */


22. 둘 이상의 키에 따라 요소를 그룹화하려는 경우 복합(composite) 키를 사용합니다.

23. 키 요소를 담기 위해서 익명 형식이나 명명된 형식으로 복합 키를 만듭니다.

24. 예제에서는 Person 클래스가 surname 및 city라는 멤버로 선언되었다고 가정합니다(assume).

25. group 클래스는 성과 구/군/시가 같은 각 개인 집합에 대해 별도의 그룹이 만들어지게 합니다.


group person by new {name = person.surname, city = person.city};


26. 쿼리 변수를 다른 메서드로 전달해야 하는 경우 명명된 형식을 사용합니다.

27. 키에 대해 자동 구현된 속성을 사용하여 특수 클래스를 만든 다음 EqualsGetHashCode 메서드를 재정의합니다.

28. 이러한 메서드를 반드시 재정의할 필요가 없는 구조체를 사용할 수도 있습니다.

29. 자세한 내용은 방법: 자동으로 구현된 속성을 사용하여 간단한 클래스 구현How to: Query for Duplicate Files in a Directory Tree (LINQ)을 참조하십시오.

30. 두 번째 항목에는 복합 키에 명명된 형식을 사용하는 방법을 보여 주는 코드 예제가 있습니다.

31. 다음 예제에서는 그룹에 적용되는 추가 쿼리 논리가 없을 때 소스 데이터를 그룹으로 정렬하기 위한 표준 패턴(the standard pattern)을 보여 줍니다.

32. 이를 비연속 그룹화(a grouping without a continuation)라고 합니다.

33. 문자열 배열의 요소는 첫 글자에 따라 그룹화됩니다.

34. 쿼리 결과는 그룹의 각 항목을 포함하는 IEnumerable<T> 컬렉션 및 char 형식의 public Key 속성을 포함하는 IGrouping<TKey, TElement> 형식입니다.

35. group 절의 결과는 시퀀스의 시퀀스입니다.

36. 따라서 반환된 각 그룹 내의 개별 요소에 액세스하려면 다음 예제와 같이 그룹 키를 반복하는 루프 내부의 중첩 foreach 루프를 사용합니다.


class GroupExample1
{
    static void Main()
    {
        // Create a data source.
        string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" };

        // Create the query.
        var wordGroups =
            from w in words
            group w by w[0];

        // Execute the query.
        foreach (var wordGroup in wordGroups)
        {
            Console.WriteLine("Words that start with the letter '{0}':", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine(word);
            }
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }        
}
/* Output:
      Words that start with the letter 'b':
        blueberry
        banana
      Words that start with the letter 'c':
        chimpanzee
        cheese
      Words that start with the letter 'a':
        abacus
        apple
     */


37. 이 예제에서는 연속에 into를 사용하여 그룹을 만든 후 그룹에서 추가 논리(logic)를 수행하는 방법을 보여줍니다.

38. 자세한 내용은 into를 참조하십시오.

39. 다음 예제에서는 각 그룹을 쿼리하여 키 값이 모음(vowel)인 그룹만 선택합니다.


class GroupClauseExample2
{
    static void Main()
    {
        // Create the data source.
        string[] words2 = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese", "elephant", "umbrella", "anteater" };

        // Create the query.
        var wordGroups2 =
            from w in words2
            group w by w[0] into grps
            where (grps.Key == 'a' || grps.Key == 'e' || grps.Key == 'i'
                   || grps.Key == 'o' || grps.Key == 'u')
            select grps;

        // Execute the query.
        foreach (var wordGroup in wordGroups2)
        {
            Console.WriteLine("Groups that start with a vowel: {0}", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine("   {0}", word);
            }
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    Groups that start with a vowel: a
        abacus
        apple
        anteater
    Groups that start with a vowel: e
        elephant
    Groups that start with a vowel: u
        umbrella
*/    


40. 컴파일 타임에 group 절은 GroupBy<TSource, TKey> 메서드에 대한 호출로 변환(translated into)됩니다.





into


1. 컨텍스트 키워드 into는 group, join, select 절의 결과를 새 식별자에 저장하는 임시 식별자(a temporary identifier)를 만드는 데 사용할 수 있습니다.

2. 이 식별자 자체는 추가 쿼리 명령에 대한 생성기(a generator)가 될 수 있습니다.

3. group 또는 select 절에서 사용되는 경우 새 식별자의 사용을 연속(a continuation)이라고도 합니다.

4. 다음 예제에서는 IGrouping의 유추된 형식(an inferred type)을 포함하는 임시 식별자 fruitGroup을 활성화하도록 into 키워드를 사용하는 방법을 보여 줍니다.

5. 식별자를 사용하여 각 그룹에서 Count 메서드를 호출하고 두 개 이상의 단어를 포함하는 해당 그룹만 선택할 수 있습니다.


class IntoSample1
{
    static void Main()
    {

        // Create a data source.
        string[] words = { "apples", "blueberries", "oranges", "bananas", "apricots"};

        // Create the query.
        var wordGroups1 =
            from w in words
            group w by w[0] into fruitGroup
            where fruitGroup.Count() >= 2
            select new { FirstLetter = fruitGroup.Key, Words = fruitGroup.Count() };

        // Execute the query. Note that we only iterate over the groups, 
        // not the items in each group
        foreach (var item in wordGroups1)
        {
            Console.WriteLine(" {0} has {1} elements.", item.FirstLetter, item.Words);
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
   a has 2 elements.
   b has 2 elements.
*/


6. 각 그룹에 대해 추가 쿼리 작업을 수행하려는 경우에만 group 절에 into 를 사용해야 합니다.

7. 자세한 내용은 group 절을 참조하십시오.

8. join 절에서 into를 사용하는 방법에 대한 예제는 join 절을 참조하십시오.





join


1. join 절은 개체 모델에서 직접 관계(direct relationship)가 없는 여러 소스 시퀀스의 요소를 연결(associate)하는 데 유용합니다.

2. 유일한 요구 사항(requirement)은 각 소스의 요소가 같은지(equality) 비교할 수 있는 일부 값을 공유해야 한다는 것입니다.

3. 예를 들어 식품 유통업체(a food distributor)에는 특정 제품(a certain product)의 공급자 목록(a list of suppliers)과 구매자 목록(a list of buyers)이 있을 수 있습니다.

4. 예를 들어 해당 제품의 공급자와 구매자가 모두 동일한 특정 지역에 있는 경우 join 절을 사용하여 이러한 공급자와 구매자의 목록을 만들 수 있습니다.

5. join 절은 두 개의 소스 시퀀스를 입력으로 사용합니다.

6. 각 시퀀스의 요소는 다른 시퀀스의 해당 속성(a corresponding property)과 비교할 수 있는 속성이거나 이러한 속성을 포함해야 합니다.

7. join 절은 특별한 equals 키워드를 사용하여 지정된 키가 같은지 비교합니다.

8. join 절에서 수행하는 모든 join은 동등 조인(equijoins)입니다.

9. join 절의 출력 모양은 수행하는 특정 조인 형식에 따라 달라집니다.

10. 가장 일반적인(common) 세 가지 조인 형식은 다음과 같습니다.

    1) 내부 조인

    2) 그룹 조인

    3) 왼쪽 우선 외부 조인

<내부 조인>

11. 다음 예제에서는 단순한 내부 동등 조인(a simple inner equijoin)을 보여 줍니다.

12. 이 쿼리는 "제품 이름/범주"쌍의 기본(flat) 시퀀스를 생성합니다.

13. 동일한 범주 문자열(the same category string)이 여러 요소에 나타납니다

14. categories의 요소에 일치하는 products가 없을 경우(have no matching products) 해당 범주는 결과에 표시되지 않습니다.


var innerJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID
    select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence


15. 자세한 내용은 방법: 내부 조인 수행을 참조하십시오.

<그룹 조인>

16. into 식이 있는 join 절을 그룹 조인이라고 합니다.


var innerGroupJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    select new { CategoryName = category.Name, Products = prodGroup };

 

17. 그룹 조인은 왼쪽 소스 시퀀스의 요소를 오른쪽 소스 시퀀스에 있는 하나 이상의 일치하는 요소와 연결하는 계층적 결과(a hierarchical result)를 생성합니다.

18. 그룹 조인은 동등한 관계형 용어가 없으며 기본적으로 개체 배열의 시퀀스입니다.

19. 왼쪽 소스의 요소와 일치하는 오른족 소스 시퀀스의 요소가 없을 경우 join 절은 해당 항목에 대해 빈 배열을 생성합니다.

20. 따라서 그룹 조인은 결과 시퀀스가 그룹으로 구성(organize)된다는 점을 제외하고 기본적으로(basically) 내부동등 조인입니다.

21. 단순히 그룹 조인의 결과를 선택하면 항목에 액세스할 수 있지만 항목이 일치하는 키를 식별할 수는 없습니다.

22. 따라서 앞의 예제와 같이 그룹 조인의 결과를 키 이름도 있는 새 형식으로 선택하는 것이 더 유용합니다.

23. 물론 그룹 조인의 결과를 다른 하위 쿼리(another subquery)의 생성기르 사용할 수도 있습니다.


var innerGroupJoinQuery2 =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from prod2 in prodGroup
    where prod2.UnitPrice > 2.50M
    select prod2;


24. 자세한 내용은 방법: 그룹화 조인 수행을 찹조하십시오.

<왼쪽 우선 외부 조인>

25. 왼쪽 우선 외부 조인(a left outer join)에서는 오른쪽 시퀀스에 일치하는 요소가 없는 경우에는 왼쪽 소스 시퀀스의 모든 요소가 반환됩니다.

26. LINQ에서 왼쪽 우선 외부 조인을 수행하려면 DefaultIfEmpty 메서드를 그룹 조인과 함께 사용하여 왼쪽 요소에 일치 항목이 없을 경우 생성할 기본 오른쪽 요소를 지정합니다.

27. null을 참조 형식의 기본값으로 사용하거나 사용자 정의 기본 형식을 지정할 수 있습니다.

28. 다음 예제에서는 사용자 정의 기본 형식이 표시됩니다.


var leftOuterJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from item in prodGroup.DefaultIfEmpty(new Product { Name = String.Empty, CategoryID = 0 })
    select new { CatName = category.Name, ProdName = item.Name };


29. 자세한 내용은 방법: 왼쪽 우선 외부 조인 수행을 참조하십시오.

<같음 연산자>

30. join 절은 동등 조인을 수행합니다.

31. 즉(In other words), 일치 항목만 기준으로 두 키가 같은지 비교할 수 있습니다.

32. "보다 큼(greater than)", 또는 "같지 않음(not equals)"과 같은 다른 비교 형식은 지원되지 않습니다.

33. 모든 조인이 동등 조인임을 확인하기 위해(To make clear that) join절은 == 연산자 대신 equals 키워드를 사용합니다.

34. equals 키워드는 join 절에서만 사용할 수 있으며 == 연산자와 중요한 한 가지 차이점이 있습니다.

35. equals를 사용하는 경우 왼쪽 키는 외부 소스 시퀀스를 사용하고(consume) 오른쪽 키는 내부 소스를 사용합니다.

36. 외부 소스는 equals의 왼쪽 범위에만 있고 내부 소스 시퀀스는 오른쪽 범위에만 있습니다.

<비동등 조인>

37. 여러 개의 from 절을 사용하여 새 시퀀스를 개별적으로(independently) 쿼리에 적용하면 비동등 조인(non-equijoins), 교차 조인(cross joins) 및 기타 사용자 지정 조인(other custom join) 작업을 수행할 수 있습니다.

38. 자세한 내용은 방법: 사용자지정 조인 작업 수행을 참조하십시오.

<개체 컬렉션 및 관계형 테이블의 조인>

39. LINQ 쿼리 식에서는 조인 작업이 개체 컬력션에서 수행됩니다.

40. 개체 컬렉션은 두 개의 관계형 테이블과 정확히 동일한 방식으로 "조인"할 수 없습니다.

41. LINQ에서 명시적(explicit) join 절은 두 개의 소스 시퀀스가 어떤 관계로도 연결(tie)되지 않은 경우에만 필요합니다.

42. LINQ to SQL을 사용하는 경우 외래 키 테이블은 개체 모델에 기본 테이블(the primary table)의 속성으로 표현됩니다.

43. 예를 들어 Northwind 데이터베이스의 Customer 테이블은 Orders 테이블과 외래 키 관계가 있습니다.

44. 테이블을 개체 모델에 매핑하면 Customer 클래스에 해당 Customer와 연결된 Orders 컬렉션을 포함하는 Orders 속성이 있습니다.

45. 실제로(In effect) 조인이 이미 수정되었습니다.

46. LINQ to SQL 컨텍스트에서 관계형 테이블을 쿼리하는 방법에 대한 자세한 내용은 방법: 데이터베이스 관계 매을 참조하십시오.

<복합 키>

47. 복합 키를 사용하여 여러 값이같은지 테스트할 수 있습니다.

48. 자세한 내용은 방법: 복합 키를 사용하여 조인을 참조하십시오.

49. 복합 키는 group 절에서도 사용할 수 있습니다.

<예제>

50. 다음 예제에서는 일치하는 동일한 키를 사용하여 동일한 데이터 소스에서 수행한 내부 조인, 그룹 조인 및  왼쪽 우선 외부 조인의 결과를 비교합니다.

51. 콘솔 표시의 결과를 명확(clarify)하게 지정하기 위해 이러한 예제에 일부 다른 코드(some extra code)가 추가됩니다.


class JoinDemonstration
{
    #region Data

    class Product
    {
        public string Name { get; set; }
        public int CategoryID { get; set; }
    }

    class Category
    {
        public string Name { get; set; }
        public int ID { get; set; }
    }

    // Specify the first data source.
    List<Category> categories = new List<Category>()
    { 
        new Category(){Name="Beverages", ID=001},
        new Category(){ Name="Condiments", ID=002},
        new Category(){ Name="Vegetables", ID=003},
        new Category() { Name="Grains", ID=004},
        new Category() { Name="Fruit", ID=005}            
    };

    // Specify the second data source.
    List<Product> products = new List<Product>()
   {
      new Product{Name="Cola",  CategoryID=001},
      new Product{Name="Tea",  CategoryID=001},
      new Product{Name="Mustard", CategoryID=002},
      new Product{Name="Pickles", CategoryID=002},
      new Product{Name="Carrots", CategoryID=003},
      new Product{Name="Bok Choy", CategoryID=003},
      new Product{Name="Peaches", CategoryID=005},
      new Product{Name="Melons", CategoryID=005},
    };
    #endregion


    static void Main(string[] args)
    {
        JoinDemonstration app = new JoinDemonstration();

        app.InnerJoin();
        app.GroupJoin();
        app.GroupInnerJoin();
        app.GroupJoin3();
        app.LeftOuterJoin();
        app.LeftOuterJoin2();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    void InnerJoin()
    {
        // Create the query that selects 
        // a property from each element.
        var innerJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID
           select new { Category = category.ID, Product = prod.Name };

        Console.WriteLine("InnerJoin:");
        // Execute the query. Access results 
        // with a simple foreach statement.
        foreach (var item in innerJoinQuery)
        {
            Console.WriteLine("{0,-10}{1}", item.Product, item.Category);
        }
        Console.WriteLine("InnerJoin: {0} items in 1 group.", innerJoinQuery.Count());
        Console.WriteLine(System.Environment.NewLine);

    }

    void GroupJoin()
    {
        // This is a demonstration query to show the output
        // of a "raw" group join. A more typical group join
        // is shown in the GroupInnerJoin method.
        var groupJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup;

        // Store the count of total items (for demonstration only).
        int totalItems = 0;

        Console.WriteLine("Simple GroupJoin:");

        // A nested foreach statement is required to access group items.
        foreach (var prodGrouping in groupJoinQuery)
        {
            Console.WriteLine("Group:");
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("   {0,-10}{1}", item.Name, item.CategoryID);
            }
        }
        Console.WriteLine("Unshaped GroupJoin: {0} items in {1} unnamed groups", totalItems, groupJoinQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void GroupInnerJoin()
    {
        var groupJoinQuery2 =
            from category in categories
            orderby category.ID
            join prod in products on category.ID equals prod.CategoryID into prodGroup
            select new
            {
                Category = category.Name,
                Products = from prod2 in prodGroup
                           orderby prod2.Name
                           select prod2
            };

        //Console.WriteLine("GroupInnerJoin:");
        int totalItems = 0;

        Console.WriteLine("GroupInnerJoin:");
        foreach (var productGroup in groupJoinQuery2)
        {
            Console.WriteLine(productGroup.Category);
            foreach (var prodItem in productGroup.Products)
            {
                totalItems++;
                Console.WriteLine("  {0,-10} {1}", prodItem.Name, prodItem.CategoryID);
            }
        }
        Console.WriteLine("GroupInnerJoin: {0} items in {1} named groups", totalItems, groupJoinQuery2.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void GroupJoin3()
    {

        var groupJoinQuery3 =
            from category in categories
            join product in products on category.ID equals product.CategoryID into prodGroup
            from prod in prodGroup
            orderby prod.CategoryID
            select new { Category = prod.CategoryID, ProductName = prod.Name };

        //Console.WriteLine("GroupInnerJoin:");
        int totalItems = 0;

        Console.WriteLine("GroupJoin3:");
        foreach (var item in groupJoinQuery3)
        {
            totalItems++;
            Console.WriteLine("   {0}:{1}", item.ProductName, item.Category);
        }

        Console.WriteLine("GroupJoin3: {0} items in 1 group", totalItems, groupJoinQuery3.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void LeftOuterJoin()
    {
        // Create the query.
        var leftOuterQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup.DefaultIfEmpty(new Product() { Name = "Nothing!", CategoryID = category.ID });

        // Store the count of total items (for demonstration only).
        int totalItems = 0;

        Console.WriteLine("Left Outer Join:");

        // A nested foreach statement  is required to access group items
        foreach (var prodGrouping in leftOuterQuery)
        {
            Console.WriteLine("Group:", prodGrouping.Count());
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("  {0,-10}{1}", item.Name, item.CategoryID);
            }
        }
        Console.WriteLine("LeftOuterJoin: {0} items in {1} groups", totalItems, leftOuterQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void LeftOuterJoin2()
    {
        // Create the query.
        var leftOuterQuery2 =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           from item in prodGroup.DefaultIfEmpty()
           select new { Name = item == null ? "Nothing!" : item.Name, CategoryID = category.ID };

        Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", leftOuterQuery2.Count());
        // Store the count of total items
        int totalItems = 0;

        Console.WriteLine("Left Outer Join 2:");

        // Groups have been flattened.
        foreach (var item in leftOuterQuery2)
        {
            totalItems++;
            Console.WriteLine("{0,-10}{1}", item.Name, item.CategoryID);
        }
        Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", totalItems);
    }
}
/*Output:

InnerJoin:
Cola      1
Tea       1
Mustard   2
Pickles   2
Carrots   3
Bok Choy  3
Peaches   5
Melons    5
InnerJoin: 8 items in 1 group.


Unshaped GroupJoin:
Group:
    Cola      1
    Tea       1
Group:
    Mustard   2
    Pickles   2
Group:
    Carrots   3
    Bok Choy  3
Group:
Group:
    Peaches   5
    Melons    5
Unshaped GroupJoin: 8 items in 5 unnamed groups


GroupInnerJoin:
Beverages
    Cola       1
    Tea        1
Condiments
    Mustard    2
    Pickles    2
Vegetables
    Bok Choy   3
    Carrots    3
Grains
Fruit
    Melons     5
    Peaches    5
GroupInnerJoin: 8 items in 5 named groups


GroupJoin3:
    Cola:1
    Tea:1
    Mustard:2
    Pickles:2
    Carrots:3
    Bok Choy:3
    Peaches:5
    Melons:5
GroupJoin3: 8 items in 1 group


Left Outer Join:
Group:
    Cola      1
    Tea       1
Group:
    Mustard   2
    Pickles   2
Group:
    Carrots   3
    Bok Choy  3
Group:
    Nothing!  4
Group:
    Peaches   5
    Melons    5
LeftOuterJoin: 9 items in 5 groups


LeftOuterJoin2: 9 items in 1 group
Left Outer Join 2:
Cola      1
Tea       1
Mustard   2
Pickles   2
Carrots   3
Bok Choy  3
Nothing!  4
Peaches   5
Melons    5
LeftOuterJoin2: 9 items in 1 group
Press any key to exit.
*/


52. 뒤에 into가 없는 join 절은 Join<TOuter, TInner, TKey, TResult> 메서드 호출로 변환됩니다.

53. 뒤에 into가 있는 join 절은 GroupJoin<TOuter, TInner, TKey, TResult> 메서드 호출로 변환됩니다.





let


1. 쿼리 식에서는 후속 절(subsequent clauses)에서 쿼리 식을 사용하기 위해 부분식(sub-expression)의 결과를 저장하는 것이 좋습니다.

2. let 키워드를 사용하여 작업을 할 수 있는데 이 키워드는 새로운 범위 변수를 만들고 제공된 식의 결과로 범위 변수를 초기화합니다.

3. 값으로 초기화하고 나면 범위 변수는 다른 값을 저장하는 데 사용될 수 없습니다.

4. 그러나 범위 변수가 쿼리 가능한 형식(a queryable type)을 사용할 경우에는 쿼리할 수 있습니다.

5. 다음 예제에서는 let이 두 가지 방법으로 사용되고 있습니다.

    1) 쿼리될 수 있는 열거 가능한 형식(enumerable type)을 만들기.

    2) 범위 변수 word에서 ToLower를 한번만 호출하기. let을 사용하지 않는다면 where 절의 각 조건자(each predicate)에서 ToLower를 호출해야 합니다.


class LetSample1
{
    static void Main()
    {
        string[] strings = 
        {
            "A penny saved is a penny earned.",
            "The early bird catches the worm.",
            "The pen is mightier than the sword." 
        };

        // Split the sentence into an array of words
        // and select those whose first letter is a vowel.
        var earlyBirdQuery =
            from sentence in strings
            let words = sentence.Split(' ')
            from word in words
            let w = word.ToLower()
            where w[0] == 'a' || w[0] == 'e'
                || w[0] == 'i' || w[0] == 'o'
                || w[0] == 'u'
            select word;

        // Execute the query.
        foreach (var v in earlyBirdQuery)
        {
            Console.WriteLine("\"{0}\" starts with a vowel", v);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    "A" starts with a vowel
    "is" starts with a vowel
    "a" starts with a vowel
    "earned." starts with a vowel
    "early" starts with a vowel
    "is" starts with a vowel
*/





orderby


1. 커리 식에서 orderby 절은 반환된 시퀀스 또는 서브시퀀스 (그룹)을 오름차순이나 내림차순으로(in either ascendig or descending order) 정렬되도록 합니다.

2. 둘 이상의 보조 정렬 작업(one or more secondary sort operations)을 수행하기 위해 여러 개의 키(Multiple keys)를 지정할 수 있습니다.

3. 요소의 형식에 대한 기본 비교자(default comparer)를 사용하여 정렬 작업(The sorting)을 수행합니다.

4. 기본 정렬 순서(the default sort order)는 오름차순입니다.

5. 사용자 지정 비교자(a custom comparer)를 지정할 수도 있습니다.

6. 자세한 내용은 Sorting Data를 참조하십시오.

7. 다음 예제에서는 첫 번째 쿼리는 A부터 시작하는 사전순(in alphabetical order)으로 정렬하고 두 번째 쿼리에서는 동일한 단어를 내림차순(in descending order)으로 정렬합니다.

8. ascending 키워드는 기본정렬 값이므로 생략(omit)할 수 있습니다.


class OrderbySample1 { static void Main() { // Create a delicious data source. string[] fruits = { "cherry", "apple", "blueberry" }; // Query for ascending sort. IEnumerable<string> sortAscendingQuery = from fruit in fruits orderby fruit //"ascending" is default select fruit; // Query for descending sort. IEnumerable<string> sortDescendingQuery = from w in fruits orderby w descending select w; // Execute the query. Console.WriteLine("Ascending:"); foreach (string s in sortAscendingQuery) { Console.WriteLine(s); } // Execute the query. Console.WriteLine(Environment.NewLine + "Descending:"); foreach (string s in sortDescendingQuery) { Console.WriteLine(s); } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } /* Output: Ascending: apple blueberry cherry Descending: cherry blueberry apple */


9. 다음 예제에서는 학생의 성을 기준으로 기본 정렬(a primary sort)을 수행한 다음 이름 순으로 두 번째 정렬(a secondary sort)을 수행합니다.


class OrderbySample2
{
    // The element type of the data source.
    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
    }

    public static List<Student> GetStudents()
    {
        // Use a collection initializer to create the data source. Note that each element
        //  in the list contains an inner sequence of scores.
        List<Student> students = new List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111},
           new Student {First="Claire", Last="O'Donnell", ID=112},
           new Student {First="Sven", Last="Mortensen", ID=113},
           new Student {First="Cesar", Last="Garcia", ID=114},
           new Student {First="Debra", Last="Garcia", ID=115} 
        };

        return students;
    }
    static void Main(string[] args)
    {
        // Create the data source.
        List<Student> students = GetStudents();

        // Create the query.
        IEnumerable<Student> sortedStudents =
            from student in students
            orderby student.Last ascending, student.First ascending
            select student;

        // Execute the query.
        Console.WriteLine("sortedStudents:");
        foreach (Student student in sortedStudents)
            Console.WriteLine(student.Last + " " + student.First);           

        // Now create groups and sort the groups. The query first sorts the names
        // of all students so that they will be in alphabetical order after they are
        // grouped. The second orderby sorts the group keys in alpha order.            
        var sortedGroups =
            from student in students
            orderby student.Last, student.First
            group student by student.Last[0] into newGroup
            orderby newGroup.Key
            select newGroup;

        // Execute the query.
        Console.WriteLine(Environment.NewLine + "sortedGroups:");
        foreach (var studentGroup in sortedGroups)
        {
            Console.WriteLine(studentGroup.Key);
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}", student.Last, student.First);
            }
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:  
sortedStudents:
Garcia Cesar
Garcia Debra
Mortensen Sven
O'Donnell Claire
Omelchenko Svetlana

sortedGroups:
G
   Garcia, Cesar
   Garcia, Debra
M
   Mortensen, Sven
O
   O'Donnell, Claire
   Omelchenko, Svetlana
*/


10. 컴파일 타임에 orderby 절은 OrderBy<TSource, TKey> 메서드를 호출로 변환(translate)합니다.

11. orderby 절의 여러 키는 ThenBy<TSource, TKey> 메서드 호출로 변환됩니다.





partial


1. 부분 형식(Partial Type) 정의를 사용하면 클래스, 구조체 또는 인터페이스의 정의를 여러 파일로 분할할 수 있습니다.


File1.cs: namespace PC

{ partial class A { int num = 0; void MethodA() { } partial void MethodC(); } }


Fiel2.cs:
namespace PC
{
    partial class A
    {
        void MethodB() { }
        partial void MethodC() { }
    }
}


2. 클래스, 구조체, 인터페이스 형식을 여러 파일로 분할(split)하면 대형 프로젝트(large projects)에서 제공된 것과 같이 자동으로 생성된 코드를 작업할 때 유용할 수 있습니다.

3. 부분 형식에는 부분 메서드가 포함될 수 있습니다.

4. 자세한 내용은 Partial 클래스 및 메서드를 참조하십시오.





partial


1. 부분 메서드(Partial Method)에는 부분 형식(Partial Type)의 일부분에 정의된 시그니처와 다른 형식의 부분에 정의된 구현이 있습니다.

2. 클래스 디자이너는 부분 메서드를 사용하여 이벤트 처리기와 유사한 메서드 후크를 제공하여 개발자가 구현 여부를 결정할 수 있습니다.

3. 개발자가 구현을 제공하지 않는 경우 컴파일러에서는 컴파일 타임에 시그니처를 제거합니다.

4. 부분 메서드에는 다음과 같은 조건이 적용됩니다.

    1) 부분 형식(Partial Type)의 두 부분에서 시그니처가 일치해야 합니다.

    2) 해당 메서드는 void를 반환해야 합니다

    3) 어떠한 액세스 한정자도 사용할 수 없습니다. 부분 메서드는 암시적으로 private입니다.

5. 다음 예제에서는 부분 클래스의 두 부분에서 정의된 부분 메서드를 보여 줍니다.


namespace PM
{
    partial class A
    {
        partial void OnSomethingHappened(string s);
    }

    // This part can be in a separate file.
    partial class A
    {
        // Comment out this method and the program
        // will still compile.
        partial void OnSomethingHappened(String s)
        {
            Console.WriteLine("Something happened: {0}", s);
        }
    }
}


6. 자세한 내용은 Partial 클래스 및 메서드를 참조하십시오.





remove


1. remove 컨텍스트 키워드는 클라이언트 코드가 사용자의 이벤트 구독을 취소할 때(unsubscribe) 호출되는 사용자 지정 이벤트 접근자를 정의하는 데 사용됩니다.

2. 사용자 지정 remove 접근자를 제공하는 경우에는 add 접근자도 제공해야 합니다.

3. 다음 예제에서는 사용자 지정 add 및 remove 접근자가 있는 이벤트를 보여 줍니다.

4. 전체 예제를 보려면 방법: 인터페이스 이벤트 구현을 참조하십시오.


class Events : IDrawingObject
{
    event EventHandler PreDrawEvent;

    event EventHandler IDrawingObject.OnDraw
    {
        add
        {
            lock (PreDrawEvent)
            {
                PreDrawEvent += value;
            }
        }
        remove
        {
            lock (PreDrawEvent)
            {
                PreDrawEvent -= value;
            }
        }
    }

}


5. 일반적으로 자체 사용자 지정 이벤트 접근자를 제공할 필요는 없습니다.

6. 대부분의 경우 이벤트를 선언할 때 컴파일러에서 자동으로 생성되는 접근자로도 충분합니다.





select


1. 쿼리 식에서 select 절은 쿼리가 실행될 때 생성할 값의 형식을 지정합니다.

2. 결과는 앞의 모든 절select 절 자체의 모든 식을 기준을 합니다.

3. 쿼리 식은 select 절 또는 group 절로 끝나야 합니다.

4. 다음 예제에서는 쿼리 식에서의 간단한 select 절을 보여 줍니다.


class SelectSample1
{   
    static void Main()
    {           
        //Create the data source
        List<int> Scores = new List<int>() { 97, 92, 81, 60 };

        // Create the query.
        IEnumerable<int> queryHighScores =
            from score in Scores
            where score > 80
            select score;

        // Execute the query.
        foreach (int i in queryHighScores)
        {
            Console.Write(i + " ");
        }            
    }
}
//Output: 97 92 81


5. select 절에 의해 생성된 시퀀스의 형식은 쿼리 변수 queryHighScores의 형식을 결정합니다.

6. 가장 간단하게는(In the simplest case), select 절이 범위 변수만 지정합니다.

7. 이로 인해 반환된 시퀀스에 데이터 소스와 동일한 형식의 요소가 포함됩니다.

8. 자세한 내용은 Type Relationships in LINQ Query Operations를 참조하십시오.

9. 그러나 select 절에서는 소스 데이터를 새 형식으로 변환(transforming)(또는 프로젝팅)하는 강력한 메커니즘을 제공합니다.

10. 자세한 내용은 LINQ를 통한 데이터 변환을 참조하십시오.

11. 다음 예제에서는 select 절의 여러 가지 가능한 형식(all the different forms)을 모두 보여 줍니다.

12. 각 쿼리에서 select 절과 쿼리 변수의 형식(studentQuery1 및 studentQuery2 등(and so on) 사이의 관계를 참고하십시오.


class SelectSample2
    {
        // Define some classes
        public class Student
        {
            public string First { get; set; }
            public string Last { get; set; }
            public int ID { get; set; }
            public List<int> Scores;
            public ContactInfo GetContactInfo(SelectSample2 app, int id)
            {
                ContactInfo cInfo =
                    (from ci in app.contactList
                    where ci.ID == id
                    select ci)
                    .FirstOrDefault();

                return cInfo;
            }

            public override string ToString()
            {
                return First + " " + Last + ":" + ID;
            }
        }

        public class ContactInfo
        {
            public int ID { get; set; }
            public string Email { get; set; }
            public string Phone { get; set; }
            public override string ToString() { return Email + "," + Phone; }
        }

        public class ScoreInfo
        {
            public double Average { get; set; }
            public int ID { get; set; }
        }

        // The primary data source
        List<Student> students = new List<Student>()
        {
             new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int>() {97, 92, 81, 60}},
             new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int>() {75, 84, 91, 39}},
             new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int>() {88, 94, 65, 91}},
             new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int>() {97, 89, 85, 82}},
        };

        // Separate data source for contact info.
        List<ContactInfo> contactList = new List<ContactInfo>()
        {
            new ContactInfo {ID=111, Email="SvetlanO@Contoso.com", Phone="206-555-0108"},
            new ContactInfo {ID=112, Email="ClaireO@Contoso.com", Phone="206-555-0298"},
            new ContactInfo {ID=113, Email="SvenMort@Contoso.com", Phone="206-555-1130"},
            new ContactInfo {ID=114, Email="CesarGar@Contoso.com", Phone="206-555-0521"}
        };


        static void Main(string[] args)
        {
            SelectSample2 app = new SelectSample2();

            // Produce a filtered sequence of unmodified Students.
            IEnumerable<Student> studentQuery1 =
                from student in app.students
                where student.ID > 111
                select student;

            Console.WriteLine("Query1: select range_variable");
            foreach (Student s in studentQuery1)
            {
                Console.WriteLine(s.ToString());
            }

            // Produce a filtered sequence of elements that contain
            // only one property of each Student.
            IEnumerable<String> studentQuery2 =
                from student in app.students
                where student.ID > 111
                select student.Last;

            Console.WriteLine("\r\n studentQuery2: select range_variable.Property");
            foreach (string s in studentQuery2)
            {
                Console.WriteLine(s);
            }

            // Produce a filtered sequence of objects created by
            // a method call on each Student.
            IEnumerable<ContactInfo> studentQuery3 =
                from student in app.students
                where student.ID > 111
                select student.GetContactInfo(app, student.ID);

            Console.WriteLine("\r\n studentQuery3: select range_variable.Method");
            foreach (ContactInfo ci in studentQuery3)
            {
                Console.WriteLine(ci.ToString());
            }

            // Produce a filtered sequence of ints from
            // the internal array inside each Student.
            IEnumerable<int> studentQuery4 =
                from student in app.students
                where student.ID > 111
                select student.Scores[0];

            Console.WriteLine("\r\n studentQuery4: select range_variable[index]");
            foreach (int i in studentQuery4)
            {
                Console.WriteLine("First score = {0}", i);
            }

            // Produce a filtered sequence of doubles 
            // that are the result of an expression.
            IEnumerable<double> studentQuery5 =
                from student in app.students
                where student.ID > 111
                select student.Scores[0] * 1.1;

            Console.WriteLine("\r\n studentQuery5: select expression");
            foreach (double d in studentQuery5)
            {
                Console.WriteLine("Adjusted first score = {0}", d);
            }

            // Produce a filtered sequence of doubles that are
            // the result of a method call.
            IEnumerable<double> studentQuery6 =
                from student in app.students
                where student.ID > 111
                select student.Scores.Average();

            Console.WriteLine("\r\n studentQuery6: select expression2");
            foreach (double d in studentQuery6)
            {
                Console.WriteLine("Average = {0}", d);
            }

            // Produce a filtered sequence of anonymous types
            // that contain only two properties from each Student.
            var studentQuery7 =
                from student in app.students
                where student.ID > 111
                select new { student.First, student.Last };

            Console.WriteLine("\r\n studentQuery7: select new anonymous type");
            foreach (var item in studentQuery7)
            {
                Console.WriteLine("{0}, {1}", item.Last, item.First);
            }

            // Produce a filtered sequence of named objects that contain
            // a method return value and a property from each Student.
            // Use named types if you need to pass the query variable 
            // across a method boundary.
            IEnumerable<ScoreInfo> studentQuery8 =
                from student in app.students
                where student.ID > 111
                select new ScoreInfo
                {
                    Average = student.Scores.Average(),
                    ID = student.ID
                };

            Console.WriteLine("\r\n studentQuery8: select new named type");
            foreach (ScoreInfo si in studentQuery8)
            {
                Console.WriteLine("ID = {0}, Average = {1}", si.ID, si.Average);
            }

            // Produce a filtered sequence of students who appear on a contact list
            // and whose average is greater than 85.
            IEnumerable<ContactInfo> studentQuery9 =
                from student in app.students
                where student.Scores.Average() > 85
                join ci in app.contactList on student.ID equals ci.ID
                select ci;

            Console.WriteLine("\r\n studentQuery9: select result of join clause");
            foreach (ContactInfo ci in studentQuery9)
            {
                Console.WriteLine("ID = {0}, Email = {1}", ci.ID, ci.Email);
            }

            // Keep the console window open in debug mode
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
            }
        }
    /* Output
        Query1: select range_variable
        Claire O'Donnell:112
        Sven Mortensen:113
        Cesar Garcia:114

        studentQuery2: select range_variable.Property
        O'Donnell
        Mortensen
        Garcia

        studentQuery3: select range_variable.Method
        ClaireO@Contoso.com,206-555-0298
        SvenMort@Contoso.com,206-555-1130
        CesarGar@Contoso.com,206-555-0521

        studentQuery4: select range_variable[index]
        First score = 75
        First score = 88
        First score = 97

        studentQuery5: select expression
        Adjusted first score = 82.5
        Adjusted first score = 96.8
        Adjusted first score = 106.7

        studentQuery6: select expression2
        Average = 72.25
        Average = 84.5
        Average = 88.25

        studentQuery7: select new anonymous type
        O'Donnell, Claire
        Mortensen, Sven
        Garcia, Cesar

        studentQuery8: select new named type
        ID = 112, Average = 72.25
        ID = 113, Average = 84.5
        ID = 114, Average = 88.25

        studentQuery9: select result of join clause
        ID = 114, Email = CesarGar@Contoso.com
*/


13. 앞 예제의 studentQuery8와 같이 반환된 시퀀스의 요소가 소스 요소의 속성의 하위 집합(subset)만 포함하도록 지정해야 하는 경우가 있습니다.

14. 반환된 시퀀스를 가능한 한 작게 유지하면 메모리 요구 사항을 줄이고 쿼리 실행 속도를 높일 수 있습니다

15. select 절에서 익명 형식을 만들고 개체 이니셜라이저를 사용하여 소스 요소의 적절한 속성으로 익명 형식을 초기화하면 이 작업을 수행할 수 있습니다.

16. 이 작업을 수행하는 방법에 대한 예제는 개체 및 컬렉션 이니셜라이저를 참조하십시오.

17. 컴파일 타임에 select 절은 Select<TSource, TResult> 표준 쿼리 연산자에 대한 메서드 호출로 변환됩니다.





set


1. set 키워드는 속성 값 또는 인덱서 요소를 할당하는 접근자 메서드를 속성 또는 인덱스에 정의합니다.

2. 자세한 내용은 속성, 자동으로 구현된 속성, 인덱서를 참조하십시오.

3. 다음은 Seconds라는 속성에 대한 set 접근자의 예입니다.


class TimePeriod
{
    private double _seconds;
    public double Seconds
    {
        get { return _seconds; }
        set { _seconds = value; }
    }
}


4. 다음은 자동으로 구현된 속성의 set 접근자에 대한 예제입니다.


class TimePeriod2
{
    public double Hours { get; set; }
}





value


1. 키워드 value는 일반 속성 선언의 set 접근자에 사용되며, 메서드의 입력 매개 변수와 비슷합니다.

2. 단어(the word) value는 클라이언트 코드에서 속성에 할당하는 값을 참조(reference)합니다.

3. 다음 예제에서 MyDerivedClass에는 Name이라는 속성이 있습니다.

4. 이 속성은 value 매개 변수를 사용하여 지원 필드 name에 새 문자열을 할당합니다.

5. 클라이언트 코드 측면에서(From the point of view of client code) 연산은 단순 할당으로 작성됩니다.


class MyBaseClass
{
    // virtual auto-implemented property. Overrides can only
    // provide specialized behavior if they implement get and set accessors.
    public virtual string Name { get; set; }

    // ordinary virtual property with backing field
    private int num;
    public virtual int Number
    {
        get { return num; }
        set { num = value; }
    }
}


class MyDerivedClass : MyBaseClass
{
    private string name;

   // Override auto-implemented property with ordinary property
   // to provide specialized accessor behavior.
    public override string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (value != String.Empty)
            {
                name = value;
            }
            else
            {
                name = "Unknown";
            }
        }
    }

}


6. value 사용에 대한 자세한 내용은 속성을 참조하십시오.





var


1. Visual C# 3.0 부터는(Beginning) 메서드 범위에서 선언된 변수가 암시적 형식 var를 가질 수 있습니다.

2. 암시적으로 형식화된 지역 변수는 형식 자체를 선언했던 것처럼 강력하게 형식화되었지만 컴파일러에게 형식을 결정합니다.


var i = 10; // implicitly typed
int i = 10; //explicitly typed

 

3. 자세한 내용은 암시적으로 형식화된 지역 변수LINQ 쿼리 작업의 형식 관계를 참조하십시오.

4. 다음 예제에서는 두 개의 쿼리 식을 보여 줍니다.

5. 첫번째 식에서 var의 사용은 허용(permit)되나 쿼리 결과의 형식이 IEnumerable<string>로 명확하게 지정되기(state) 때문에 필요한 것은 아닙니다.

6. 그러나 두 번째 식에서는 결과가 익명 형식의 컬렉션이고 형식의 이름이 컴파일러 자체 이외에는 액세스할 수 없으므로 var를 사용해야 합니다.

7. 예제 #2에서는 foreach 반복 변수 item은 암시적으로도 형식화되어야 합니다.


// Example #1: var is optional because
// the select clause specifies a string
string[] words = { "apple", "strawberry", "grape", "peach", "banana" };
var wordQuery = from word in words
                where word[0] == 'g'
                select word;

// Because each element in the sequence is a string, 
// not an anonymous type, var is optional here also.
foreach (string s in wordQuery)
{
    Console.WriteLine(s);
}

// Example #2: var is required because
// the select clause specifies an anonymous type
var custQuery = from cust in customers
                where cust.City == "Phoenix"
                select new { cust.Name, cust.Phone };

// var must be used because each item 
// in the sequence is an anonymous type
foreach (var item in custQuery)
{
    Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);
}






where(제네릭 형식 제약 조건)


1. 제네릭 형식 정의에서 where 절은 제네릭 선언에 정의된 형식 매개 변수의 인수로 사용할 수 있는 형식에 대해 제약 조건(constraints)을 지정하는 데 사용됩니다.

2. 예를 들어 다음과 같이 형식 매개 변수 T가 IComparable<T> 인터페이스를 구현하도록 제네릭 클래스 MyGenericClass를 선언할 수 있습니다.


public class MyGenericClass<T> where T:IComparable { }


System_CAPS_note참고

쿼리 식의 where 절에 대한 자세한 내용은 where 절(C# 참조)을 참조하십시오.

3. where 절에는 인터페이스 제약 조건 외의 기본 클래스 제약 조건도 포함될 수 있습니다.

4. 이 제약 조건은 지정된 클래스를 기본 클래스로 갖거나 지정된 클래스 자체인 형식만 해당 제네릭 형식 인수로 사용할 수 있도록 제한합니다.

5. 이러한 제약 조건을 사용할 경우에는 해당 형식 매개 변수에 대한 다른 모든 제약 조건 앞에 사용해야 합니다.


class MyClass<T, U>
    where T : class
    where U : struct
{ }


6. where 절에는 생성자 제약 조건도 포함될 수 있습니다.

7. new 연산자를 사용하여 형식 매개 변수의 인스턴스를 만들 수 있지만 이렇게 하려면 형식 매개 변수를 생성자 제약 조건 new()로 제한해야 합니다.

8. new() 제약 조건을 사용하면 컴파일러에서는 제공된 모든 형식 인수가 액세스 가능하고 매개 변수 없는(또는 기본) 생성자를 가져야 한다는 것을 알 수 있습니다.


public class MyGenericClass<T> where T : IComparable, new()
{
    // The following line is not possible without new() constraint:
    T item = new T();
}


9. new() 제약 조건은 where 절의 마지막에 나와 있습니다.

10. 형식 매개 변수가 여러 개이면 다음 예제와 같이 where 절을 각 형식 매개 변수마다 하나씩 사용합니다.


interface IMyInterface
{
}

class Dictionary<TKey, TVal>
    where TKey : IComparable, IEnumerable
    where TVal : IMyInterface
{
    public void Add(TKey key, TVal val)
    {
    }
}


11. 다음과 같이 제네릭 메서드의 형식 매개 변수에 제약 조건을 연결할(attach) 수도 있습니다.


public bool MyMethod<T>(T t) where T : IMyInterface { }


12. 대리자에 대한 형식 매개 변수 제약 조건을 설명하기 위한 구문은 메서드에 사용하는 구문과 동일합니다.


delegate T MyDelegate<T>() where T : new()


13. 제네릭 대리자에 대한 자세한 내용은 제네릭 대리자를 참조하십시오.

14. 제약 조건의 구문 및 사용 방법에 대한 자세한 내용은 형식 매개 변수에 대한 제약 조건을 참조하십시오.





where 절


1. where 절은 쿼리 식에서 반환할 데이터 소스의 요소를 쿼리 식에 지정하는 데 사용됩니다.

2. 이 절은 범위 변수로 참조되는 각 소스 요소에 Boolean 조건(조건자(predicate))을 적용하고, 지정한 조건이 true인 요소를 반환합니다.

3. 쿼리 식 하나에는 where 절을 여러 개 포함할 수 있으며 절 하나에는 조건자 하위 식(predicate subexpressions)을 여러 개 포함할 수 있습니다.

4. 다음 예제에서 where 절은 5 미만의(less than five) 숫자만 필터링합니다.

5. where 절을 제거하면 데이터 소스에서 모든 숫자가 반환됩니다.

6. 여기서 num < 5 식은 각 요소에 적용되는 조건자입니다


class WhereSample
{
    static void Main()
    {   
        // Simple data source. Arrays support IEnumerable<T>.
        int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

        // Simple query with one predicate in where clause.
        var queryLowNums =
            from num in numbers
            where num < 5
            select num;

        // Execute the query.
        foreach (var s in queryLowNums)
        {
            Console.Write(s.ToString() + " ");
        }
    }
}
//Output: 4 1 3 2 0


7. where 절 하나에는 && 및 || 연산자를 사용하여 조건자를 필요한 만큼 지정할 수 있습니다.

8. 다음 예제에서는 쿼리에 조건자 두 개를 지정하여 5미만의 짝수만(only the even numbers) 선택합니다.


class WhereSample2
{
    static void Main()
    {
        // Data source.
        int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

        // Create the query with two predicates in where clause.
        var queryLowNums2 =
            from num in numbers
            where num < 5 && num % 2 == 0
            select num;

        // Execute the query
        foreach (var s in queryLowNums2)
        {
            Console.Write(s.ToString() + " ");
        }
        Console.WriteLine();

        // Create the query with two where clause.
        var queryLowNums3 =
            from num in numbers
            where num < 5
            where num % 2 == 0
            select num;

        // Execute the query
        foreach (var s in queryLowNums3)
        {
            Console.Write(s.ToString() + " ");
        }

    }
}
// Output: 
// 4 2 0
// 4 2 0


9. where 절에는 Boolean 값을 반환하는 메서드를 하나 이상 포함할 수 있습니다.

10. 다음 예제에서는 where 절에 메서드를 사용하여 범위 변수의 현재 값이 짝수인지 홀수인지 결정합니다.


class WhereSample3
{       
    static void Main()
    {
        // Data source
        int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

        // Create the query with a method call in the where clause.
        // Note: This won't work in LINQ to SQL unless you have a
        // stored procedure that is mapped to a method by this name.
        var queryEvenNums =
            from num in numbers
            where IsEven(num)
            select num;

         // Execute the query.
        foreach (var s in queryEvenNums)
        {
            Console.Write(s.ToString() + " ");
        }
    }

    // Method may be instance method or static method.
    static bool IsEven(int i)
    {
        return i % 2 == 0;
    }    
}
//Output: 4 8 6 2 0


11. where 절은 필터링 메커니즘입니다.

12. 쿼리 식의 모든 위치에(positioned almost anywhere) 사용할 수 있지만 첫 번째 또는 마지막 절은 될 수 없습니다.

13. where 절은 소스 요소 필터링을 그룹화 전에 할지, 그룹화 후에 할지에 따라 group 절 앞이나 뒤에 올 수 있습니다.

14. 지정한 조건자가 데이터 소스의 요소에 대해 올바르지 않으면 컴파일 타임 오류가 발생합니다.

15. 이는 LINQ의 강력한 형식 검사(the strong type-checking) 기능의 장점(benefit) 중 하나입니다.

16. 컴파일 타임에 where 키워드는 Where 표준 쿼리 연산자 메서드에 대한 호출로 변환됩니다.






yield


1. 여러분이 문장에 yield 키워드를 사용하면, 거기에 나타난 메서드, 연산자, get 접근자가 반복기(iterator)라는 것을 표시(indicate)하는 것입니다.

2. yield를 사용하여 반복기를 정의할 경우 사용자 지정 컬렉션 형식에 IEnumerableIEnumerator 패턴을 구현하면 명시적 추가 클래스(열거형의 상태를 보관하는 클래스, 예제는 IEnumerator<T> 참조)를 사용하지 않아도 됩니다.

3. 다음 예제에서는 yield 문장의 두가지 형태를 보여줍니다.


yield return <expression>;
yield break;


4. yield return 문은 각 요소를 한 번에 하나씩(one at a time) 반환하기 위해 사용합니다

5. 여러분은 foreach 문 또는 LINQ 쿼리를 사용할 때 반복기 메서드를 소비합니다.

6. foreach 루프의 각 반복마다 반복기 메서드를 호출합니다.

7. 반복기 메서드에서 yield return 문에 도달한 경우, expression이 반환되고, 코드의 현재 위치가 유지(retain)됩니다.

8. 다음 번의 반복기 함수 호출 시에, 해당 위치부터 실행이 재개됩니다.

9. 여러분은 해당 반복을 종료하기 위해 yield break 문을 사용할 수 있습니다.

10. 반복기에 대한 자세한 내용은 반복기를 참조하십시오.

<반복기 메서드 및 Get 접근자>

11. 반복기 선언은 다음 요구 사항을 충족해야 합니다(meet the following requirements).

12. 반환 형식은 IEnumerable, IEnumerable<T>, IEnumerator, IEnumerator<T>가 되어야 합니다.

13. 선언에 ref 또는 out 매개 변수가 들어갈 수 없습니다.

14. IEnumerable 또는 IEnumerator를 반환하는 반복기의 yield 형식은 object입니다.

15. 반복기가 IEnumerable<T> 또는 IEnumerator<T>를 반환할 경우 yield return 문의 식 형식에서 제네릭 형식 매개 변수로 암시적 변환이 있어야 합니다.

16. 다음 특징을 지닌 메서드에 yield return 또는 yield break 문을 포함할 수 없습니다.

    1) 무명 메서드. 자세한 내용은 무명 메서드를 참조하십시오.

    2) unsafe 블록을 포함하는 메서드. 자세한 내용은 unsafe를 참조하십시오.

<예외 처리>

17. yield return 문은 try-catch 블록 안에 입력할 수 없습니다.

18. yield return 문은 try-finally 문의 try 블록 내에 입력할 수 있습니다.

19. yield break 문은 try 또는 catch 블록 안에서 사용할 수 있으나 finally 블록 안에서는 사용할 수 없습니다.

20. foreach 본문 (반복기 메서드 바깥)에서 예외를 throw하는 경우, 반복기 메서드에 있는 finally 블록이 실행됩니다.

21. 다음 코드는 반복기 메서드로부터 IEnumerable<string>을 반환하고 해당 요소를 반복합니다.


IEnumerable<string> elements = MyIteratorMethod();
foreach (string element in elements)
{
   …
}


22. MyIteratorMethod 호출은 메서드의 본문을 실행하지 않습니다.

23. 대신 해당 호출은 IEnumerable<string>을 반환해서 elements 변수에 담습니다.

24. foreach 루프 반복 중에, elements에 대한 MoveNext 메서드가 호출됩니다.

25. 이 호출은 다음 yield return 문에 도달할 때가지 MyIteratorMethod의 본문을 실행합니다.

26. yield return 문의 반환 식은, 루프 본문에서 소비하는 element 변수의 값을 결정할 뿐 아니라 IEnumeragble<string>인 elements의 Current 속성 또한 결정합니다.

27. foreach 루프의 차후 반복마다 전에 중지했던(left off) 곳부터 반복기 본문의 실행을 재개하고 yield return 문에 도달하면 중지됩니다.

28. foreach 루프는 반복기 메서드의 끝 또는 yield break 문에 도달했을 때 종료됩니다.

<예제>

29. 다음 에제는 for 루프 안에 yield return 문이 있습니다.

30. Process 내에 foreach 문의 본문을 반복할 때마다, Power 반복기 함수로의 호출이 발생합니다.

31. 반복기 함수를 호출할 때마다, 다음번 for 루프를 반복할 때의 yield return 문 실행까지 진행합니다proceed).

32. 해당 반복기 메서드의 반환 형식은 반복기 인터페이스 형식인 IEnumerable입니다.

33. 반복기 메서드가 호출되면 숫자의 거듭제곱(the powers)을 포함하는 열거 가능 개체를 반환합니다.


public class PowersOf2
{
    static void Main()
    {
        // Display powers of 2 up to the exponent of 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }

    public static System.Collections.Generic.IEnumerable<int> Power(int number, int exponent)
    {
        int result = 1;

        for (int i = 0; i < exponent; i++)
        {
            result = result * number;
            yield return result;
        }
    }

    // Output: 2 4 8 16 32 64 128 256
}


34. 다음 예제는 반복기인 get 접근자를 소개합니다.

35. 예제에서 각 yield return 문은 사용자 정의  클래스의 인스턴스를 반환합니다.


public static class GalaxyClass
{
    public static void ShowGalaxies()
    {
        var theGalaxies = new Galaxies();
        foreach (Galaxy theGalaxy in theGalaxies.NextGalaxy)
        {
            Debug.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears.ToString());
        }
    }

    public class Galaxies
    {

        public System.Collections.Generic.IEnumerable<Galaxy> NextGalaxy
        {
            get
            {
                yield return new Galaxy { Name = "Tadpole", MegaLightYears = 400 };
                yield return new Galaxy { Name = "Pinwheel", MegaLightYears = 25 };
                yield return new Galaxy { Name = "Milky Way", MegaLightYears = 0 };
                yield return new Galaxy { Name = "Andromeda", MegaLightYears = 3 };
            }
        }

    }

    public class Galaxy
    {
        public String Name { get; set; }
        public int MegaLightYears { get; set; }
    }
}






'프로그래밍 > C#' 카테고리의 다른 글

제네릭  (0) 2016.06.11
C# 기타  (0) 2016.06.10
C# 키워드 T~Z  (0) 2016.06.03
C# 키워드 O~S  (0) 2016.05.31
C# 키워드 F~N  (0) 2016.05.21
:
Posted by 지훈2