類和接口的實現
接口定義:為一組方法簽名指定一個名稱的方式。
類實現接口,就一定要提供接口所有方法的實現。
即使抽象類,也要全部實現,但是,它可以把接口方法聲明為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的集合基於接口,因為各種集合間極少共享的代碼。