익명 메소드를 var에 할당 할 수없는 이유는 무엇입니까?


139

다음 코드가 있습니다.

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

그러나 다음은 컴파일되지 않습니다.

var comparer = delegate(string value) {
    return value != "0";
};

컴파일러가 왜 그것을 알 수 Func<string, bool>없습니까? 하나의 문자열 매개 변수를 사용하고 부울을 반환합니다. 대신 오류가 발생합니다.

암시 적으로 형식화 된 로컬 변수에 익명 메서드를 할당 할 수 없습니다.

나는 하나의 추측을 가지고 있으며 var 버전이 컴파일 된 경우 다음과 같은 경우 일관성이 부족합니다.

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

Func <>은 최대 4 개의 인수 (.NET 3.5에서는 사용중인 것) 만 허용하므로 위의 내용은 의미가 없습니다. 아마도 누군가가 문제를 분명히 할 수있을 것입니다. 감사.


3
.NET 4에서 4 개의 인수 인수 에 대해 Func<>최대 16 개의 인수를 허용합니다.
Anthony Pegram

설명해 주셔서 감사합니다. .NET 3.5를 사용하고 있습니다.
Marlon

9
컴파일러가 왜 그렇게 생각 Func<string, bool>할까요? 그것은 Converter<string, bool>나에게 보인다 !
벤 Voigt


3
때때로 나는 VB를 그리워한다 ..Dim comparer = Function(value$) value <> "0"
Slai

답변:


155

다른 사람들은 이미 의미 할 있는 가능한 많은 델리게이트 유형이 있다고 지적했습니다. 너무 특별 Func해서 Predicate또는 대신에 기본값이 될 가치가있는 것Action 또는 다른 가능성은? 그리고 람다의 경우 표현 트리 형식이 아닌 대리자 형식을 선택하려는 의도가 분명한 이유는 무엇입니까?

그러나 우리는 이것이 Func특별 하다고 말할 수 있으며 유추 된 람다 또는 익명의 방법은 Func의 무언가 라고 말할 수 있습니다. 우리는 여전히 모든 종류의 문제가 있습니다. 다음과 같은 경우 어떤 유형의 유추를 원하십니까?

var x1 = (ref int y)=>123;

Func<T>아무 것도 참조하는 유형 이 없습니다 .

var x2 = y=>123;

리턴 값은 알고 있지만 형식 매개 변수의 유형은 모릅니다. (또는 우리는? 반환은 int? long? short? 바이트입니까?)

var x3 = (int y)=>null;

반품 유형은 알 수 없지만 무효화 할 수는 없습니다. 리턴 유형은 모든 참조 유형 또는 널 입력 가능 값 유형일 수 있습니다.

var x4 = (int y)=>{ throw new Exception(); }

다시, 우리는 리턴 타입을 모른다. 그리고 이번에는 그것이 무효 있다.

var x5 = (int y)=> q += y;

그것은 void-returning statement lambda 또는 q에 할당 된 값을 반환하는 것입니까? 둘 다 합법적입니다. 우리는 어느 것을 선택해야합니까?

자, 당신은 그 기능들을 지원하지 않는다고 말할 것입니다. 유형을 해결할 수있는 "일반적인"경우 만 지원하십시오. 도움이되지 않습니다. 어떻게하면 내 인생이 더 쉬워 집니까? 이 기능이 가끔 작동하고 가끔 실패하는 경우에도 이러한 모든 실패 상황 을 감지 하고 각각에 대해 의미있는 오류 메시지표시 하는 코드를 작성해야합니다 . 우리는 여전히 모든 행동을 명시하고, 문서화하고, 테스트를 작성해야합니다. 이것은 매우 비싼 기능입니다 은 사용자가 약 6 번의 키 입력을 줄일 수 입니다. 우리는 절반의 시간 동안 작동하지 않고 작동하는 경우 거의 이점을 제공하지 않는 기능에 대해 테스트 케이스를 작성하는 데 많은 시간을 소비하는 것보다 언어에 가치를 더하는 더 좋은 방법이 있습니다.

실제로 유용한 상황은 다음과 같습니다.

var xAnon = (int y)=>new { Y = y };

해당 "말할 수있는"유형이 없기 때문입니다. 그러나 우리는 항상이 문제가 있으며 유형을 추론하기 위해 메소드 유형 유추를 사용합니다.

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

그리고 이제 메소드 타입 추론은 func 타입이 무엇인지 알아냅니다.


43
언제 SO 답변을 책으로 편집 할 예정입니까? 나는 :)을 구입할 수
매트 그리어

13
나는 Eric Lippert의 SO 답변에 대한 제안을 두 번째로한다. 추천 제목 : "스택에서 반사"
Adam Rackis

24
@ 에릭 : 좋은 대답이지만 , 실제로 D에서 완벽하게 작동하기 때문에 불가능한 것으로 이것을 설명하는 것은 약간 오도합니다. 그들의 맥락에서 ... 그래서 IMHO의 대답은 "그것이 우리가 그렇게했기 때문에"무엇보다 중요합니다. :)
user541686

5
@abstractdissonance 또한 컴파일러는 오픈 소스입니다. 이 기능에 관심이 있다면 필요한 시간과 노력을 기부 할 수 있습니다. 풀 요청을 제출하는 것이 좋습니다.
Eric Lippert

7
@AbstractDissonance : 개발자와 시간이라는 제한된 리소스 측면에서 비용을 측정했습니다. 이 책임은 하나님에 의해 부여되지 않았습니다. 그것은 개발자 부서의 부사장에 의해 부과되었습니다. 어떻게 든 C # 팀이 예산 프로세스를 무시할 수 있다는 생각은 이상한 것입니다. C # 커뮤니티에 소원을 표명 한 전문가, Microsoft의 전략적 사명, 디자인의 뛰어난 취향을 염두에 둔 전문가를 신중하고 신중하게 고려하여 트레이드 오프가 이루어 졌음을 여전히 확신합니다.
Eric Lippert

29

Eric Lippert만이 확실하게 알고 있지만 위임 유형의 서명이 유형을 고유하게 결정하지 않기 때문이라고 생각합니다.

귀하의 예를 고려하십시오.

var comparer = delegate(string value) { return value != "0"; };

여기에 대한 두 가지 가능한 추론이 있습니다. var 이 있습니다.

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

컴파일러는 어느 것을 추론해야합니까? 둘 중 하나를 선택해야 할 이유가 없습니다. 그리고 a Predicate<T>는 기능적으로 a와 동일 하지만 Func<T, bool>.NET 유형 시스템 수준에서 여전히 다른 유형입니다. 따라서 컴파일러는 대리자 형식을 명확하게 확인할 수 없으므로 형식 유추에 실패해야합니다.


1
Microsoft의 다른 사람들도 확실히 알고 있습니다. ;) 그러나 네, 주된 이유는 컴파일 시간 유형이 없기 때문에 결정할 수 없다는 것입니다. 언어 사양의 섹션 8.5.1에서는 익명 함수가 암시 적으로 형식화 된 변수 선언에 사용되지 않는 이유를 강조합니다.
Anthony Pegram

3
네. 더 나쁜 것은 람다의 경우 델리게이트 유형인지 여부조차 알 수 없다는 것입니다. 표현식 트리 일 수 있습니다.
Eric Lippert


컴파일러가 람다 함수에 대해 C ++처럼 새로운 고유 한 유형을 제작할 수없는 이유
Weipeng L

".NET 유형 시스템 수준에서"어떻게 다른가요?
arao6 '12

6

에릭 Lippert의 오래된이 그는 말한다 곳에 약을

실제로 C # 2.0 사양에서는이를 호출합니다. 메소드 그룹 표현식 및 익명 메소드 표현식은 C # 2.0에서 유형이없는 표현식이며 람다 표현식은 C # 3.0에서이를 결합합니다. 그러므로 그들이 암묵적 선언의 오른쪽에 "나체"로 나타나는 것은 불법입니다.


그리고 이것은 언어 사양의 섹션 8.5.1에 ​​의해 강조됩니다. 암시 적으로 형식이 지정된 로컬 변수에 사용하려면 "초기화 식에 컴파일 타임 형식이 있어야합니다."
Anthony Pegram

5

다른 대표는 다른 유형으로 간주됩니다. 예를 들어, Action와 다르고 MethodInvoker의 인스턴스 Action를 유형의 변수에 할당 할 수 없습니다 MethodInvoker.

그래서, 익명의 대표 (또는 람다) 등의 주어진 () => {}, 그것에게 인 Action또는를 MethodInvoker? 컴파일러가 말할 수 없습니다.

마찬가지로, string인수를 받아서를 반환 하는 대리자 형식을 선언 bool하면 컴파일러 Func<string, bool>가 내 대리자 형식 대신 실제로 원하는 것을 어떻게 알 수 있습니까? 델리게이트 유형을 유추 할 수 없습니다.


2

다음은 암시 적으로 형식이 지정된 로컬 변수에 관한 MSDN의 요점입니다.

  1. var는 로컬 변수가 동일한 명령문에서 선언되고 초기화 될 때만 사용할 수 있습니다. 변수는 널 또는 메소드 그룹 또는 익명 함수로 초기화 될 수 없습니다.
  2. var 키워드는 컴파일러에게 초기화 명령문의 오른쪽에있는 표현식에서 변수 유형을 유추하도록 지시합니다.
  3. var 키워드는 "variant"를 의미하지 않으며 변수가 느슨하게 입력되었거나 늦게 묶여 있음을 나타내지 않습니다. 단지 컴파일러가 가장 적합한 유형을 결정하고 할당한다는 의미입니다.

MSDN 참조 : 암시 적으로 형식이 지정된 로컬 변수

익명 방법에 대해 다음을 고려하십시오.

  1. 익명 메소드를 사용하면 매개 변수 목록을 생략 할 수 있습니다.

MSDN 참조 : 익명 방법

익명 메소드에는 실제로 다른 메소드 서명이있을 수 있으므로 컴파일러는 할당 할 가장 적합한 유형을 올바르게 추론 할 수 없습니다.


1

내 게시물은 실제 질문에 대답하지 않지만 다음과 같은 근본적인 질문에 대답합니다.

"어떻게 애매 모호한 유형을 입력하지 않아도 Func<string, string, int, CustomInputType, bool, ReturnType>되는가?"[1]

내가 게으른 / 해킹 프로그래머이기 때문에 나는 Func<dynamic, object> -단일 입력 매개 변수를 사용하고 객체를 반환하는-를 .

여러 인수에 대해 다음과 같이 사용할 수 있습니다.

dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
    return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));

팁 : 사용할 수 있습니다 Action<dynamic> 객체를 반환 할 필요가없는 경우 .

네, 아마도 프로그래밍 원칙에 위배된다는 것을 알고 있습니다.

나는 대의원에서 초심자입니다 ... 내가 배운 것을 나누고 싶었습니다.


[1] 이것은 미리 정의 된 Func매개 변수 를 요구하는 메서드를 호출하지 않는 것으로 가정합니다 . 이 경우 해당 문자열을 입력해야합니다.


0

어때요?

var item = new
    {
        toolisn = 100,
        LangId = "ENG",
        toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId)
        {
              var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html";
              return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : "";
        }
};

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