[英]Why is an explicitly implemented interface method called with callvirt and an implicitly implemented not?
[英]Why does this interface have to be explicitly implemented?
幾年后回到C#所以我有點生疏了。 遇到這個(簡化的)代碼,它讓我頭疼。
為什么必須顯式實現IDataItem.Children
屬性? 普通的Children
財產是否滿足要求? 畢竟,該屬性直接用於滿足它。 為什么不隱含?
public interface IDataItem {
IEnumerable<string> Children { get; }
}
public class DataItem : IDataItem {
public Collection<string> Children { get; } = new Collection<string>();
// Why doesn't 'Children' above implement this automatically?!
// After all it's used directly to satisfy the requirement!
IEnumerable<string> IDataItem.Children => Children;
}
根據C#源代碼,這里是Collection<T>
的定義:
[System.Runtime.InteropServices.ComVisible(false)]
public class Collection<T> :
System.Collections.Generic.ICollection<T>,
System.Collections.Generic.IEnumerable<T>, <-- Right Here
System.Collections.Generic.IList<T>,
System.Collections.Generic.IReadOnlyCollection<T>,
System.Collections.Generic.IReadOnlyList<T>,
System.Collections.IList
正如你所看到的,它明確地實現了IEnumerable<T>
,根據我的理解,如果'X'實現'Y'然后'X' 是 'Y',那么Collection<String>
是一個IEnumerable<String>
所以我是不確定為什么不滿意。
也許這個例子讓它更清晰。 我們希望簽名完全匹配1,2 ,不允許任何替換,盡管類型之間存在任何繼承關系。
我們不允許寫這個:
public interface IDataItem {
void DoStuff(string value);
}
public class DataItem : IDataItem {
public void DoStuff(object value) { }
}
你的例子是相同的,除了你要求返回類型而不是參數(並采用縮小而不是加寬轉換,原因很明顯)。 盡管如此,同樣的原則也適用。 在匹配簽名時,類型必須完全匹配3 。
您可以要求提供允許此類事件發生的語言,並且可能存在此類語言。 但事實是,這些是C#的規則。
1除了涉及泛型和接口/ delgates的Co和Contra-variance的一些有限支持之外。
2有些人可能會爭論簽名是否是在這里使用的正確詞匯,因為在這種情況下 ,返回類型與參數類型,通用arity等一樣重要; 在大多數其他人談論C#方法簽名的情況下,他們將明確忽略返回類型,因為他們(明確地或隱含地)考慮重載規則所說的內容,並且對於重載,返回類型不是簽名的一部分。
盡管如此,我對我在這里使用“簽名”一詞感到滿意。 簽名未在C#規范中正式定義,並且在使用它的地方,通常會指出簽名的哪些部分不被考慮用於重載。
3更不用說如果您的Children
方法返回一個碰巧實現IEnumerable<string>
的struct
,將會引發的問題。 現在你有了一個返回值類型和調用者值的方法(通過IDataItem
接口,它希望接收對象的引用) 。
因此,該方法甚至不能按原樣使用。 我們必須(在這種情況下) 隱藏裝箱轉換來實現接口。 當推測C#的這一部分時,我相信它們不會為你自己可以輕易編寫的代碼設置太多“隱藏的魔力”。
在您的示例中,您的“普通”Children屬性實際上並不滿足接口要求。 類型不同。 你可以施展它並不重要 - 它們是不同的。
類似的例子,也許更明顯的是,如果你將實現一個接口,其實際方法返回IEnumerable並嘗試從實際類中的ICollection方法。 仍然存在編譯時錯誤。
正如@Ben Voigt所說,轉換仍會產生一些代碼,如果你想擁有它 - 你需要隱式添加它。
這里已經回答了這個問題(你必須匹配接口類型),我們可以用一個例子來演示這個問題。 如果這有效:
public interface IDataItem {
IEnumerable<string> Children { get; set; }
void Wipe();
}
public class DataItem : IDataItem {
public Collection<string> Children { get; } = new Collection<string>();
public void Wipe() {
Children.ClearItems(); //Property exists on Collection, but not on IEnumerable
}
}
然后我們使用這樣的代碼:
IDataItem x = new DataItem();
//Should be legal, as Queue implements IEnumerable. Note entirely sure
//how this would work, but this is the system you are asking about.
x.Children = new Queue<string>();
x.Wipe(); // Will fail, as Queue does not have a ClearItems method within
您要么意味着該屬性只能枚舉,要么您需要Collection類的屬性 - 適當地鍵入您的接口。
任何實現接口的類都必須包含與接口指定的簽名匹配的方法的定義。 界面僅定義簽名。 這樣,C#中的接口類似於抽象類,其中所有方法都是抽象的。
接口可以包含方法,屬性,事件,索引器或這四種成員類型的任意組合。
這是關於接口的好讀物。 C#中的接口
希望它能幫助你進一步理解。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.