이터레이터 메서드가 입력 변수로 반드시 시퀀스를 받을 필요는 없다. 이터레이터 메서드를 구현할 때 새로운 시퀀스를 출력하기 위해 yield return을 자주 사용하는데 이 과정에서 입력 시퀀스를 활용하는 대신 새로운 팩토리 메서드를 이용할 수도 있다. 작업을 수행하기 전에 필요한 요소를 모두 생성해서 컬렉션에 저장해두는 대신 필요할 때마다 개발 요소를 생성하는 식이다. 이 방법으로 코드를 작성하면 사용 되지 않을 요소를 미리 생성하는 것을 피할 수 있다. 예시 1) 정숫값의 시퀀스 생성 static IList CreateSequence(int numberOfElements, int startAt, int stepBy) { var collection = new List(numberOfElements); ..
이터레이터 메서드의 사용 예를 살피다 보면 크게 두 가지 유형이 있다. 시퀀스 내의 개별 항목을 이용하여 작업을 수행하는 유형 외에도, 시퀀스의 순회 방식에 변경을 주는 유형이 있다. (예를 들어 특정 조건에 부합하는 항목만을 가져오거나, 매 N번째 항목만을 건너뛰며 가져오는 경우) 이처럼 순회 방식에 변경을 주는 유형은 어떤 유형의 업무라도 실제로 개별 항목에 대하여 수행해야 하는 작업의 내용과 시퀀스의 순회 방식은 서로 연관이 없으므로 분리하여 처리할 수 있다. 이 두 가지를 한 번에 수행하면 코드 간의 결합도가 높아지고 중복 코드가 발생하게 된다. Action, Predicate, Function 익명 델리게이트를 사용할 때는 function과 action이라는 두 가지 패턴이 있고 func..
C#은 다양한 반복 구문을 제공하기 때문에 일상적인 개발에는 전혀 지장이 없다. 하지만 쿼리 구문을 사용하는 것이 반복문을 사용하는 것보다 좋은 경우가 꽤 있다. 쿼리 구문의 장점! 1) 프로그램의 논리를 명령형 방식에서 선언적인 방식으로 전환할 수 있다. 2) 질의의 내용을 구성할 수 있을 뿐 아니라 개별 항목에 대해 수행하려는 작업의 수행 시기를 연기할 수 있다. 3) 사용자의 의도를 더 명확하게 드러낼 수 있다. 예시 1) 0부터 99까지의 수에 대해 (X, Y) 좌표 객체를 생성 1-1) 루프 private static IEnumerable ProduceIndices() { for (var x = 0; x
메서드를 작성하다 보면 단일의 객체를 반환하기보다 일련의 시퀀스를 반환해야 하는 경우가 더러 있다. 시퀀스를 반환하는 메서드를 작성해야 한다면 컬렉션을 반환하기보다는 이터레이터를 반환하는 것이 더 좋다. 이터레이터를 반환하면 이를 이용하여 다양한 작업을 좀 더 수월하게 수행할 수 있기 때문이다. cf) 시퀀스(sequence) : 데이터에 순서를 붙여 나열한 것 이터레이터 메서드(iterator method) 이터레이터 메서드란 호출자가 요청한 시퀀스를 생성하기 위해 yield return 문을 사용하는 메서드를 말한다. 다음은 알파벳 소문자에 대한 시퀀스를 생성하는 매우 기본적인 이터레이터 메서드의 구현 예를 나타내었다. public static IEnumerator GenerateAlphabe..
프로그램을 개발하다 보면 List나 Dictionary와 같이 제네릭 컬렉션에 타입 매개변수를 지정하여 사용하게 될 것이다. 기존에 사용 중인 컬렉션 타입에 영향을 주지 않으면서 새로운 기능을 추가하고 싶다면 구체화된 컬렉션 타입에 대해 확장 메서드를 작성하면 된다. 예시 : IEnumerable 이번에도 IEnumerable에 이미 구현된 예시가 있다. 타입 매개변수로 특정 숫자 타입이 전달되었을 경우에만 사용할 수 있는 있는 확장 메서드들이 있다. public static class Enumerable { public static int Average(this IEnumerable sequence); public static int Max(this IEnumerable sequence); pu..
확장 메서드를 이용하면 인터페이스에 새로운 동작을 추가할 수 있다. 인터페이스에는 가능한 한 최소한의 기능만을 정의하고, 확장 메서드를 세트로 함께 구현하면 손쉽게 기능을 확장할 수 있다. 특히 API를 추가적으로 정의하지 않고도 새로운 기능을 추가할 수 있다. 예시 : IEnumerable System.Linq.Enumerable 클래스가 이 기법을 활용한 대표적인 예다. System.Enumerable에는 IEnumerable에 정의된 50개 이상의 확장 메서드가 포함되어 있다. Where, OrderBy, GroupInfo 등이 있다. 이처럼 IEnumerable에 대해 확장 메서드를 정의하면 이미 IEnumerable를 구현하고 있는 클래스를 수정할 필요가 없다. 이전과 동일하게 GetEnum..
C#의 제네릭을 사용하면 컴파일 타임에 타입을 미리 체크하여 에러를 사전에 잡을 수 있고, 컴파일러가 직접 타입을 캐스팅해줘서 편하고 다른 타입이지만 동일한 코드를 사용할 때, 코드 재사용성을 높여줘서 아주 유용하다. 그렇다면 왜 이렇게 편한 제네릭 인터페이스가 있는데 논제네릭 인터페이스도 같이 구현하라고 하는걸까? 논제네릭 인터페이스를 함께 구현해야 하는 이유 제네릭이 생기기 이전에 개발된 코드들을 무시할 수 있다면 좋겠지만 유감스럽게도 무시하는 것이 어려운 것이 사실이다. 새로운 라이브러리를 개발할 때 제네릭 타입뿐 아니라 고전적인 방식도 함께 지원하다면 라이브러리의 활용도를 더 높일 수 있다. 만약 제네릭 타입이 아닌 방식도 지원하겠다고 결정했다면 다음 3가지 요소에 대해서 논제네릭 방식..
제네릭을 사용하다 보면 무심코 제네릭 클래스를 만드는 게 습관이 되어버릴 수도 있다. 하지만 유틸리티 성격의 클래스를 만드는 경우에는 제네릭 클래스를 만드는 것보다 일반 클래스에 제네릭 메서드를 구현하는 것이 더 좋다. 왜 그런지 차근차근 알아보자. 왜 제네릭 메서드로?? 왜 제네릭 메서드로 구현하는 것이 좋은가? 우선, 제네릭 클래스를 작성하면 컴파일러 입장에서는 전체 클래스에 대하여 타입 매개변수에 대한 제약 조건을 고려하여 컴파일을 해야 하지만 일반 클래스 내에 제네릭 메서드들을 배치하면 각 메서드별로 제약 조건을 다르게 설정할 수 있다. 제네릭 클래스로 작성하여 제약 조건의 범위가 넓어지면 넓어질수록 코드를 수정하기가 점점 더 까다로워진다. 정리하자면, 타입 매개변수로 인스턴스 필드를 ..