2010-08-18 105 views
70

我已經創建了一個擴展方法爲ASP.NET MVC的ViewPage中調用擴展方法所需要的「這個」關鍵字,例如:爲什麼是從擴展的類

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

當調用從該方法視圖(從ViewPage派生),我得到的錯誤「CS0103:名稱爲‘方法’不存在於當前上下文存在」除非我用this關鍵字來調用它:

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

爲什麼this需要關鍵字?還是沒有它的工作,但我錯過了什麼?

(我認爲必須有這個問題的一個副本,但我沒能找到一個)

更新

由於Ben Robinson says的語法來調用擴展方法只是編譯糖。那麼爲什麼編譯器不能自動檢查當前類型的基類型的擴展方法,而不需要this關鍵字呢?

+0

@ M4N - 這看起來像你之後可能的重複:[爲什麼我不能在擴展List的類中調用OrderBy?](http://stackoverflow.com/questions/2144034/why-canti-i -all-order-in-a-class-that-extends-list) – GenericTypeTea 2010-08-18 10:23:23

+0

@GenericTypeTea:是的,這是一個類似的問題。但我不問如何調用擴展方法(我*知道*我必須添加'this'關鍵字),但我問爲什麼需要'this'關鍵字。我更新了我的問題。 – M4N 2010-08-18 10:29:56

+0

@ M4N - 好點,他們說必須完成,但答案並沒有給出足夠的解釋。 – GenericTypeTea 2010-08-18 10:41:57

回答

45

幾個要點:

首先,所提出的特徵(隱式「這個」上擴展方法調用)是不必要。擴展方法對於LINQ查詢解析以我們想要的方式工作是必需的;接收者總是在查詢中聲明,所以沒有必要支持隱式這使得LINQ工作。

其次,功能的工作的擴展方法更一般的設計:即,擴展方法允許你擴展類型,你不能擴展自己,可能是因爲它是一個接口,而你沒有知道實現,或者因爲你知道實現,但沒有源代碼。

如果你在場景中你使用的類型該類型內的擴展方法,那麼你訪問源代碼。 爲什麼你首先使用擴展方法呢?如果您有權訪問擴展類型的源代碼,則可以自己編寫實例方法,然後根本不必使用擴展方法!然後,您的實現可以利用訪問對象的私有狀態的優勢,哪些擴展方法不能。

從您有權訪問的類型中更容易使用擴展方法,鼓勵使用擴展方法而不是實例方法。擴展方法很好,但如果你有一個實例方法,通常更好。

鑑於這兩點,語言設計者不再擔心這個問題,以解釋爲什麼不是存在。現在就來解釋你爲什麼應該是。功能與他們相關的成本很高。此功能不是必需的,並且與擴展方法的既定設計目標相悖;爲什麼我們應該承擔實施它的成本?解釋這個功能啓用了什麼引人注目的重要場景,我們將在未來考慮實施它。我沒有看到任何有說服力的重要情況,但可能有一個我錯過了。

+9

感謝您的答案!兩點: 1:我並沒有要求實現該功能,只是爲了解釋,爲什麼它不存在/爲什麼'this'是必需的。 2:爲什麼我要從擴展類型中調用擴展方法?在給出的例子中,我將一些便捷方法添加到基類(ViewPage)中,並從派生類中調用它。這消除了爲我的所有視圖創建通用基類的需要。 順便說一句:我同意在擴展類型中使用擴展方法很尷尬,但再次看到MVC ViewPage的例子。 – M4N 2010-08-18 15:25:37

+0

我不認爲LINQ需要擴展方法的理由是一個很好的理由,因爲它不是現在它們存在的唯一原因。這並不妨礙我同意其餘的答案,即隱含的這是不必要的。我可以看到一個隱式的用法(實現一個擴展方法已經存在的接口或派生類),但即使在這裏,擴展方法「從外部工作」也反映出IMO是一件好事。 – 2010-09-24 14:45:27

+3

@ M4N:這也正是我的情況:我想擴展UserControl方法並將該擴展提供給所有我的UserControl,而不需要顯式的'this'。我知道編譯器開發不是民主,但考慮到我的投票暗含'這個':-) – 2011-01-21 02:40:24

7

如果沒有它,編譯器會將其視爲靜態類中的靜態方法,該靜態類將頁作爲第一個參數。即

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

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

我認爲這應該是ViewExtensions.Method(page)。 – 2010-08-18 11:16:19

1

由於擴展方法不與的ViewPage類存在。你需要告訴編譯器你在調用擴展方法。記住this.Method()只是ViewExtensions.Method(this)的編譯器糖。這與您不能通過方法名稱在任何類中間調用擴展方法的方式相同。

+1

這是有道理的(不知何故)。但是如果它是編譯器糖,那麼爲什麼編譯器不檢查沒有'this'關鍵字的擴展方法? – M4N 2010-08-18 10:23:08

+0

看起來像是對我的疏忽。正如你所說 - 編譯器可以看到該方法不存在,然後檢查擴展的可用性。 – TomTom 2010-08-18 10:26:35

+0

可以說這可以工作。我懷疑這是一個使代碼更具可讀性的特定設計決策。如果你可以簡單地在一個類中調用ExtensionMethod(),那麼如果這個類不包含那個方法,但是使用this.ExtensionMethod()至少和使用MyInstance.ExtentionMethod()是一致的, – 2010-08-18 10:48:00

3

重要的是要注意,擴展方法和常規方法之間存在差異。我想你剛剛遇到過其中一個。

我給你另一個區別的例子:在null對象引用上調用擴展方法相當簡單。幸運的是,這對於常規方法來說要困難得多。 (但它可以做到的。IIRC,喬恩斯基特演示瞭如何通過操縱CIL代碼來做到這一點。)

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

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

話雖這麼說,我同意這似乎有點unlogical是this需要調用擴展方法。畢竟,一個擴展方法應該理想地「感覺」並且表現得像普通的一樣。

但實際上,擴展方法更像是在現有語言之上添加語法糖,而不是在早期核心功能上很好地融入語言的各個方面。

+0

在Boo語言中,您可以直接在null上調用Extension方法或屬性,即null.Do()。 – 2010-08-18 10:28:44

+1

@Sergey:聽起來很危險...... :)讓我好奇Boo如何知道'null'(隱含)類型?可能只是關於任何引用類型,它如何知道'null'上有哪些方法可用? – stakx 2010-08-18 10:31:04

+1

可能有一個很好的理由能夠使用空引用來調用擴展方法。 – 2010-08-18 12:55:48

6

在實例方法中,'this'以透明方式隱式傳遞給每個方法,因此您可以訪問它提供的所有成員。

擴展方法是靜態的。通過調用Method()而不是this.Method()Method(this),您不會告訴編譯器傳遞給該方法的內容。

你可能會說'爲什麼它不知道調用對象是什麼,並將其作爲參數傳遞?

答案是擴展方法是靜態的,可以從靜態上下文中調用,沒有'this'。

我想他們可以在編譯期間檢查一下,但說實話,這可能是很多工作的極少回報。說實話,我沒有看到一些明確的擴展方法調用的好處。事實上,它們可能被誤認爲是方法,這意味着它們有時可能非常不直觀(例如,NullReferenceExceptions不會被拋出)。我有時會認爲他們應該爲擴展方法引入一個新的「管道式」運算符。

0

我正在研究流暢的API並遇到同樣的問題。即使我有權訪問我正在擴展的類,我仍然希望每個流利方法的邏輯都在它們自己的文件中。 「this」這個關鍵詞非常不直觀,用戶一直在想這個方法不見了。我所做的是讓我的類成爲一個部分類,它實現了我需要的方法,而不是使用擴展方法。我沒有看到答案中的部分內容。如果你有這個問題,partials可能是一個更好的選擇。