C#里的顯式介面實現是什麼原理?
今天偶然看到了這兩個文檔:
Explicit Interface Implementation Tutorial (C#)Explicit Interface Implementation (C# Programming Guide)很好奇這是什麼原理?是C#支持的還是CIL/CLS/CLR支持的?
針對 @李建忠 的回答說幾句。
包括老趙,好幾個同學都在說為了「避免命名衝突」, 這也是MSDN一些人給的所謂「官方理由」。
sign....繞這麼大彎子就為了一個 命名衝突.... 為什麼不直接改名字? 隱藏成員......好吧,真不知道為什麼要隱藏。 如果private,就乾乾脆脆private;如果public,就乾乾脆脆public。騎牆派設計,純粹為了給api的使用者增加心智負擔。
「改名字」不是解決命名衝突的辦法,原因很多,說幾個常見的情況。
1、介面不是你自己定義的,你只拿到dll,沒有源代碼。
2、介面是為不同組件/框架定義的,在各自組件中都是合理的命名,但命名有衝突。框架實現者沒法為了使用者而改動,實現者不可能知道使用者會和誰一起用。甚至因為要保證兼容性,根本沒法改。3、有些介面成員在實現的時候,就是不準備給人用的。例如你要調用的一個方法接受IList參數,但不會修改它。你寫一個自己的類來實現IList時,就把不準備用的隱藏起來,甚至可以額外暴露一個同名方法供內部使用,此時它可以不是介面方法:private class MyBuffer&
public void Add&
public void Clear() { ... }
void IList&
throw new NotSupportedException();
}
void IList& 這樣我們可以修改我們自己的Buffer(因為我們拿到的是MyBuffer類型),但作為IList& 4. 命名是根據成員的概念來的,不能因為命名衝突就妥協。尤其是優秀的實踐是讓方法「單純」,越「單純」的方法名字越簡單,越容易衝突。由於絕大部分語言是不能通過返回值來區分方法的,這個需求更加重要。打個比方,我們自己寫了個TreeList類,它自然是要實現IList&
throw new NotSupportedException();
}
}
private class TreeList&
public T RemoveAt(int index) {
...
}
void RemoveAt(int index) {
RemoveAt(index);
}
}
TreeList根據索引來獲取元素是O(log(N))的行為,所以說假如我要在刪除前拿到這個元素,則通過IList&
另外,最後提到的「隱藏成員」不是private和public能做到的。假如一個類不想讓dynamic或WPF綁定訪問到一些屬性/方法,但又要實現某個介面的屬性/方法,就可以把這個屬性/方法定義為顯式實現,這樣就不會被使用者誤用。當你是框架或通用代碼提供者的時候,你就知道避免誤用是多麼重要的事情了。
總之,多寫一些代碼,多想辦法提高一點代碼質量,多想辦法提高一點代碼性能,多想一下如何與別人配合,就會知道顯式實現介面是多麼重要的設計。
最後,我不記得Jeffrey Richter說過這個設計是為了讓某些成員不要有太多類型。首先,「可以讓某些成員不要有太多類型」和「為了讓某些成員不要有太多類型」是兩個概念。其次,就算是他這麼說了,也不用盲信,他說錯了也就是錯了。問的是顯式介面的實現原理。
首先搞清楚,介面的原理是什麼——跟C++的虛根類差不多一樣的東西。區別是,介面的成員,語法設計上,只能通過介面來訪問,而通過其實現類不可以直接訪問。說白了,介面的正體就是顯式介面。但是每次都要轉換成介面才能訪問很不方便,因此C#弄了個方便法門——隱式介面,給實現介面的類多做一個函數,指向跟介面同名同參的函數,同時簡化了語法。這就是隱式介面的實質。
當然這種簡化是有代價的,佔用了一個函數。當這種方便變成不便,讓你覺得為難困擾的時候,就改用顯示介面吧,具體老趙的答案里已經說出
===============================================補充:這並非一種「腦殘設計」,我們想一下天堂上的類多繼承——爸爸們帶來了多少困擾?這不是改名字就可以解決的,你可以改變自己,但是不能改變你爸爸。介面(顯式)解決了這問題。
顯式介面實現當初不一定是設計來給C#用的啊,也許C#最後只是順便就支持了……
VB就沒有什麼隱式顯式的概念,它只支持顯式介面實現,也就是說每個介面方法都要顯示指定相對於C#EIMI的不優雅,VB裡面更強大更清爽,而且Public的方法也能用來顯示實現,也不存在不能被派生之類的限制沒啥原理,無非就是在metadata層面,當以類的面貌出現時,將metadata設為private;當以介面面貌出現時,將metadata設為public罷了。
不過,顯式介面實現絕對是一個腦殘的設計。 我記得Jeffrey Richter說這個設計是為了 讓某些成員太多的類型(比如Int32)顯得少一些。 這是我歷史上看到最搞笑的理由了.....
-----------------------------------------------------------------------------------------
包括老趙,好幾個同學都在說為了「避免命名衝突」, 這也是MSDN一些人給的所謂「官方理由」。
sign....繞這麼大彎子就為了一個 命名衝突.... 為什麼不直接改名字? 隱藏成員......好吧,真不知道為什麼要隱藏。 如果private,就乾乾脆脆private;如果public,就乾乾脆脆public。騎牆派設計,純粹為了給api的使用者增加心智負擔。
保留「腦殘設計」的評價。這個顯示介面應用還是比較廣泛的 , 記得上個月吧 , 有一個新手問我的問題如下:請問,我的A程序集中的User Class 中 ,Name , Age . Get 是 Public , Set 是Internal 訪問修飾符,但是我還想讓B程序集可以賦值 . 如何解決呢 ?
public class UserInformation
{
public string Name { get; set; }
public uint Age { get; internal set; }
}
----------------------------------------------------------- 貼心 , 用心 分割 --------------------------------------------------------
我給這位朋友提供了兩種方案:第一種方案:java Getter ,Setter 模式.public class UserInformation
{
public string Name { get; internal set; }
public uint Age { get; set; }
public void SetName(string name)
{
Name = name;
}
}
第二種方案:C# 顯示介面實現模式 , 我給這個模式起名叫 「讀寫裝飾」
public interface IInformation
{
string Name { get; }
uint Age { get; }
}
public interface IMutableInformation : IInformation
{
new string Name { set; }
new uint Age { set; }
}
public class UserInformation : IInformation , IMutableInformation
{
private string _Name = string.Empty;
private uint _Age = default(uint);
public string Name { get { return _Name; } }
public uint Age { get { return _Age; } }
string IMutableInformation.Name
{
set { _Name = value; }
}
uint IMutableInformation.Age
{
set { _Age = value; }
}
}
// 開始測試啦.....
using System;
using static Console;
//using A
class Program
{
static void Main(string[] args)
{
A.UserInformation information = new A.UserInformation();
A.IMutableInformation mutableInformation = information;
mutableInformation.Name = "張三丰";
mutableInformation.Age = 102;
WriteLine(information.Name);
WriteLine(information.Age);
// information.Name = "張三丰"; // 這樣,此處編譯的時候會出錯,你需要用【IMutableInformation】介面 給【A.UserInformation】賦值.
ReadLine();
}
}
推薦閱讀:
※如何開始學習CoreCLR源代碼?
※Stack-based 的虛擬機有什麼常用的優化策略?
※RyuJIT為什麼比JIT64編譯速度快?
※程序集什麼玩意?我知道其表現形式為dll和exe,但是exe不是直接執行的文件嗎?而dll只是類庫,供exe調用代碼?