수색…


비고

개요

대리자 형식 은 특정 메서드 서명을 나타내는 형식 입니다. 이 유형의 인스턴스는 일치하는 서명이있는 특정 메소드를 참조합니다. 메서드 매개 변수에는 대리자 형식이있을 수 있으므로이 메서드는 호출 될 수있는 다른 메서드에 대한 참조를 전달해야합니다.

기본 제공 대리자 형식 : Action<...> , Predicate<T>Func<...,TResult>

System 네임 스페이스에 포함 된 Action<...> , Predicate<T>Func<...,TResult> (가) "..."0 16 일반 타입 파라미터 사이 나타내는 대리자 (0 파라미터, Action 비 - 인 일반적인).

Func 은 반환 형식이 TResult 와 일치하는 메서드를 나타내며 Action 은 반환 값 (void)이없는 메서드를 나타냅니다. 두 경우 모두, 추가 제네릭 형식 매개 변수는 메서드 매개 변수와 순차적으로 일치합니다.

Predicate 는 boolean 리턴 유형의 메소드를 나타내고, T는 입력 매개 변수입니다.

사용자 지정 대리자 형식

명명 된 대리자 형식은 delegate 키워드를 사용하여 선언 할 수 있습니다.

델리게이트 호출

대리자는 메서드와 동일한 구문을 사용하여 호출 할 수 있습니다. 대리자 인스턴스의 이름과 매개 변수가 포함 된 괄호입니다.

대리인에게 할당

대리인은 다음과 같은 방법으로 할당 될 수 있습니다.

  • 명명 된 메소드 지정하기
  • 람다를 사용하여 익명 메소드 할당
  • delegate 키워드를 사용하여 명명 된 메서드 지정

대표자 합치기

+ 연산자를 사용하여 하나의 대리자 인스턴스에 여러 대리자 개체를 할당 할 수 있습니다. - 연산자를 사용하여 다른 대리자에서 구성 요소 대리자를 제거 할 수 있습니다.

명명 된 메서드 대리자의 기본 참조

델리게이트에 명명 된 메서드를 할당 할 때 다음과 같은 경우 동일한 기본 개체를 참조합니다.

  • 클래스의 동일한 인스턴스에서 동일한 인스턴스 메소드입니다.

  • 그것들은 클래스에서 같은 정적 메서드입니다.

    public class Greeter
    {
        public void WriteInstance()
        {
            Console.WriteLine("Instance");
        }
    
        public static void WriteStatic()
        {
            Console.WriteLine("Static");
        }
    }
    
    // ...
    
    Greeter greeter1 = new Greeter();
    Greeter greeter2 = new Greeter();
    
    Action instance1 = greeter1.WriteInstance;
    Action instance2 = greeter2.WriteInstance;
    Action instance1Again = greeter1.WriteInstance;
    
    Console.WriteLine(instance1.Equals(instance2)); // False
    Console.WriteLine(instance1.Equals(instance1Again)); // True
    
    Action @static = Greeter.WriteStatic;
    Action staticAgain = Greeter.WriteStatic;
    
    Console.WriteLine(@static.Equals(staticAgain)); // True
    

델리게이트 형식 선언

다음 구문은 생성 delegate 이름과 타입 NumberInOutDelegate 취하는 방법을 나타내는, int 및 반환 int .

public delegate int NumberInOutDelegate(int input);

다음과 같이 사용할 수 있습니다.

public static class Program
{
    static void Main()
    {
        NumberInOutDelegate square = MathDelegates.Square;
        int answer1 = square(4); 
        Console.WriteLine(answer1); // Will output 16

        NumberInOutDelegate cube = MathDelegates.Cube;
        int answer2 = cube(4);
        Console.WriteLine(answer2); // Will output 64            
    }
}

public static class MathDelegates
{
    static int Square (int x)
    {
        return x*x;
    }

    static int Cube (int x)
    {
        return x*x*x;
    }
}

example 대리자 인스턴스는 Square 메서드와 같은 방식으로 실행됩니다. 대리자 인스턴스는 말 그대로 호출자의 대리자 역할을합니다. 호출자가 대리자를 호출하고 대리자가 대상 메서드를 호출합니다. 이 간접 지정은 호출자를 대상 메서드에서 분리합니다.


일반 대리자 형식을 선언 할 수 있습니다.이 경우 일부 형식 인수에서 형식이 공변 ( out ) 또는 반 변형 ( in )이라는 것을 지정할 수 있습니다. 예 :

public delegate TTo Converter<in TFrom, out TTo>(TFrom input);

다른 일반 형식과 마찬가지로 일반 대리자 형식은 다음과 같은 제약 조건을 가질 수 있습니다 where TFrom : struct, IConvertible where TTo : new() .

이벤트 핸들러 유형과 같은 멀티 캐스트 대리자에 사용되는 델리 게이트 유형에 대한 공동 및 contravariance를 피하십시오. 이는 분산 때문에 런타임 유형이 컴파일 타임 유형과 다른 경우 연결 ( + )이 실패 할 수 있기 때문입니다. 예를 들어 다음을 피하십시오.

public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);

대신 불변의 제네릭 형식을 사용하십시오.

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

또한 일부 매개 변수가 ref 또는 out 수정되는 대리자가 다음과 같이 지원됩니다.

public delegate bool TryParser<T>(string input, out T result);

(샘플 TryParser<decimal> example = decimal.TryParse; ), 또는 마지막 매개 변수에 params 자가있는 대리자. 대리인 유형에는 선택적 매개 변수가있을 수 있습니다 (기본값 제공). 대리자 유형은 서명 또는 반환 유형에 int* 또는 char* 와 같은 포인터 유형을 사용할 수 있습니다 ( unsafe 키워드 사용). 대리자 형식 및 해당 매개 변수는 사용자 지정 특성을 전달할 수 있습니다.

펑크 , 동작 및 술어 델리게이트 유형

시스템 네임 스페이스에는 Func<..., TResult> 형식의 대리자 형식이 포함되어 있으며 0에서 15 개의 일반 매개 변수 사이에 TResult 형식을 반환합니다.

private void UseFunc(Func<string> func)
{
    string output = func(); // Func with a single generic type parameter returns that type
    Console.WriteLine(output);
}

private void UseFunc(Func<int, int, string> func)
{
    string output = func(4, 2); // Func with multiple generic type parameters takes all but the first as parameters of that type
    Console.WriteLine(output);
}

System 네임 스페이스에는 다른 수의 일반 매개 변수 (0에서 16까지)가있는 Action<...> 대리자 형식도 들어 있습니다. Func<T1, .., Tn> 과 비슷하지만 항상 void 반환합니다.

private void UseAction(Action action)
{
    action(); // The non-generic Action has no parameters
}

private void UseAction(Action<int, string> action)
{
    action(4, "two"); // The generic action is invoked with parameters matching its type arguments
}

Predicate<T>Func 한 형태이지만 항상 bool 반환합니다. 술어는 사용자 정의 기준을 지정하는 방법입니다. 입력 값과 술어 내에 정의 된 논리에 따라 true 또는 false 리턴합니다. 따라서 Predicate<T>Func<T, bool> 과 같은 방식으로 동작하며 둘 다 초기화되어 동일한 방식으로 사용될 수 있습니다.

Predicate<string> predicate = s => s.StartsWith("a");
Func<string, bool> func = s => s.StartsWith("a");

// Both of these return true
var predicateReturnsTrue = predicate("abc");
var funcReturnsTrue = func("abc");

// Both of these return false
var predicateReturnsFalse = predicate("xyz");
var funcReturnsFalse = func("xyz");

Predicate<T> 또는 Func<T, bool> 것을 사용할지 선택하는 것은 실제로 문제입니다. Predicate<T> 는 저자의 의도를 더 잘 표현한 반면, Func<T, bool> 은 C # 개발자의 상당수에게 익숙 할 것입니다.

그 외에도 다른 API와 상호 작용할 때 특히 옵션 중 하나만 사용할 수있는 경우가 있습니다. 예를 들어 List<T>Array<T> 일반적으로 메서드에 Predicate<T> 를 사용하지만 대부분의 LINQ 확장에서는 Func<T, bool> 만 허용합니다.

대리자에 명명 된 메서드 할당

명명 된 메서드는 서명이 일치하는 대리자에 할당 할 수 있습니다.

public static class Example
{
    public static int AddOne(int input)
    {
        return input + 1;
    }
}


Func<int,int> addOne = Example.AddOne

Example.AddOne 소요 int 하고 반환 int 의 서명이 대리자와 일치 Func<int,int> . Example.AddOne 은 일치하는 서명을 가지고 있기 때문에 addOne 직접 할당 할 수 있습니다.

대의원 평등

델리게이트에서 .Equals() 를 호출 .Equals() 참조 평등과 비교됩니다.

Action action1 = () => Console.WriteLine("Hello delegates");
Action action2 = () => Console.WriteLine("Hello delegates");
Action action1Again = action1;

Console.WriteLine(action1.Equals(action1)) // True
Console.WriteLine(action1.Equals(action2)) // False
Console.WriteLine(action1Again.Equals(action1)) // True

이러한 규칙은 멀티 캐스트 대리자에서 += 또는 -= 를 수행 할 때도 적용됩니다 (예 : 이벤트 구독 및 가입 취소시).

람다에 의한 델리게이트 할당

Lambda는 익명 메소드를 생성하여 델리게이트에 할당 할 수 있습니다.

Func<int,int> addOne = x => x+1;

이런 식으로 변수를 만들 때는 명시 적으로 타입 선언이 필요합니다.

var addOne = x => x+1; // Does not work

대리자를 매개 변수로 전달

델리게이트는 유형이 지정된 함수 포인터로 사용할 수 있습니다.

class FuncAsParameters
{
  public void Run()
  {
    DoSomething(ErrorHandler1);
    DoSomething(ErrorHandler2);
  }

  public bool ErrorHandler1(string message)
  {
    Console.WriteLine(message);
    var shouldWeContinue = ...  
    return shouldWeContinue;
  }

  public bool ErrorHandler2(string message)
  {
    // ...Write message to file...
    var shouldWeContinue = ...  
    return shouldWeContinue;
  }

  public void DoSomething(Func<string, bool> errorHandler)
  {
    // In here, we don't care what handler we got passed!
    ...
    if (...error...)
    {
      if (!errorHandler("Some error occurred!"))
      {
        // The handler decided we can't continue
        return;
      }
    }
  }
}

대리인 결합 (멀티 캐스트 대리인)

덧셈 + 뺄셈 - 작업 위임 인스턴스를 결합 할 수 있습니다. 위임자는 할당 된 위임자 목록을 포함합니다.

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace DelegatesExample {
    class MainClass {
        private delegate void MyDelegate(int a);

        private static void PrintInt(int a) {
            Console.WriteLine(a);
        }

        private static void PrintType<T>(T a) {
            Console.WriteLine(a.GetType());
        }

        public static void Main (string[] args)
        {
            MyDelegate d1 = PrintInt;
            MyDelegate d2 = PrintType;

            // Output:
            // 1
            d1(1);

            // Output:
            // System.Int32
            d2(1);

            MyDelegate d3 = d1 + d2;
            // Output:
            // 1
            // System.Int32
            d3(1);

            MyDelegate d4 = d3 - d2;
            // Output:
            // 1
            d4(1);

            // Output:
            // True
            Console.WriteLine(d1 == d4);
        }
    }
}

이 예제에서 d3d1d2 대리자의 조합이므로 호출 될 때 프로그램은 1System.Int32 문자열을 모두 출력합니다.


대리자와 무효 반환 유형 결합 :

멀티 캐스트 대리자가 nonvoid 반환 형식 인 경우 호출자는 호출 할 마지막 메서드에서 반환 값을받습니다. 앞의 메서드는 여전히 호출되지만 반환 값은 무시됩니다.

    class Program
    {
        public delegate int Transformer(int x);

        static void Main(string[] args)
        {
            Transformer t = Square;
            t += Cube;
            Console.WriteLine(t(2));  // O/P 8 
        }

        static int Square(int x) { return x * x; }

        static int Cube(int x) { return x*x*x; }
    }

t(2) 는 첫 번째 Square 를 호출 한 다음 Cube 호출합니다. Square의 반환 값은 무시되고 마지막 메소드의 반환 값 즉 Cube 가 유지됩니다.

안전 호출 멀티 캐스트 대리인

혹시 멀티 캐스트 델리게이트를 호출하고 싶었지만 체인에서 예외가 발생하더라도 전체 호출 목록을 호출하기를 원합니다. 그럼 당신은 행운을 빌어, 나는 그 일을하는 확장 메소드를 만들었고, 전체 목록의 실행이 완료된 후에 AggregateException 던졌습니다 :

public static class DelegateExtensions
{
    public static void SafeInvoke(this Delegate del,params object[] args)
    {
        var exceptions = new List<Exception>();

        foreach (var handler in del.GetInvocationList())
        {
            try
            {
                handler.Method.Invoke(handler.Target, args);
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }

        if(exceptions.Any())
        {
            throw new AggregateException(exceptions);
        }
    }
}

public class Test
{
    public delegate void SampleDelegate();

    public void Run()
    {
        SampleDelegate delegateInstance = this.Target2;
        delegateInstance += this.Target1;

        try
        {
            delegateInstance.SafeInvoke();
        } 
        catch(AggregateException ex)
        {
            // Do any exception handling here
        }
    }

    private void Target1()
    {
        Console.WriteLine("Target 1 executed");
    }

    private void Target2()
    {
        Console.WriteLine("Target 2 executed");
        throw new Exception();
    }
}

이 결과는 다음과 같습니다.

Target 2 executed
Target 1 executed

SaveInvoke 없이 직접 호출하면 대상 2 만 실행됩니다.

대의원 내부 폐쇄

클로저는 Parent 메서드 변수 및 부모 범위에 정의 된 다른 익명 메서드를 사용할 수있는 인라인 익명 메서드입니다.

본질적으로, 클로저는 나중에 실행될 수 있지만 처음 생성 된 환경을 유지하는 코드 블록입니다. 즉, 클로저는 코드를 생성 한 메소드의 로컬 변수 등을 사용할 수 있습니다. 메소드가 실행을 완료했습니다. - Jon Skeet

delegate int testDel();
static void Main(string[] args)
{
    int foo = 4;
    testDel myClosure = delegate()
    {
        return foo;
    };
    int bar = myClosure();

}

.NET의 클로저 (Closures)에서 가져온 예제.

함수의 변환 캡슐화

public class MyObject{
    public DateTime? TestDate { get; set; }

    public Func<MyObject, bool> DateIsValid = myObject => myObject.TestDate.HasValue && myObject.TestDate > DateTime.Now;

    public void DoSomething(){
        //We can do this:
        if(this.TestDate.HasValue && this.TestDate > DateTime.Now){
            CallAnotherMethod();
        }

        //or this:
        if(DateIsValid(this)){
            CallAnotherMethod();
        }
    }
}

깨끗한 코딩의 정신으로 Func로 위와 같은 검사 및 변환을 캡슐화하면 코드를 읽고 이해하기가 더 쉽습니다. 위의 예제는 매우 간단하지만 각기 다른 유효성 검사 규칙을 가진 여러 DateTime 속성이 있고 서로 다른 조합을 확인하고 싶다면 어떻게해야할까요? 리턴 로직을 확립 한 단순한 한 줄 Func은 읽기 쉽고 코드의 복잡성을 줄일 수 있습니다. 아래의 Func 호출을 생각해보고 얼마나 많은 코드가 메서드를 복잡하게 만들지 상상해보십시오.

public void CheckForIntegrity(){
    if(ShipDateIsValid(this) && TestResultsHaveBeenIssued(this) && !TestResultsFail(this)){
        SendPassingTestNotification();
    }
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow