Log Stash

as an Industrial Personnel

프로그래밍

C#: object => IEnumerable<object>

SavvyTuna 2017. 11. 21. 01:12

1. Enumerable.Repeat()

C#으로 코드를 짜다보면 종종 어떤 하나의 객체를, 그 객체를 담고 있는 IEnumerable<T>로 변환해야 하는일이 종종 생긴다. 그땐 Enumerable.Repeat()을 사용하면 편하다.

예를들어, 어떤 메소드 중간에 객체 obj가 있고, 이것을 IEnumerable<> 타입으로 만들어서 반환해야 한다고 생각해보자.

GameObject obj;

가장 단순하게 생각해보자면, List<> 컨테이너를 하나 할당해서 그 리스트에 값을 집어넣을 수 있다.

List<GameObject> list = new List<GameObject>(1);
list.Add(obj);
return list;

유니티3d 엔진의 코루틴 기능을 알고 있다면, C#의 발생자 문법을 사용하는 방식도 생각해 볼 수 있겠다.

IEnumerable<GameObject> ToEnumerable(GameObject obj)
{
    yield return obj;
    yield break;
}

...

return ToEnumerable(obj);

하지만 대충봐도 알겠지만 귀찮다. 정말 귀찮다. 저 두 세줄 늘어나는게 정말 귀찮다. 특히 (디버깅도 귀찮게 잘 안되는) Linq로 길게 이어지는 람다 함수 체인들 속에서 저런 코드를 짜는건 매우 귀찮다. 게다가 리스트는 메모리도 더 먹겠지.

이럴땐 Enumerable.Repeat() 메소드를 사용해주면 된다.

return Enumerable.Repeat(obj, 1);

메소드 이름에서 유추할 수 있겠지만, obj 객체를 1번 반복해서 내 뱉는 IEnumerable<GameObject>를 만들어낸다.

2. 최근에 써먹어본 곳

최근에 아이템 보상 정보들을 합칠 수 있는 타입이면 하나의 보상 정보로 합치고, 아니면 그냥 냅두는 루틴을 작성했어야 했다.

예를들면, [돈 10, 돈 20, 무기(칼), 무기(창)]의 보상 정보 리스트가 있을 때, 돈 타입의 보상 정보를 하나로 합쳐서 [돈 30, 무기(칼), 무기(창)]의 리스트로 만드는 루틴이라고 생각하면 된다.

보상 정보들을 GroupBy로 묶고, 나온 IGrouping들을 처리할 때 써먹었다.

/// <summary>
/// 아이템 타입에 따라 (가능하면) 수량을 합쳐서 돌려주는 메소드.
/// </summary>
public static IEnumerable<ItemInfo> CombineItemInfo(IEnumerable<ItemInfo> rewards)
{
    IEnumerable<ItemInfo> combinedRewards = rewards
        .GroupBy(reward => reward.subType)
        .Select(group =>
        {
            ItemType itemType = group.FirstOrDefault().itemType; // hmmmm...

            switch (itemType)
            {
                case ItemType.COLLAPSIBLE_TYPE0:
                case ItemType.COLLAPSIBLE_TYPE1:
                case ItemType.COLLAPSIBLE_TYPE2:
                    // Merge quantities
                    int quantity = group.Select(reward => reward.quantity).Sum();
                    return Enumerable.Repeat(new ItemInfo(itemType, group.Key, quantity), 1);

                case ItemType.COLLAPSIBLE_TYPE3:
                case ItemType.COLLAPSIBLE_TYPE4:
                    // Merge in other way..
                    break;

                default:
                    // No need to be merged
                    return group;
            }
        })
        .SelectMany(x => x); // flatMap

    return combinedRewards;
}

대충 gist는 이러하다. 물론 당연히 원본 소스는 아님.