Action, Func, Lambda

2022. 1. 19. 00:00CSharp/Advance

반응형

Action

매개 변수가 없고 값을 반환하지 않는 메서드를 캡슐화 한 대리자 (delegate)

 

Action<T>

매개 변수가 하나이고 값을 반환하지 않는 메서드를 캡슐화 한 대리자 (delegate)

 

Action<T1, ..... Tn>  

매개 변수가 n 개이고 값을 반환하지 않는 메서드를 캡슐화 한 대리자 (delegate)

(n = 최대 16,  입력 가능 parameter 가 16개까지 가능하다.)

 

Func<TResult>

매개 변수가 없고 TResult 매개 변수에 지정된 형식의 값을 반환하는 메서드를 캡슐화 한 대리자 (delegate)

 

Func<T, TResult>

매개 변수가 하나이고 TResult 매개 변수에 지정된 형식의 값을 반환하는 메서드를 캡슐화 한 대리자 (delegate)

 

Func<T1, .... Tn, TResult>

매개 변수가 n 개이고 TResult 매개 변수에 지정된 형식의 값을 반환하는 메서드를 캡슐화 한 대리자 (delegate)

(n = 최대 16, 입력 가능 parameter 가 16개까지 가능하다.)

 

이 두 가지 Generic delegate type 을  이용해서 delegate type을 따로 선언(정의) 하지 않고 사용 가능하다.

 

이전 delegate 에서 사용한 code 를 Action, Func 을 사용하여 수정해 보자.

 

public class Action_Func
{
    // Delegate type 선언
    //public delegate Shape CreateShapeDelegate();
    //public delegate void AddCircleDelegate(Circle e);
    //public delegate void AddRectangleDelegate(Rectangle e);
    //public delegate void AddTriangleDelegate(Triangle e);

    public class Shape
    {
        public Shape()
        {
            PrintDraw();
        }
        protected virtual string GetClassName()
        {
            return GetType().Name;
        }

        protected virtual void PrintDraw()
        {
            Console.WriteLine($"Drawing a {GetClassName()}");
        }
    }
    public class Circle : Shape { }
    public class Rectangle : Shape { }
    public class Triangle : Shape { }

    public class Creator
    {
        //// call back 용
        //public AddCircleDelegate? AddCircleDelegateHandler;
        //public AddRectangleDelegate? AddRectangleDelegateHandler;
        //public AddTriangleDelegate? AddTriangleDelegateHandler;

        //// multi method call
        //public CreateShapeDelegate? MultiAction;

        //Func 와 Action 을 사용하여 수정
        public Func<Shape> MultiAction;
        public Action<Circle> AddCircleDelegateHandler;
        public Action<Rectangle> AddRectangleDelegateHandler;
        public Action<Triangle> AddTriangleDelegateHandler;

        public Shape MakeShape() => new Shape();
        public Circle MakeCircle() => new Circle();
        public Rectangle MakeRectangle() => new Rectangle();
        public Triangle MakeTriangle() => new Triangle();

        public void Test()
        {
            // 공변성 Test
            MultiAction = MakeShape;
            MultiAction += MakeCircle;
            MultiAction += MakeRectangle;
            MultiAction += MakeTriangle;
            MultiAction -= MakeCircle;

            Console.WriteLine("MultiAction test");
            MultiAction();

            Console.WriteLine();
            Console.WriteLine("Message fire test");
            AddCircleDelegateHandler?.Invoke(new Circle());
            AddRectangleDelegateHandler?.Invoke(new Rectangle());
            AddTriangleDelegateHandler?.Invoke(new Triangle());
        }

    }

    public class Test : ITest
    {
        List<Shape> _shapes = new List<Shape>();
        Creator creator = new Creator();

        public void Run()
        {
            //creator.AddCircleDelegateHandler += OnAddCircle;
            //creator.AddRectangleDelegateHandler += OnAddRectangle;
            //creator.AddTriangleDelegateHandler += OnAddTriangle;

            creator.Test();   

            // 반공변성 Test (모두 OnAddShape 에서 처리)
            creator.AddCircleDelegateHandler += OnAddMultiHandler;
            creator.AddRectangleDelegateHandler += OnAddMultiHandler;
            creator.AddTriangleDelegateHandler += OnAddMultiHandler;
        }
        public void OnAddCircle(Circle circle)
        {
            Console.WriteLine($"adding {circle.GetType().Name} in List<Shape>");
            _shapes.Add(circle);
            Console.WriteLine($"added {circle.GetType().Name} in List<Shape>");
        }
        public void OnAddRectangle(Rectangle rectangle)
        {
            Console.WriteLine($"adding {rectangle.GetType().Name} in List<Shape>");
            _shapes.Add(rectangle);
            Console.WriteLine($"added {rectangle.GetType().Name} in List<Shape>");
        }
        public void OnAddTriangle(Triangle triangle)
        {
            Console.WriteLine($"adding {triangle.GetType().Name} in List<Shape>");
            _shapes.Add(triangle);
            Console.WriteLine($"added {triangle.GetType().Name} in List<Shape>");
        }

        public void OnAddMultiHandler(Shape shape)
        {
            Console.WriteLine($"adding {shape.GetType().Name} in List<Shape>");
            _shapes.Add(shape);
            Console.WriteLine($"added {shape.GetType().Name} in List<Shape>");
        }

    }
}

 

delegate type 선언 부분이 주석 처리 되었고

// Delegate type 선언
//public delegate Shape CreateShapeDelegate();
//public delegate void AddCircleDelegate(Circle e);
//public delegate void AddRectangleDelegate(Rectangle e);
//public delegate void AddTriangleDelegate(Triangle e);

delegate type 을 사용하여 변수를 정의 하는 부분 또한 주석 처리 되고 다음과 같이 변경되었다.

//// call back 용
//public AddCircleDelegate? AddCircleDelegateHandler;
//public AddRectangleDelegate? AddRectangleDelegateHandler;
//public AddTriangleDelegate? AddTriangleDelegateHandler;

//// multi method call
//public CreateShapeDelegate? MultiAction;

//Func 와 Action 을 사용하여 수정
public Func<Shape> MultiAction;
public Action<Circle> AddCircleDelegateHandler;
public Action<Rectangle> AddRectangleDelegateHandler;
public Action<Triangle> AddTriangleDelegateHandler;

 

Lambda Expressions

 

익명 함수를 만드는 방법입니다. 

//식이 본문으로 포함된 식 람다 (Expression lambda)
(input-parameters) => expression

//문 블록이 본문으로 포함된 문 람다 (Statement lambda)
(input-parameters) => { <sequence-of-statements> }

 

간단한 람다식 

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

Expression Tree  형식으로 변환도 가능

System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
Console.WriteLine(e);
// Output:
// x => (x * x)

Linq 문을 작성할 때 람다 식을 사용하기도 함

int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);
Console.WriteLine(string.Join(" ", squaredNumbers));
// Output:
// 4 9 16 25

 

람다식을 사용하여 위에 Action , Func 로 이루어진 코드를 변경해 보자

public class LambdaExpressions
{

    public class Shape
    {
        public Shape()
        {
            PrintDraw();
        }
        protected virtual string GetClassName()
        {
            return GetType().Name;
        }

        protected virtual void PrintDraw()
        {
            Console.WriteLine($"Drawing a {GetClassName()}");
        }
    }
    public class Circle : Shape { }
    public class Rectangle : Shape { }
    public class Triangle : Shape { }

    public class Creator
    {

        //Func 와 Action 을 사용하여 수정
        public Func<Shape> MultiAction;
        public Action<Circle> AddCircleDelegateHandler;
        public Action<Rectangle> AddRectangleDelegateHandler;
        public Action<Triangle> AddTriangleDelegateHandler;

        public void Test()
        {
            // 공변성 Test
            // lambda 식을 이용하여 적용
            MultiAction = () => new Shape(); 
            MultiAction += () => new Circle();
            MultiAction += () => new Rectangle();
            MultiAction += () => new Triangle();
            MultiAction -= () => new Circle();
            // lambda 식을 사용할 경우 -= 를 통한 remove 동작은
            // 이전 lambda 식을 가르킬수 있는 방법이 없으므로 동작하지 않는다.
            // event 등록 및 삭제 시에 많이 하는 실수 이므로 주의하자
            // 이로인해 memory leak 이 발생하는 경우가 많다.

            Console.WriteLine("MultiAction test");
            MultiAction();

            Console.WriteLine();
            Console.WriteLine("Message fire test");
            AddCircleDelegateHandler?.Invoke(new Circle());
            AddRectangleDelegateHandler?.Invoke(new Rectangle());
            AddTriangleDelegateHandler?.Invoke(new Triangle());
        }

    }

    public class Test : ITest
    {
        List<Shape> _shapes = new List<Shape>();
        Creator creator = new Creator();

        public void Run()
        {

            // lambda 식으로 처리
            // 아래와 같은 경우는 lambda 식이 중복 되므로 
            // 다음과 같이 처리 하자
            Action<Shape> multiAction = shape =>
            {
                Console.WriteLine($"adding {shape.GetType().Name} in List<Shape>");
                _shapes.Add(shape);
                Console.WriteLine($"added {shape.GetType().Name} in List<Shape>");
            };

            creator.AddCircleDelegateHandler += multiAction;
            creator.AddRectangleDelegateHandler += multiAction;
            creator.AddTriangleDelegateHandler += multiAction;
            creator.Test();
        }
    }
}

 

관련영상

https://youtu.be/HPqp5QZnbvY

 

반응형

'CSharp > Advance' 카테고리의 다른 글

제네릭 컬렉션 (Generic Collection)  (0) 2022.01.24
Record  (0) 2022.01.21
Lambda advanced  (0) 2022.01.20
Event (이벤트)  (0) 2022.01.18
Delegate (대리자)  (0) 2022.01.17