2013-04-02 7 views
16

params 수정 자 (배열 유형의 매개 변수 중 하나를 소위 "매개 변수 배열"로 변환 함)가 메소드 서명의 일부가 아니라는 것을 알고 있습니다. 이제 다음 예제를 고려하십시오.메서드 오버라이드에서 params 수정 자 변경

class Giraffid 
{ 
    public virtual void Eat(int[] leaves) 
    { 
     Console.WriteLine("G"); 
    } 
} 
class Okapi : Giraffid 
{ 
    public override void Eat(params int[] leaves) 
    { 
     Console.WriteLine("O"); 
    } 
} 

경고없이 컴파일됩니다. 그런 말 :

var okapi = new Okapi(); 
okapi.Eat(2, 4, 6); // will not compile! 

오류 (No overload for method 'Eat' takes 3 arguments)를 제공합니다.

컴파일러가 params 수정자를 해당 매개 변수에 System.ParamArrayAttribute의 응용 프로그램으로 변환한다는 것을 알고 있습니다. 일반적으로 가상 메소드의 매개 변수에 하나의 속성 콜렉션을 적용한 다음 파생 클래스의 대체 메소드에서 "해당"매개 변수를 다른 속성 세트로 데코 레이팅 할 때 아무런 문제가 없습니다.

그러나 컴파일러는 내 params 키워드를 자동으로 무시하도록 선택합니다. 반대로, 반대로하면 기본 클래스 Giraffid의 매개 변수에 params을 적용한 다음 Okapi의 재정의 키워드를 생략하면 컴파일러에서 메서드를 모두 System.ParamArrayAttribute으로 꾸미기로 선택합니다. 나는 IL DASM으로 이러한 것들을 확인했다.

내 질문 :

이 문서화 된 행동인가? 나는 C# 언어 명세를 철저히 수색하지 않고 이것에 대해 언급했다.

적어도 Visual Studio 개발 환경은 이것에 대해 혼란 스러울 수 있습니다. 위 메서드 호출에서 2, 4, 6을 입력 할 때 인텔리 센스는 팁void Okapi.Eat(params int[] leaves)을 표시합니다. 을 비교 한 내용


는, I는 인터페이스 방법을 구현 및 인터페이스 params의 존재/부재를 변경하고 클래스를 구현했는데, I는 어느 델리게이트 타입 정의의 델리게이트 타입을 정의하고 params 변경 여부를 시도하거나 메서드 그룹에 내 대리자 형식의 변수를 할당 한 메서드 이 경우에는 params -ness를 완벽하게 변경하는 것이 가능했습니다.

답변

16

컴파일러의 동작은 정확하지만 이는 다소 엉망입니다. 나는 이것이 적어도 경고가되는 것을 선호했을 것이다.

스펙에서 어디에서 이것이 옳은지 찾을 수 없다는 사실은 놀랄 일이 아닙니다. 관련 비트는 다음과 같습니다.

M (A) 형식의 메소드 호출 바인딩 시간 처리 (여기서 M은 메소드 그룹이고 A는 선택적 인수 목록)는 다음 단계로 구성됩니다. 메소드 호출에 대한 후보 메소드 세트가 구성됩니다. 메소드 그룹 M과 관련된 각 메소드 F에 대해 F가 비 제네릭 인 경우 M은 유형 인수 목록이없고 F는 A와 관련하여 적용 할 수 있습니다.

"메소드 방법 그룹 M "과 관련되어 있습니까? 우선, 방법 그룹은 무엇입니까?

부재 조회로 인한 오버 방법의 집합 인 방법 기,

...

OK 있으므로 부재 조회 규칙은 무엇인가?

그렇지 않으면, 세트는 상속 된 멤버와 목적에 N이라는 이름의 접근 회원을 포함하여 T에 N라는 이름의 액세스 가능한 모든 회원들로 구성되어 있습니다. 재정의 수정자를 포함하는 구성원은 집합에서 제외됩니다.

강조가 추가되었습니다. 여기

실제적인 결말은 원래을 무시 를 아니었다 방법,을 선언 오버로드 확인하기위한 목적으로 는, 오버라이드 방법은 했다 방법이어야한다는 것이다.이 규칙은, 불행하게도,이 경우 위반 :

virtual void M(int x, int y) { } 
... 
override void M(int y, int x) { } 
... 
M(x = 1, y = 2); 

과부하 해상도가 더 파생 버전의 이름을을 사용합니다. 이것은 게임에서 이름 지어 졌던 인수가 아주 늦게 추가되었다는 사실의 불행한 결과입니다.

요컨대

은 : 방법은 "PARAMS"인지의 여부를 결정하기 위해, 분석하지 오버라이드 방법에서 원래 방법에서 수행된다.

컴파일러에서 경고를 주면 좋았을 것입니다.

즉 적어도 Visual Studio 개발 환경은 수정이

에 대한 혼란 말할 수있다. IntelliSense 레이어에는 항상 재정의 된 메서드가 아닌 메서드의 메서드 정보가 표시됩니다. 연구 결과에 따르면 사용자가 메서드가 원래의 선언 메서드 인 것처럼 보이게 할 때 혼란 스럽다는 것을 알았습니다. 물론 앞에서 언급했듯이 이름있는 인수에 사용할 매개 변수 이름입니다.

+2

이것도 인터페이스가 작동하는 이유에 대한 답변입니다 (http://stackoverflow.com/questions/27843804/compiling-generic-interface-vs-generic-abstract-class-params-keyword의 중복 코드 샘플) - "override"가 없으므로,'params' *를 사용하는 메소드는 가능한 일치 목록에서 제외되지 않습니다. –

5

가 나는 C#을 사양의 1.6.6.4 항에 기술 된 것 같아요 :

가상 메서드는 파생 클래스에서 재정의 할 수 있습니다. 인스턴스 메서드 선언에 override 한정자가 포함되어 있으면 메서드는 동일한 서명이있는 상속 된 가상 메서드를 재정의합니다. 가상 메소드 선언은 새로운 메소드를 도입하지만, 메소드 선언은 에 의해 기존 상속 가상 메소드를 특화하여 해당 메소드의 새 구현을 제공합니다.

그에 따르면, 여기서는 virtual 메소드 선언이 매우 중요합니다. 그리고 그 메소드에 대한 모든 호출에 virtual 메소드 선언이 사용됩니다.올바른 override n 개의 구현 (지정된 경우)은 런타임에 취해집니다. 여기서 params은 전혀 관련이 없습니다.

는 그것은 간단한 테스트로 확인 할 수 있습니다

var o = new Okapi(); 
o.Eat(1, 2, 3); 

100 % 잘 작동하는 선언으로

class Giraffid 
{ 
    public virtual void Eat(params int[] leaves) 
    { 
     Console.WriteLine("G"); 
    } 
} 
class Okapi : Giraffid 
{ 
    public override void Eat(int[] leaves) 
    { 
     Console.WriteLine("O"); 
    } 
} 

.