[英]Why does the C# compiler not fault code where a static method calls an instance method?
以下代碼有一個靜態方法Foo()
,調用實例方法Bar()
:
public sealed class Example
{
int count;
public static void Foo( dynamic x )
{
Bar(x);
}
void Bar( dynamic x )
{
count++;
}
}
它編譯時沒有錯誤*,但在運行時生成運行時綁定器異常。 正如預期的那樣,刪除這些方法的動態參數會導致編譯器錯誤。
那么為什么有一個動態參數允許編譯代碼呢? ReSharper 也不會將其顯示為錯誤。
編輯 1: *在 Visual Studio 2008 中
編輯 2:添加sealed
因為子類可能包含靜態Bar(...)
方法。 當不可能在運行時調用除實例方法之外的任何方法時,即使是密封版本也會編譯。
更新:以下答案寫於 2012 年,在 C# 7.3 (May 2018) 推出之前。 在C# 7.3 的新增功能中,改進的重載候選部分的第 1 項中,解釋了重載解析規則如何更改以便提前丟棄非靜態重載。 所以下面的答案(以及整個問題)現在大多只有歷史意義!
(C# 7.3 之前:)
出於某種原因,重載解析總是在檢查靜態與非靜態之前找到最佳匹配。 請嘗試所有靜態類型的代碼:
class SillyStuff
{
static void SameName(object o) { }
void SameName(string s) { }
public static void Test()
{
SameName("Hi mom");
}
}
這將無法編譯,因為最好的重載是采用string
重載。 但是,嘿,這是一個實例方法,所以編譯器會抱怨(而不是采用第二好的重載)。
補充:所以我覺得原題的dynamic
例子的解釋是,為了保持一致,當類型是動態的時候我們也會先找到最好的重載(只檢查參數數量和參數類型等,而不是靜態vs.非靜態),然后才是檢查靜態的。 但這意味着靜態檢查必須等到運行時。 因此觀察到的行為。
后期補充:可以從Eric Lippert 的這篇博文中推斷出他們為什么選擇以這種有趣的順序做事的一些背景。
Foo 有一個動態參數“x”,這意味着 Bar(x) 是一個動態表達式。
Example 完全有可能使用以下方法:
static Bar(SomeType obj)
在這種情況下,將解析正確的方法,因此語句 Bar(x) 是完全有效的。 有一個實例方法 Bar(x) 的事實是無關緊要的,甚至沒有考慮:根據定義,因為 Bar(x) 是一個動態表達式,我們將解析推遲到運行時。
“動態”表達式將在運行時綁定,因此如果您使用正確的簽名或實例方法定義靜態方法,編譯器將不會檢查它。
“正確”的方法將在運行時確定。 編譯器無法知道在運行時是否存在有效方法。
“dynamic”關鍵字是為動態和腳本語言定義的,其中方法可以在任何時間定義,甚至在運行時。 瘋狂的事情
這是一個處理整數但不處理字符串的示例,因為該方法在實例上。
class Program {
static void Main(string[] args) {
Example.Foo(1234);
Example.Foo("1234");
}
}
public class Example {
int count;
public static void Foo(dynamic x) {
Bar(x);
}
public static void Bar(int a) {
Console.WriteLine(a);
}
void Bar(dynamic x) {
count++;
}
}
您可以添加一個方法來處理所有無法處理的“錯誤”調用
public class Example {
int count;
public static void Foo(dynamic x) {
Bar(x);
}
public static void Bar<T>(T a) {
Console.WriteLine("Error handling:" + a);
}
public static void Bar(int a) {
Console.WriteLine(a);
}
void Bar(dynamic x) {
count++;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.