그러냐

C# 2.0의 새로운 기능들 본문

c#

C# 2.0의 새로운 기능들

관절분리 2016. 1. 28. 11:22
반응형

C# 2.0의 새로운 기능들
 
 
이번 글에서는 Generic을 제외한 C# 2.0의 새로운 기능들에 대해서 소개할 것이다(Solve It의 다른 필자 분이 VB.NET에서의 Generic에 대해서 소개를 하였기 때문에, C#에서의 Generic에 대한 설명은 생략하기로 한다). C# 2.0에 새롭게 추가된 기능이 상당히 많기 때문에 이 글에서 소개하는 내용 중에서 특별히 관심을 끄는 내용이 있다면, C# 2.0 스펙에 대해서 소개하고 있는 서적을 구입하여 보다 자세한 정보를 확인하도록 한다.

반복문의 활용, Iterators
C#의 foreach 문을 사용하게 되면서부터, 기존의 C/C++ 개발자들은 반복문의 활용에 대해서 굉장히 다른 시각을 갖게 되었다. for와 while을 사용하는 일반적인 반복문은 개발자가 반복문이 끝나는 조건을 임의로 설정하기 위하여 사용하지만, 단순히 배열이나 리스트를 반복하기 위해서는 foreach 문을 사용한다. 하지만 foreach 문은 성능과 구현에 있어서 제약 사항이 있었고, C# 2.0에서 새롭게 제공하는 Iterator는 그러한 문제에 대한 해결책을 제공한다.

Iterator는 foreach 문에서 사용되어 지정된 범위만큼 코드를 반복하면서 지속적으로 결과를 반환하는 코드 블록을 가리킨다. 여기서 Iterator는 C# 2.0의 새로운 키워드가 아니라 IEnumerable 인터페이스를 리턴하는 코드 블록 자체를 가리킨다.

원래 어떤 컬렉션이 foreach 문에서 사용되기 위해서는 IEnumerable 인터페이스를 상속받아 GetEnumerator() 메쏘드를 구현해야 하며, 열거자(Enumerator)는 IEnumerator 인터페이스를 상속받아 관련 메쏘드들을 구현해주어야 하는 굉장히 복잡한 코드를 구현해주어야 한다.

하지만 완전한 컬렉션의 구현이 아니라 foreach 문에서 반복적으로 사용되기 위한 클래스를 구현하고자 할 때에도, 이와 같이 복잡한 과정을 거쳐서 컬렉션 클래스를 구현해야 한다면 이는 개발자에게 큰 부담이 아닐 수 없다. 예를 들어서 특정 범위 내의 짝수만을 반환하는 메쏘드를 작성한다면 Iterator를 사용하여 다음과 같이 구현할 수 있다.

 <리스트 1> 짝수만을 반환하는 EvenList 클래스

public class EvenList
{
  public static IEnumerable EvenNumber(int number, int exponent)
  {
    int current = number;
    while(current <= exponent)
    {
      if ( current % 2 == 0 )
        yield return current;
      current++;
    }
  }

  static void Main()
  {
    // 1부터 10까지의 짝수만 출력한다.
    foreach(int i in EvenNumber(1, 10))
      Console.Write("{0} ", i);
  }
}

위의 <리스트 1>을 실행시키면 다음과 같은 결과를 출력할 것이다.

2 4 6 8 10

<리스트 1>에서 EvenNumber 메쏘드가 Iterator로 사용되었으며, yield return 키워드는 while 문이 실행되면서 current 값이 짝수일 때 foreach 문에 결과를 반환하는 코드이다. 이 부분이 기존의 프로그래밍 방식과 다르기 때문에 다소 혼란을 야기할 수 있다. 분명 EvenNumber 메쏘드가 호출되는 도중에 current 값이 짝수가 되어 리턴하게 된다면 아마도 다음 번 EvenNumber 메쏘드 호출 시에 current 값은 언제나 초기 값인 1로 설정될 것이다.

하지만 yield return 키워드가 사용되면 런타임은 yield return이 호출된 위치를 기억하여 다음 번 메쏘드 호출 시에 저장된 위치 다음 코드를 실행시킨다. 이는 마치 다중 스레드 프로그래밍에서 Event 객체를 사용하여 프로그램을 작동시키는 것과 굉장히 비슷하다. 그래서 Iterator를 잘 사용한다면, 마치 다중 쓰레드 프로그래밍을 하는 것과 비슷한 효과를 얻을 수 있을 것 같다는 생각해 해본다. 그리고 Iterator가 가능한 이유는 컴파일러가 IEnumerable과 IEnumerator 인터페이스에 필요한 코드를 자동을 생성하기 때문이다.

참고로 Iterator에서도 IEnumerable와 같이 선언하여 Generic을 사용할 수 있다.

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

Partial 클래스는 기존의 클래스 앞에 partial 키워드를 추가하여 작성할 수 있다. 물론, partial 키워드를 사용한 클래스는 여러 파일에 나뉘어 존재할 수 있다.

 <리스트 2> 여러 파일로 나뉘어진 MyForms Partial 클래스

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

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

<리스트 2>의 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)들은 컴파일 시에 모두 병합된다.

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의 블로그를 통해서 확인하도록 한다.@

 

출처 : http://www.zdnet.co.kr/microsite/powerguide/techguide/solveit/0,39033240,39141062,00.htm

 

 

반응형