확장 클래스 내에서 확장 메서드를 호출하는 데 'this'키워드가 필요한 이유


79

ASP.NET MVC ViewPage에 대한 확장 메서드를 만들었습니다. 예 :

public static class ViewExtensions
{
    public static string Method<T>(this ViewPage<T> page) where T : class
    {
        return "something";
    }
}

View에서이 메서드를 호출 할 때 (에서 파생 됨 ViewPage) 키워드를 사용하여 호출 하지 않으면 " CS0103 : The name 'Method'is not exist in the current context " 오류가 발생 this합니다.

<%: Method() %> <!-- gives error CS0103 -->
<%: this.Method() %> <!-- works -->

this키워드가 필요한 이유는 무엇 입니까? 아니면 그것 없이도 작동하지만 뭔가 빠졌나요?

(이 질문의 중복이있는 것 같지만 찾을 수 없었습니다)

업데이트 :

벤 로빈슨은 말한다 , 전화 번호 방법에 대한 구문은 컴파일러 설탕이다. 그렇다면 컴파일러가 this 키워드를 요구하지 않고 현재 유형의 기본 유형의 확장 메서드를 자동으로 확인할 수없는 이유는 무엇입니까?


@ M4N-이것은 당신이 추구했던 중복 가능성처럼 보입니다 : List를 확장하는 클래스에서 OrderBy를 호출 할 수없는 이유는 무엇입니까?
djdd87 2010-08-18

@GenericTypeTea : 네, 비슷한 질문입니다. 그러나 확장 메서드를 호출하는 방법을 묻는 것이 아니라 ( 키워드 를 추가해야한다는 것을 알고 있음this ) this키워드가 필요한 이유를 묻습니다 . 내 질문을 업데이트했습니다.
M4N

@ M4N-좋은 지적, 그들은 그것을해야한다고 말하지만 대답은 실제로 이유에 대한 충분한 설명을 제공하지 않습니다.
djdd87 2010-08-18

인스턴스 메서드에서 'this'는 암시 적으로 메서드에 전달됩니다. this.Method () 또는 Method (this) 대신 Method ()를 호출하면 컴파일러에게 메서드에 전달할 내용을 알려주지 않습니다.
Alex Humphrey

@Alex. 기술적으로는 컴파일러가 Method ()를 호출하는 맥락에서 "this"를 추론 할 수 있다고 생각하지만 이것이 잘못된 디자인 선택이었고 코드를 읽기 어렵게 만들 것이라고 생각합니다.
Ben Robinson

답변:


55

몇 가지 요점 :

첫째, 제안 된 기능 (확장 메서드 호출에 대한 암시 적 "this.")이 필요하지 않습니다 . LINQ 쿼리 이해가 우리가 원하는 방식으로 작동하려면 확장 메서드가 필요했습니다. 수신자는 항상 쿼리에 명시되므로 LINQ가 작동하도록하기 위해 암시 적으로이를 지원할 필요는 없습니다.

둘째,이 기능 은 확장 메서드의보다 일반적인 디자인에 대해 작동 합니다. 즉, 확장 메서드를 사용하면 인터페이스이고 구현을 알지 못하기 때문에 확장 할 수없는 유형을 확장 할 수 있습니다. 구현을 알고 있지만 소스 코드가 없습니다.

해당 유형 내의 유형에 대해 확장 메소드를 사용하는 시나리오에있는 경우 소스 코드에 액세스 할 있습니다. 그렇다면 처음에 확장 방법을 사용하는 이유는 무엇입니까? 확장 된 유형의 소스 코드에 대한 액세스 권한이 있으면 인스턴스 메소드를 직접 작성할 수 있으며 확장 메소드를 전혀 사용할 필요가 없습니다! 그러면 구현에서 확장 메서드가 할 수없는 개체의 개인 상태에 액세스 할 수 있습니다.

액세스 권한이있는 유형 내에서 확장 메서드를 사용하기 쉽게 만드는 것은 인스턴스 메서드보다 확장 메서드를 사용하도록 장려하는 것입니다. 확장 메서드는 훌륭하지만 일반적으로 인스턴스 메서드가있는 경우 사용하는 것이 좋습니다.

이 두 가지 점을 감안할 때 더 이상 기능이 존재 하지 않는 이유를 설명하기 위해 언어 디자이너가 부담하지 않습니다 . 이제 이유를 설명 할 일인 해야한다 . 기능에는 이와 관련된 막대한 비용이 있습니다. 이 기능은 필요하지 않으며 확장 방법의 명시된 설계 목표에 위배됩니다. 구현 비용을 부담해야하는 이유는 무엇입니까? 이 기능을 통해 어떤 매력적이고 중요한 시나리오를 사용할 수 있는지 설명하고 향후 구현을 고려할 것입니다. 나는 그것을 정당화하는 설득력 있고 중요한 시나리오를 보지 못했지만 아마도 내가 놓친 시나리오가있을 것입니다.


11
답변 해 주셔서 감사합니다! 두 가지 요점 : 1 : 나는 그 기능이 구현 될 것을 요구하지 않았습니다. 단지 설명을 위해 그것이없는 이유 / this필요한 이유 였습니다. 2 : 확장 유형에서 확장 메소드를 호출하는 이유는 무엇입니까? 주어진 예제에서는 기본 클래스 (ViewPage)에 몇 가지 편리한 메서드를 추가하고 파생 클래스에서 호출합니다. 이렇게하면 모든 뷰에 대해 공통 기본 클래스를 만들 필요가 없습니다. BTW : 확장 유형 내에서 확장 방법을 사용하는 것이 어색하다는 데 동의하지만 MVC ViewPage의 예제를 다시 참조하십시오.
M4N

3
@ M4N : 그게 바로 제 상황입니다. UserControl 메서드를 확장하고 명시적인 'this'없이 모든 UserControl에서 해당 확장을 사용할 수 있도록하고 싶습니다. 나는 그 컴파일러 개발은 민주주의가 아니라는 것을 알고 있지만, '이':-) 암시이 내 투표를 고려
던컨 Bayne

8
안녕하세요 Eric, 이것이 주요 시나리오가 아니라는 것을 알고 있습니다. 그러나 일반적인 패턴은 1 인 것 같습니다. 기본 클래스에 대한 소스 편집 액세스 권한이 없습니다. 2. 파생 클래스의 본문 내에서 호출하려는 일부 (보호 된) 메서드를 기본 형식에 추가하려고합니다. 이를 달성하기위한 다른 메커니즘이 있습니까? this.MethodName 열심히 입력하는 것이 아니지만 ... 잠시 후 귀찮을
Gishu

5
소스가없는 기본 클래스에 확장 메서드를 추가하는 시나리오는 유효한 것이라고 생각합니다. 이 경우 "this"없이 메서드를 호출 할 수 있으면 좋았을 것입니다. BTW 이것은 "정적 가져 오기"를 사용하여 다음 C # 버전에서 해결할 수 있습니다.
lysergic-acid-

4
확장하는 클래스 내에서 확장 메서드를 사용하는 유효한 이유 중 하나는 인터페이스를 구현하는 여러 유형이있는 경우입니다. 예 : 당신은 구현하는 두 개의 클래스를 가지고 ICrossProductable, 당신은 전화 해당 인터페이스에 대한 확장 메서드를 정의합니다 GetProduct. 매우 간단한 예이지만 종종 유용하다고 생각합니다. 하지만 모든 경우에 최선의 방법은 아닙니다.
Ian Newson

9

이것이 없으면 컴파일러는 페이지를 첫 번째 매개 변수로 사용하는 정적 클래스의 정적 메서드로 간주합니다. 즉

// without 'this'
string s = ViewExtensions.Method(page);

// with 'this'
string s = page.Method();

1
나는 그것이 ViewExtensions.Method (page)라고 생각합니다.
Dave Van den Eynde

6

인스턴스 메서드에서 'this'는 암시 적으로 각 메서드에 투명하게 전달되므로 제공하는 모든 멤버에 액세스 할 수 있습니다.

확장 메서드는 정적입니다. 또는 Method()대신 호출 하면 컴파일러에게 메서드에 전달할 내용을 알리지 않습니다.this.Method()Method(this)

당신은 '왜 호출하는 객체가 무엇인지 깨닫고 그것을 매개 변수로 전달하지 않는가?'라고 말할 수 있습니다.

대답은 확장 메서드가 정적이며 'this'가없는 정적 컨텍스트에서 호출 할 수 있다는 것입니다.

나는 그들이 컴파일하는 동안 그것을 확인할 수 있다고 생각하지만, 솔직히 말해서, 그것은 아마도 극히 적은 보상으로 많은 작업 일 것입니다. 솔직히 말해서 확장 메서드 호출의 명시 성을 제거하는 데 따른 이점은 거의 없습니다. 인스턴스 메서드로 착각 할 수 있다는 사실은 때때로 매우 직관적이지 않을 수 있음을 의미합니다 (예를 들어 NullReferenceExceptions는 throw되지 않음). 나는 때때로 그들이 확장 메소드를위한 새로운 '파이프 포워드'스타일 연산자를 도입 했어야한다고 생각합니다.


그래서 정적 인 컨텍스트에서 호출 할 수 있도록 설계를 선택하는 것은 정말 나쁜 것 같습니다. 왜 그런지 모르겠습니다.
AustinWBryan 2008 년

3

확장 방법과 일반 방법간에 차이있다는 점에 유의하는 것이 중요 합니다. 나는 당신이 그들 중 하나를 방금 본 것 같습니다.

다른 차이점의 예를 들어 보겠습니다 null. 객체 참조 에서 확장 메서드를 호출하는 것은 매우 쉽습니다 . 다행히도 이것은 일반적인 방법으로 수행하는 것이 훨씬 더 어렵습니다. (하지만 할 수 있습니다. IIRC, Jon Skeet은 CIL 코드를 조작하여이를 수행하는 방법을 보여주었습니다.)

static void ExtensionMethod(this object obj) { ... }

object nullObj = null;
nullObj.ExtensionMethod();  // will succeed without a NullReferenceException!

this, 확장 메서드를 호출하는 데 필요한 것이 조금 비논리적으로 보인다는 데 동의합니다 . 결국 확장 방법은 이상적으로는 "느낌"을 가져야하며 일반적인 방법과 똑같이 작동해야합니다.

그러나 실제로 확장 방법은 모든 측면에서 언어에 잘 맞는 초기 핵심 기능보다 기존 언어 위에 추가 된 구문 설탕과 비슷합니다.


Boo 언어에서는 null 즉, null.Do ()에서 Extension 메서드 또는 속성을 직접 호출 할 수 있습니다.
Sergey Mirvoda

1
@Sergey : 위험한 것 같네요 ... :) Boo가 (묵시적인) 유형을 null어떻게 알고 있는지 궁금합니다 . 모든 참조 유형이 될 수 있습니다. 어떤 방법을 사용할 수 있는지 어떻게 알 수 null있습니까?
stakx-더 이상

1
널 참조로 확장 메소드를 호출 할 수있는 좋은 이유가있을 수 있습니다.
Dave Van den Eynde

@DaveVandenEynde : IMHO, 컴파일러가 비가 상 호출로 호출해야하는 비가 상 메서드에 대한 속성을 정의하지 않는 것은 .net의 실수였습니다. String논리적으로 값으로 동작하는 일부 불변 클래스 유형 은 값으로 작동하며 null 일 때 합리적인 기본값을 가질 수 있습니다 (예 : .net은 string산발적으로 만 수행하는 대신 정적 유형의 널 참조를 일관되게 빈 문자열로 간주 할 수 있음 ). 같은 일에 정적 메서드를 사용하는 if (!String.IsNullOrEmpty(stringVar))것은 단순히 끔찍합니다.
supercat

1
@supercat 예, 끔찍합니다. string.IsNullOrEmpty ()를 수행 할 수도 있습니다. 훨씬 예뻐요.
Dave Van den Eynde 2012

1

ViewPage 클래스에는 확장 메서드가 없기 때문입니다. 확장 메서드를 호출하는 내용을 컴파일러에 알려야합니다. .NET this.Method()용 컴파일러 설탕을 기억하십시오 ViewExtensions.Method(this). 메서드 이름으로 클래스 중간 내에서 확장 메서드를 호출 할 수없는 것과 같은 방식입니다.


1
이것은 의미가 있습니다 (어쨌든). 그러나 컴파일러 설탕이라면 왜 컴파일러가 this키워드 없이 확장 메서드를 확인할 수 없습니까?
M4N

나에게는 감독처럼 보입니다. 말했듯이 컴파일러는 메서드가 존재하지 않음을 확인한 다음 확장의 가용성을 확인할 수 있습니다.
TomTom

네, 틀림없이 이것이 효과가있을 수 있습니다. 코드를 더 읽기 쉽게 만드는 것이 특정 디자인 결정이라고 생각합니다. 클래스 내에서 ExtensionMethod ()를 호출 할 수 있다면 해당 클래스에 해당 메서드가 포함되어 있지 않지만 this.ExtensionMethod ()를 사용하는 경우 코드를 읽는 사람에게 약간 혼란 스러울 수 있습니다. 적어도 MyInstance.ExtentionMethod () 사용과 일치합니다.
Ben Robinson

this.Method ()는 ViewExtensions.Method (this)의 구문 설탕과 더 비슷하다고 생각합니다.
Alex Humphrey

1

유창한 API를 작업 중이며 동일한 문제가 발생했습니다. 확장중인 클래스에 액세스 할 수 있지만 각 유창한 메서드의 논리가 자체 파일에 있기를 원했습니다. "this"키워드는 매우 직관적이지 않았고 사용자는 계속해서 방법이 없다고 생각했습니다. 내가 한 일은 내 클래스를 확장 메서드를 사용하는 대신 필요한 메서드를 구현하는 부분 클래스로 만드는 것입니다. 나는 답변에서 부분적인 언급을 보지 못했습니다. 이 질문이 있으면 부분이 더 나은 옵션이 될 수 있습니다.

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