類和介面的實現
介面定義:為一組方法簽名指定一個名稱的方式。
類實現介面,就一定要提供介面所有方法的實現。
即使抽象類別,也要全部實現,但是,它可以把介面方法聲明為abstract的,從而把這個介面方法留給衍生類別去實現,如下:
public interface ITest
{
void Test();
}
public abstract class AbstractClass : ITest
{
public abstract void Test();
}
public class ConcreateClass : AbstractClass
{
public override void Test()
{
//Coding implementation;
}
}
14.2 定義介面
介面中可以有方法/事件/屬性,因為後者本質上也是 方法
介面中不可以有靜態成員(包括常量/枚舉)
介面之間可以"繼承",可以認為是包含另一種介面的約定,並不是真正意義的繼承
介面下所有成員,預設為public,不用聲明
14.3 實現介面
實現的介面方法,一定要標記為public,此時在IL中為virtual和sealed,即不允許子類重寫該方法(這時new也不管用了)。
要顯示將調用的介面方法標記為virtual,則可以在子類中重寫該方法
//如果顯示標記為sealed,那麼就更不可以重寫了
14.4 調用介面方法
在運行時,可以將一個變數從一種介面類型轉型為另一種介面類型,只要該對象的類型實現了這兩種介面,如下:
//String實現了Icloneable, IEnumerable
String s = "Jax";
ICloneable cloneable = s;
IEnumerable enumable = (IEnumerable)cloneable;
註:cloneable只可使用ICloneable的介面方法,不可以使用String的方法;enumable變數雖由ICloneable轉型而來,但也不能使用ICloneable介面方法。
實值型別也可以實現介面,但是在轉成介面類型前要先裝箱——介面變數必須是指向堆上的一個對象的引用。
//Int32實現了IFormattable介面
Int32 i = 0;
//i在轉成介面類型前要先裝箱
IFormattable formattable = (IFormattable)i;
14.5 介面方法的隱式/顯示實現
介面方法一般都是隱式實現的,可訪問性一定要聲明為public。
EIMI:顯示介面方法實現,用定義方法的那個介面的名稱來作為方法名稱的首碼。
不屬於類型對象的一部分,只是將一個介面串連到類型上,同時避免了暴露行為和方法
不能指定可訪問性public/private——在IL中標記為為private,只有通過介面變數才能訪問該方法,以防止類型對象直接存取。
不能標記為virtual,不能被重寫。
public interface ITest
{
void Test();
}
public class TestClass : ITest
{
public void Test()
{
Console.WriteLine("1");
}
void ITest.Test()
{
Console.WriteLine("2");
}
} TestClass t = new TestClass();
t.Test(); //輸出1
ITest it = (ITest)t;
it.Test(); //輸出2
14.6 泛型介面
3個好處
1.編譯時間的型別安全
Int32 x = 1;
String s = "1";
IComparable c = x;
c.CompareTo(x);
c.CompareTo(s); //運行期錯誤,因為Object是不安全類型
IComparable<Int32> cc = x; //強型別,所以直接受整型x,不接受字串s,否則編譯期報錯
2.操作實值型別時減少裝箱
上個例子將x傳到Compare方法要裝箱,使用泛型不用裝箱,按值傳遞。
非泛型現在仍存在於FCL,是為了向後相容。
3.同一個類可以實現同一個泛型介面若干次,只要使用不同型別參數
public sealed class Number : IComparable<Int32>, IComparable<String>
{
public int CompareTo(int other) { }
public int CompareTo(string other) { }
}
可以把IComparable<Int32>和IComparable<String>看作兩個不同的介面,就好理解了。
14.7 泛型介面的參數約束
2個好處:
1.可以將型別參數約束為多個介面,從而使傳入的參數類型必須實現所有介面約束
2.減少裝箱
參數約束,會產生特定的IL語言,使得直接在實值型別上調用介面方法,而不用裝箱。
14.9 用EIMI改進編譯時間型別安全
使用EIMI技術,處理非泛型介面,保證型別安全
struct SomeValueType : IComparable
{
private Int32 m_x;
public SomeValueType(Int32 x)
{
m_x = x;
}
//這是一個型別安全的方法
public Int32 CompareTo(SomeValueType other)
{
return m_x - other.m_x;
}
//這是一個類型不安全的方法,但是一定要有,才能保證編譯通過,因為參數不同,可以認為是重載,而且這個方法才是介面方法的實現
Int32 IComparable.CompareTo(Object other)
{
return CompareTo((SomeValueType)other);
}
}
調用的時候要注意:
SomeValueType v = new SomeValueType();
Object o = new Object();
Int32 n = v.CompareTo(v); //類對象v的Comapre方法,保證型別安全
IComparable c = v;
n = c.CompareTo(v);
n = c.CompareTo(o); //介面對象c的Comapre方法,不能保證型別安全,所以不要使用介面對象
14.10 EIMI的缺點
3個缺點:
1.沒有說明具體如何?一個EIMI方法
2.實值型別執行個體在轉換為介面類型時,會被裝箱
3.EIMI方法不能被衍生類別型繼承
14.11 介面與類繼承
類繼承: 表示 IS-A。便於使用,不必提供所有實現;可以override和new重寫;易於在基類中新增成員,而不需改動子類
介面: 表示 CAN-DO。以上類繼承的優點一概沒有。
實值型別繼承自System.ValueType,只能使用介面
FCL的集合基於介面,因為各種集合間極少共用程式碼。