Invoke 메서드를 사용할 때 컴파일이 OK이고 Func <int, int>를 직접 반환해도 OK가 아닌 이유는 무엇입니까?


28

이 사례를 이해하지 못합니다.

public delegate int test(int i);

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // <- code doesn't compile
}

Invoke메소드를 사용할 때 컴파일이 OK 이고 csharp Func<int,int>직접 리턴 할 때 OK가 아닌가?


일정이 있다는 의미의 대리인이 있습니다. 호출은 스레드 간 예외를 방지하고 여러 프로세스가 오브젝트에 액세스 할 수 있도록합니다.
jdweng

당신은 당신이 같은 두 개의 동일한-보이는 대표단 사용하는 경우에도이 문제를 볼 수 있습니다 delegate void test1(int i);delegate void test2(int i);
매튜 왓슨

답변:


27

이 동작을 이해하기 위해 알아야 할 두 가지가 있습니다.

  1. 모든 델리게이트는에서 파생 System.Delegate되지만 다른 델리게이트는 유형이 다르므로 서로를 할당 할 수 없습니다.
  2. C # 언어 는 메서드 나 람다를 대리자에게 할당하기위한 특수 처리 기능을 제공합니다 .

다른 델리게이트의 유형이 다르므로 한 유형의 델리게이트를 다른 유형에 지정할 수 없습니다.

예를 들면 다음과 같습니다.

delegate void test1(int i);
delegate void test2(int i);

그때:

test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a;                 // Using normal assignment, therefore does not compile.

위의 첫 번째 줄은 람다 또는 메서드를 대리자에 할당하기 위해 특수 처리를 사용하므로 OK를 컴파일합니다.

실제로이 줄은 컴파일러에 의해 다음과 같이 효과적으로 다시 작성됩니다.

test1 a = new test1(Console.WriteLine);

위의 두 번째 줄은 한 유형의 인스턴스를 다른 호환되지 않는 유형에 할당하려고하기 때문에 컴파일되지 않습니다.

지금까지 유형에 갈 때, 사이의 대입 호환성이없는 test1test2서로 다른 종류 때문에이.

생각하는 데 도움이된다면이 클래스 계층 구조를 고려하십시오.

class Base
{
}

class Test1 : Base
{
}

class Test2 : Base
{
}

다음 코드는 컴파일, 비록되지 않습니다 Test1Test2같은 기본 클래스에서 파생 :

Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.

이것은 한 델리게이트 유형을 다른 델리게이트 유형에 할당 할 수없는 이유를 설명합니다. 그것은 단지 일반적인 C # 언어입니다.

그러나 중요한 것은 왜 메서드 나 람다를 호환 가능한 대리자에게 할당 할 수 있는지 이해하는 것입니다. 위에서 언급했듯이 이는 델리게이트에 대한 C # 언어 지원의 일부입니다.

마지막으로 귀하의 질문에 대답하십시오.

사용할 때 Invoke()호환되지 않는 형식을 할당하지 않고 메서드 또는 람다를 대리자에 할당하기 위해 특수 C # 언어 처리를 사용하여 대리자에 METHOD 호출을 할당하므로 확인이 컴파일됩니다.

OP에서 컴파일하는 코드는 완전히 명확하게합니다.

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

실제로 개념적으로 다음과 같은 것으로 변환됩니다.

public test Success()
{
    Func<int, int> f = x => x;
    return new test(f.Invoke);
}

실패한 코드는 호환되지 않는 두 가지 유형 사이에 할당하려고 시도합니다.

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // Attempting to assign one delegate type to another: Fails
}

6

두 번째 경우 f는 유형 Func<int, int>이지만 메소드는를 반환한다고합니다 test. 이들은 서로 관련이없는 관련 (위임) 유형이므로 서로 컴파일러 오류가 발생합니다. 언어 사양 의이 섹션 으로 이동하여 "대리인"을 검색 할 수 있습니다 . 동일한 서명을 가진 델리게이트 간의 변환에 대한 언급은 없습니다.

그러나 전자의 경우, f.InvokeA는 메소드 그룹 식 실제로 입력이없는. C # 컴파일러는 메소드 그룹 변환을 통해 컨텍스트에 따라 메소드 그룹 표현식을 특정 델리게이트 유형으로 변환 합니다.

( 여기 에서 다섯 번째 글 머리표 인용, 내 것을 강조)

식은 다음 중 하나로 분류됩니다.

  • ...

  • 멤버 조회로 인한 오버로드 된 메소드 세트 인 메소드 그룹. [...] 메소드 그룹은 invocation_expression, delegate_creation_expression 및 is연산자 의 왼쪽으로 허용되며 암시 적으로 호환 가능한 대리자 유형으로 변환 될 수 있습니다.

이 경우 test대리자 유형으로 변환됩니다 .

즉, 이미 유형이 있지만 아직 유형이 return f없기 때문에 작동 하지 않습니다.ff.Invoke


2

여기서 문제는 유형 호환성입니다.

다음은 MSDN 소스 의 Func 델리게이트 정의입니다 .

public delegate TResult Func<in T, out TResult>(T arg);

위에서 언급 한 Func과 정의 된 델리게이트간에 직접적인 관계가없는 경우 :

public delegate int test(int i);

첫 번째 스 니펫이 컴파일되는 이유 :

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
 }

델리게이트는 입력 매개 변수 및 출력 결과 인 서명을 사용하여 비교되며, 궁극적으로 델리게이트는 함수 포인터이며 두 함수는 서명을 통해서만 비교할 수 있습니다. 런타임시 Func를 통해 호출 된 메소드는 Test대리자 에게 할당됩니다. Signature는 동일하므로 원활하게 작동합니다. 함수 포인터 할당입니다. Test델리게이트가 이제 Func 델리게이트가 가리키는 메소드를 호출합니다.

2 차 스 니펫이 컴파일되지 않는 이유

Func와 테스트 대리자 사이에는 형식 / 할당 호환성이 없으며 Func은 형식 시스템 규칙의 일부로 채울 수 없습니다. test delegate첫 번째 경우 와 같이 결과를 할당하고 채울 수있는 경우에도 마찬가지입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.