'Extension Method'에 해당되는 글 2건

Programming/.NET

[Programming/.NET] - [C#] 확장 메서드(Extension Method) - 1

 

위 글에서는 확장메서드를 직접 구현하고 해당 메서드를 호출하는 방법으로 확장메서드구현 방법에 대해 알아보았습니다. 하지만 기본적인 기능을 구현하는 경우라면 굳이 확장메서드를 일일이 직접 만들필요가 없습니다. 확장메서드를 기반으로 이미 C#에서는 마침표 표기법(Dot Notation)을 지원하고 있기 때문입니다.

 

var result = bb.Where(r => r.Size == 50);

 

위 예제는 마침표 표기법을 사용하고 있는 예시입니다. 이 메서드를 호출하고 난 뒤의 결과는 Filter메서드를 호출한것과 동일한 결과를 가져옵니다. 웬만한 처리작업은 LINQ 확장메서드로서 이미 IEnumerable에 구현되어 있는 것입니다.

 

아래는 자주 사용하는 확장메서드를 정리한 표입니다.

 

 All

 모든 데이터가 지정한 조건과 일치하는 true

 비지연

 Any

 데이터에서 지정한 조건과 하나이상 일치하면 true

 비지연

 Contains

 데이터에서 지정한 항목또는 값과 일치하는게 존재하면 true

 비지연

 Count

 데이터에 존재하는 항목의 갯수

 비지연

 First

 데이터의 첫번째 항목 반환

 비지연

 FirstOrDefault

 데이터의 첫번째 항목을 반환하되 데이터가 존재하지 않는다면 기본값을 대신 반환

 비지연

 Last

 데이터의 마지막 항목 반환

 비지연

 LastOrDefault

 데이터의 마지막 항목을 반환하되 데이터가 존재하지 않는다면 기본값을 대신 반환

 비지연

 Max

 데이터중 지정된 요소의 최대값을 반환

 비지연

 Min

 데이터중 지정된 요소의 최소값을 반환

 비지연

 OrderBy

 지정된 요소를 기준으로 Asc정렬

 지연

 OrderByDescending

 지정된 요소를 기준으로 Desc정렬

 지연

 Reverse

 데이터의 순서를 뒤집음

 지연

 Select

 가져올 데이터의 요소를 지정

 지연

 Single

 데이터중 첫번째 항목을 반환하되 항목이 하나이상 존재하면 예외발생

 비지연

 SingleOrDefault

 데이터중 첫번째 항목을 반환하되 항목이 하나이상 존재하면 예외를 발생시키고 하나도 없으면 기본값을 반환

 비지연

 Skip

 지정한 수만큼 항목을 건너뜀

 지연

 SkipWhile

 지정한 조건과 일치하면 해당 항목을 건너뜀

 지연

 Sum

 지정한 조건으로 합계를 구함

 비지연

 Take

 지정한 수만큼의 항목을 반환

 지연

 TakeWhile

 지정한 조건과 일치하는 수만큼의 항목을 반환

 지연

 ToArray

 데이터를 Array형태로 반환

 비지연

 ToDictionary

 데이터를 Dictionary형태로 반환

 비지연

 ToList

 데이터를 List형태로 반환

 비지연

 Where

 지정한 조건과 일치하는 항목을 반환

 지연

 

표에서 '지연'이라고 되어 있는 메서드는 처리과정에서 데이터가 완전히 열거되기 전까지는 실행되지 않음을 의미합니다.

 

예를 들어

 

var result = bb.OrderBy(r => r.Price).Take(1);

bb[1].Price = 100;

 

OrderBy와 Take메서드는 지연되는 메서드입니다. 이 메서드들을 조합해 호출하면 데이터가 모두 열거되기 전까지는 실행되지 않습니다. 그래서 bb[1].Price = 100; 으로 인해 두번째 Box의 값을 수정하면 이 구문의 실행이 결과에 그대로 반영되는 것입니다.

 

반면

 

int result = bb.Sum(r => r.Price);

bb[1].Price = 100;

 

'비지연'은 데이터의 열거상황과는 상관없이 메서드가 호출되는 즉시 실행될 것입니다. 그래서 합계를 구하는 Sum메소드가 호출되면 즉시 모든 Price값의 합계를 구하게 되고 뒤이어 오는 bb[1].Price = 100; 의 실행과는 전혀 무관한 결과를 보여주게 됩니다.

'Programming > .NET' 카테고리의 다른 글

[C#] SqlDataReader  (0) 2017.08.30
[C#] String  (0) 2017.08.22
[C#] 확장 메서드(Extension Method) - 2  (0) 2017.08.16
[C#] ?? 연산자  (0) 2017.08.10
[C#] 형변환(캐스팅 - Casting)  (0) 2017.08.01
[C#] 이벤트(event)  (0) 2017.07.26
0 0
Programming/.NET

Box라는 제품에 대한 개체를 List로 반환해주는 다음 클래스가 있습니다.

 

public class Box

{

    public string Name
    {
        get;
        set;
    }

    public int Size
    {
        get;
        set;
    }

    public int Price

    {

        get;
        set;
    }
}

public class Boxes

    public List<Box> B
    {
        get;
        set;
    }
}

 

Boxes 클래스는 외부에서 공급받은 클래스가 소스가 없다고 가정해 보겠습니다. 그런데 이 클래스에 현재 존재하는 각 제품의 가격합계를 모두 구하는 메서드를 작성해야 한다면 이때 확장 메서드를 유용하게 사용할 수 있습니다.

 

public static class MyBoxes
{
    public static int TotalPrice(this Boxes mybox)
    {
        int total_price = 0;

        foreach (Box b in mybox.B)
            total_price += b.Price;

        return total_price;
    }
}

 

위 클래스에서 this로 시작하는 매개변수에 주목해 주세요. this는 이 메서드가 확장메서드로 사용될 것임을 알려주고 있으며 Boxes라는 클래스에 확장메서드가 적용될것임을 알 수 있습니다. 또한 매개변수로 전달되는 Boxes개체를 사용하면 각 Box제품의 속성에도 접근할 수 있습니다.

 

Boxes bx = new Boxes { B = new List<Box> { new Box { Name = "Big", Size = 100, Price = 1500 },

 new Box { Name = "Normal", Size = 50, Price = 1000 },

 new Box { Name = "small", Size = 50, Price = 500 } } };

int p = bx.TotalPrice();

 

이처럼 확장메서드를 사용하면 본래 클래스에 있었던 메서드처럼 추가적인 메서드를 자연스럽게 구현할 수 있습니다. 이를 응용하면 확장메서드를 클래스가 아닌 인터페이스에 적용하여 같은 인터페이스를 구현하는 모든 개체에 동일한 확장메서드를 구현할 수도 있습니다.

 

public class Boxes : IEnumerable<Box>

    public List<Box> B
    {
        get;
        set;
    }

    public IEnumerator<Box> GetEnumerator()
    {
        return B.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

 

Boxes 클래스가 Ienumerable 인터페이스를 구현하도록 하였습니다.

 

public static class MyBoxes
{
    public static int TotalPrice(this IEnumerable<Box> mybox)
    {
        int total_price = 0;

        foreach (Box b in mybox)
            total_price += b.Price;

        return total_price;
    }       
}

 

위와 같이 클래스를 인터페이스로 바꿔주기만 하면 해당 인터페이스(IEnumerable)에 메서드가 구현됩니다. 예제에서 mybox는 기존의 .B가 제거되었는데 이는 IEnumerable인터페이스에 의해 자체적으로 배열을 순회할 수 있는 기능을 가지게 되었기 때문입니다.

 

IEnumerable<Box> bx = new Boxes { B = new List<Box> { 

new Box { Name = "Big", Size = 100, Price = 1500 }, 

new Box { Name = "Normal", Size = 50, Price = 1000 }, 

new Box { Name = "small", Size = 50, Price = 500 } } };
int p = bx.TotalPrice();

Box[] bb = { new Box { Name = "Big", Size = 100, Price = 1500 }, 

new Box { Name = "Normal", Size = 50, Price = 1000 }, 

new Box { Name = "small", Size = 50, Price = 500 } };
int pp = bb.TotalPrice();

 

Box 개체에 대한 배열에도 확장메서드를 구현하고 있다는것에 주목해 주세요. IEnumerable 인터페이스 자체에 확장메서드를 구현했으므로 이 IEnumerable 인터페이스를 구현하고 있는 배열(Box[] bb)까지도 ToalPrice라는 메서드를 호출할 수 있게 되었다는걸 알 수 있습니다.

 

또한 기존에는 컬렉션처럼 Add메서드를 추가해 초기화를 수행하려면 반드시 클래스가 ICollection<T> 인터페이스를 상속받아야 했는데 C# 6.0부터는 Add메서드가 호출되면 확장메서드에서 Add메서드를 찾는 기능이 추가되어 굳이 ICollection<T> 인터페이스를 상속할 필요가 없어졌습니다.

 

public static class BoxesExtention
{
    public static void Add(this Boxes instanceint num)
    {
        //
    }
}

 

Boxes b = new Boxes() { 1, 2, 3 };

이것만 봐도 확장메서드는 상당히 높은 확장성을 제공하고 있는데 위에서 처럼 IEnumerable 인터페이스에 확장메서드를 적용하여 이번에는 배열안의 특정요소를 필터링하는 메서드를 구현해 보도록 하겠습니다.

 

아래는 말씀드린 기능 구현을 위해 MyBoxes클래스에 추가한 확장메서드입니다.

 

public static IEnumerable<Box> Filter(this IEnumerable<Box> mybox, int selectBoxbySize)
{
    foreach (Box b in mybox) {
        if (b.Size == selectBoxbySize)
            yield return b;
    }
}

 

이 확장메서드는 Box의 Size값을 받는 변수를 매개변수로 받고 있는데 배열을 순회하면서 지정한 Size와 일치하는 Box개체를 IEnumerable형식으로 반환하도록 하고 있습니다.

 

IEnumerable<Box> result = bb.Filter(50);

 

참고로 위와 같은 필터링방식을 좀더 효휼적이고 유연하게 바꾸고자 한다면 람다식을 응용해 다음과 같이 구현할 수 있습니다.

 

public static IEnumerable<Box> Filter(this IEnumerable<Box> mybox, Func<Boxbool> selectBox)
{
    foreach (Box b in mybox) {
        if (selectBox(b))
            yield return b;
    }
}

 

우선 확장메서드를 Func형식으로 바꾼 후

 

IEnumerable<Box> result = bb.Filter(b => b.Size == 50);

IEnumerable<Box> result = bb.Filter(b => b.Size == 50 || b.Price > 500);

 

이와 같이 람다식을 적용하면 특정 필터링조건에 제한되지 않고 자유롭게 필터형식을 지정할 수 있습니다.

0 0
1
블로그 이미지

클리엘