C#은 다양한 반복 구문을 제공하기 때문에 일상적인 개발에는 전혀 지장이 없다. 하지만 쿼리 구문을 사용하는 것이 반복문을 사용하는 것보다 좋은 경우가 꽤 있다.
쿼리 구문의 장점
1. 프로그램의 논리를 명령형 방식에서 선언적인 방식으로 전환할 수 있다.
2. 질의의 내용을 구성할 수 있을 뿐 아니라 개별 항목에 대해 수행하려는 작업의 수행 시기를 연기할 수 있다.
3. 사용자의 의도를 더 명확하게 드러낼 수 있다.
예시 1) 0부터 99가지의 수에 대해 (X, Y) 좌표 객체를 생성
1-1) 루프
var foo = new int[100];
for (var num = 0; num < foo.Length; numm++)
foo[num] = num * num;
foreach (int i in foo)
e.WriteLine(i.toString());
1-2) 쿼리구문
var foo = (from n in Enumerable.Range(0, 100)
select n * n).ToArray();
위 루프를 아래 쿼리 구문처럼 바꿀 수 있지만 이 예시는 당순하여 쿼리 구문의 장점이 그닥 부각되지 않는듯하다. 다음 예시로 쿼리 구문의 장점을 살펴보자
예시 2) (0, 0)에서 떨어진 거리 순으로 정렬하여 객체를 반환
2-1) 루프
private static IEnumerable<Tuple<int, int>> ProduceIndices3()
{
var storage = new List<Tuple<int, int>>();
for (var x = 0; x < 100; x++)
for (var y = 0; x < 100; y++)
storage.Add(Tuple.Create(x, y));
storage.Sort((point1, point2))
=> (point2.Item1 * point2.Item1 + point2.Item2 * point2.Item2)
.CompareTo( point1.Item1 * point1.Item1 + point1.Item2 * point1.Item2));
return storage;
}
2-2) 쿼리구문
private static IEnumerable<Tuple<int, int>> QueryIndices3()
{
return from x in Enumerable.Range(0, 100)
from y in Enumerable.Range(0, 100)
orderby(x * x + y * y)
descending select Tuple.Create(x, y);
}
이제는 장점이 뚜렷하게 보이는것 같다. 명령형으로 구현한 코드는 이전보다 훨씬 이해하기 어려워졌다. 명령형 모델은 동작이 수행되는 절차에 주안을 두기 때문에 동작이 진행되는 과정을 쫓아가다가 혼돈에 빠지기 쉽고, 원래 의도를 잊는 경우가 흔하다.
그에 반면, 쿼리 구문이 갖는 또 다른 장점은 더욱 다양하게 조합이 가능하다는 점이다. 쿼리구문은 개별 항목에 대하여 수행해야 하는 작업을 작은 코드 블록으로 생성한다.
같은 작업을 반복문을 통해 해결하려면 각 작업 단계별로 임시 저장소를 만들거나 추가 메서드를 만들어서 개별 작업들을 조합해야 할 것이다.
쿼리 구문을 사용하였지만 메서드 호출 구문을 사용하더라도 맥락은 비슷하다.
혹자는 쿼리 구문을 통해 작성한 코드가 루프보다 느리게 수행된다고 주장하곤 한다. 통상 루프를 이용하여 직접 코딩하면 쿼리보다 성능이 좋은 코드를 작성할 수 있지만 항상 그런 것은 아니다. 쿼리 구문의 성능이 의심된다면 우선 수행 성능을 측정해보길바라고 LINQ의 병렬 확장을 사용해보는 것도 좋은 방법이다.
결론
반복 구문을 사용해야 한다면 이를 쿼리 구문으로 작성할 수 있는지 생각해 보고 쿼리 구문으로도 어렵다면 메서드 호출 구문까지 고려해보자. 거의 대부분의 경우 반복 구문을 사용하는 것보다 훨씬 깔끔하게 코드를 작성할 수 있을 것이다.
'C# > Effective C#' 카테고리의 다른 글
[Effective C#] Item32 Action, Predicate, Function 과 순회 방식을 분리하라 (0) | 2022.11.15 |
---|---|
[Effective C#] Item 31 시퀀스에 사용할 수 있는 조합 가능한 API를 작성하라. (0) | 2022.11.07 |
[Effective C#] Item 29 컬렉션을 반환하기보다 이터레이터를 반환하는 것이 낫다. (0) | 2022.11.07 |
[Effective C# Item 28] 확장 메서드를 이용하여 구체화된 제네릭 타입을 개선하라. (1) | 2022.10.28 |
[Effective C#] Item27 인터페이스는 간략히 정의하고 기능의 확장은 확장 메서드를 사용하라. (0) | 2022.10.26 |