- 自訂型別可以實現內建 interface 來擴充自己的功能。
- interface 只是一群 abstract members 的集合。interface 表達一種 behavior 可以讓 class 或 structure 選擇去 implement 這樣的 behavior。
- interface 當中只有 property 和 method 成員,而且所有成員都是 abstract。
- Interface Types 和 Abstract Base Class 的差異
- Abstrace Base Class 可以定義 constructor, field 和 nonabstract members;但是 Interface 只能包含 abstract members。
- 由 abstract parent class 所建構出的多型有一個主要的限制,就是只有 derived types 支援 abstract parent 定義的成員。因此無法要求在不同繼承階層下的 types 支援相同的多型介面。相反的,一旦定義了 interface,他可以被任何型別 implement,再任意的繼承階層之中,甚至任意的 namespace 或 assembly。
- 假如有一個 method 叫做 CloneMe() 並且 take 一個 ICloneable interface 為輸入參數,我們就可以將任何實做了 ICloneable interface 的物件 (即使這些物件不屬於同一個繼承階層) 傳入 CloneMe() 方法當中。換句話說,透過 interface 可以讓不屬於同一個 inherence hierachy 的 class 也能實現多型。
- abstract base class 另外一個限制是,所有 derived type 針對每一個 abstract member 都要提供具體的 implement,即使有時候 derived type 去實現那個 member 沒有甚麼意義的時候,也是得照做。interface 則可以避免這個缺點,它就像是一個隨插即用的 usb,需要他的時候在 implement 他即可。
- Defining Custom Interfaces
- 所有 interface members 都是隱含宣告為 public 和 abstract。
- Interfaces 只是單純的 protocol 不能包含實做。
- Implementing an Interface
- 如果一個類別要繼承又要 plug-in interface 的話,在 : 後面要先寫繼承,然後再寫 interface。
- 一旦 plug-in 了該 interface,就必須實現所有 interface 當中的 member。
- Invoking Interface Members at the Object Level
- 一旦你的 class plug-in 了某個 interface,接下來該怎麼使用 interface 提供的 functionality 呢? 直接的做法是,宣告一個物件,然後呼叫 class 當中實現完成的 interface function。但是除非你就是這個 class 的實作者,不然經常妳是不知道這個 class 是否 support 某個 interface,於是就產生了這個問題: How can we dynamically determine the set of interfaces supported by a type?
- Interfaces As Parameters
- Interfaces As Return Values
- Arrays of Interface Types
- Resolving Name Clashes via Explicit Interface Implementation
- 由於 class 並沒有限制可以 plug 上多少的 interface,當一個 class plug 上多個 interface的時候,interfaces 當中的 member 可能會有重覆命名的情況發生 (Name Clashes)。
- 若 A class plug-in 了 B 和 C interfaces,B 和 C 都有 D method,如果 A class 直接實現 D 方法,並無法鑑別出 B 和 C 有何差異。因此需要 explicit interface implementation: 就是在方法的前面明確的指出是哪一個 interface ( e.g. void B.D (arg) {...} )。
- 並且注意,在使用 explicit interface implementation 時前方不需要加 access modifier,其將自動為 private。因此,這些成員將無法透過 object level 取得,也無法透過 . 來看到該成員。
- 因此你必須透過 explicit casting 來取得該成員的功能。例如: B a_b = (B)A; a_b.D,先將 A 轉成 B interface a_b,再從 a_b 取出 D method。
- 凡透過 explicit interface implementation 的 member 都將被設成 private,某種程度來說是一種封裝,而想要更進一步利用 interface 功能者,則要自行用 explicit casting 提取出來使用。
- Interface Hierarchies
- when an interface extends an existing interface, it inherits the abstract members defined by the parent type(s)
- Interface hierarchies can be useful when you wish to extend the functionality of an existing interface without breaking existing code bases
- we are able to invoke each method at the object level (as they are all public) as well as extract out a reference to each supported interface explicitly via casting
- Multiple inheritance for interface types is a-okay
- Interfaces can be extremely useful when
- You have a single hierarchy where only a subset of the derived types support a common behavior
- You need to model a common behavior that is found across multiple hierarchies with no common parent class beyond System.Object
- Building Enumerable Types
- C# 中的 foreach 可以 loop 陣列當中的每一個 element,其實真正的關鍵是任何型別只要支援一個方法 GetEnumerator 都可以使用 foreach。
- 假如你自行創建了一個類別,並宣告了一個陣列當中存放該類別創造出的物件,那麼並無法使用 foreach 去 loop,compiler 會告訴你該類別並沒有實現 GetEnumerator 這個方法。
- GetEnumerator 這個方法存在於 IEnumerable interface 當中,此介面潛藏於 System.Collections 當中,支援此 interface 的型別代表可以 expose sub items to caller。
- 要使自定的物件可以使用 foreach,一個方式就是精確的在該類別當中清楚的定義 IEnumerable 當中每一個 member,或者比較簡單的方式,在類別當中宣告一個 GetEnumerator 方法,在方法的定義中使用 Array 本身的 GetEnumerator 方法當作 return 值。
- 由於 GetEnumerator 方法的返回值是一個 IEnumerable 介面,如上述所做 object user 可以呼叫 GetEnumerator 方法取得 IEnumerable 介面進而使用該介面當中的其他方法取得陣列中的物件,若要避免 object user 取得 IEnumerable 介面可以使用 explicit interface implementation。
- 另外一種方法就是利用 iterator,使用這種方式一樣要在類別當中宣告一個 GetEnumerator 方法並且返回值也是 IEnumerable 介面,不同的是該類別不需要 plug-in 任何 interface。
- 也可以不使用 internal 的 foreach (如上圖),而使用 yield return 的好處是,可以將 class 當中的內部資料丟出去給 foreach 使用。
- yield 這個 keyword 不一定要在GetEnumerator 中使用,可以用在任何 method 當中,這些方法技術上的名稱是 named iterators,建構這種方法必須清楚他的回傳值是 IEnumerable interface。
- 這個方法的好處是可以讓一個單一的 container 定義多種方式去拿到他的回傳值,例如 順排 或 逆排。
- Building Cloneable Objects (ICloneable)
- public interface ICloneable
{
object Clone();
} - value type 和 reference type 的不同就是當你 assign value type 的變數給另一個變數的時候是複製一份,而 reference type 的話則是指向同一個物件的記憶體位置。所以,當你想要讓自定的 class (reference type) 可以擁有像 value type 那樣的 "複製一份" 的功能,就需要實做 ICloneable interface,這個 interface 定義了一個方法 Clone()
- 實做 Clone() 方法依 class 的定義各有不同,不過基本上就是將成員變數的值複製到一個新的物件,再將這個物件 return 給 user。注意: Clone() 方法 return 值是一般的物件型別,因此要 assign 的時候要用 explicit cast ! ( e.g. Point p4 = (Point)p3.Clone(); )
- 當類別中並沒有任何 reference type 的成員,可以用更簡易的方法實做 Clone() 方法,
public object Clone()
{
// Copy each field of the Point member by member.
return this.MemberwiseClone();
}
- 但是,當類別當中有 reference type 的成員時,使用 MemberwiseClone() 會導致 shallow copy,即該 reference type 成員並沒有被複製一份,導致兩個不同物件之間更動一個物件的該 reference type 成員,另一物件的該 reference type 成員也會跟著更動。
- 假若你希望達到 deep copy 的話,則必須在 Clone() 當中執行完 MemberwiseClone() 之後,針對 reference type 的成員,再另行宣告一個新的物件,將當前的值 copy 進去,然後再刷新 MemberwiseClone() return 出來的物件的該成員。
public object Clone()
{
// First get a shallow copy.
Point newPoint = (Point)this.MemberwiseClone();
// Then fill in the gaps.
PointDescription currentDesc = new PointDescription();
currentDesc.petName = this.desc.petName;
newPoint.desc = currentDesc;
return newPoint;
}
- Building Comparable Objects (IComparable)
- 此介面允許物件根據特定的 key 來排序。
public interface IComparable
{
int CompareTo(object o);
}
- 假設你自定了一個類別 Car,並且宣告了一個陣列 CarArray 當中裝有該類別的物件,這時你呼叫 CarArray.Sort() 時會跳出 exception 告訴你需要實現 IComparable 介面。
// The iteration of the Car can be ordered
// based on the CarID.
public class Car : IComparable
{
...
// IComparable implementation.
int IComparable.CompareTo(object obj)
{
Car temp = (Car)obj;
if(this.carID > temp.carID)
return 1; (come after, 越大的排越後面)
if(this.carID < temp.carID)
return -1; (come before, 越小的排越前面)
else
return 0;
}
}
- 若你希望按照類別中的某個成員變數來排序,而該成員變數又是屬於 intrinsic type 的話,可以直接套用該變數的 CompareTo() 方法。
int IComparable.CompareTo(object obj)
{
Car temp = (Car)obj;
return this.carID.CompareTo(temp.carID);
}
- Specifying Multiple Sort Orders (IComparer),當你想要根據不只一個成員來做排序的話,那麼就要利用到另外一個 interface "IComparer", 該介面定義如下:
// A generic way to compare two objects.
interface IComparer
{
int Compare(object o1, object o2);
}
- IComparer 介面並非在你想要排序的型別中實現,通常是在 helper class 當中實現這個介面。而這個 helper class 剛好可以當作 System.Array 當中 Sort() 方法的一個輸入參數:
// This helper class is used to sort an array of Cars by pet name.
using System.Collections;
public class PetNameComparer : IComparer
{
// Test the pet name of each object.
int IComparer.Compare(object o1, object o2)
{
Car t1 = (Car)o1;
Car t2 = (Car)o2;
return String.Compare(t1.PetName, t2.PetName);
}
}
static void Main(string[] args)
- {
- ...
- // Now sort by pet name.
- Array.Sort(myAutos, new PetNameComparer());
}
- Custom Properties,Custom Sort Types 也可以將 helper class 寫進該類別的 properties 當中,這樣就可以直接在 Sort() 的參數直接取 property,更為直覺。
No comments:
Post a Comment