比對型別的幾種方式

  • 230
  • 0
  • 2020-03-22

簡單討論一些型別比對的方式。

比對型別有許多種方式,例如 is 運算子或是 Type class 裡的一些方法與屬性。以下討論範例中所使用的自訂型別如下:

  public interface IShape
    {
        double GetArea();
    }

  public abstract class Shape : IShape
    {
        public string Name { get; set; }

        public abstract double GetArea();
    }

  public class Rect : Shape
    {
        public double Width { get; set; }
        public double Height { get; set; }
        public override double GetArea()
        {
            return Width * Height; 
        }
    }

  public class Circle : Shape
    {
        public double Radius { get; set; }
        public override double GetArea()
        {
            return Math.PI * Math.Pow(Radius, 2);
        }
    }
1. is operator

is 運算子用於判別某個物件是否屬於某個型別,包含基底類別和介面都可以判別,例如:

object rect = new Rect();
Console.WriteLine($"rect is IShape : {rect is IShape}");
Console.WriteLine($"rect is Shape : {rect is Shape}");
Console.WriteLine($"rect is Rect : {rect is Rect}");
Console.WriteLine($"rect is object : {rect is object}");

object i = 10;
Console.WriteLine($"i is int : {i is int }");
Console.WriteLine($"i is int : {i is ValueType }");
2. IsValueType property

這兩個是 Type Class 的執行個體屬性,用於判別該型別是否為實值型別,這只需要型別,毋須使用執行個體,例如:

 Console.WriteLine($"int type IsValueType : {typeof(int).IsValueType}");
 Console.WriteLine($"ValueType IsValueType : {typeof(ValueType).IsValueType}");

第二個結果會是 false,因為 ValuType class 雖然是實值型別之母,但它本身是參考型別。

3. IsEnum property

 一樣是 Type class 的執行個體屬性,用於判定某個型別是否為列舉型別

Console.WriteLine($"DayOfWeek IsEnum : {typeof(DayOfWeek).IsEnum}");
Console.WriteLine($"int type IsEnum : {typeof(int).IsEnum}");
4. IsClass property

  Type class 的執行個體屬性,用於判定某個型別是否為類別或委派

 Console.WriteLine($"Func<T> IsClass : {typeof(Func<>).IsClass }");
 Console.WriteLine($"Shape IsClass :{typeof(Shape).IsClass}");
 Console.WriteLine($"Circle IsClass :{typeof(Circle).IsClass}");
 Console.WriteLine($"IShape IsClass :{typeof(IShape).IsClass}");

最後一個 ISahpe 的部分,因為是介面,結果為 false。

5. IsInterface property

  Type class 的執行個體屬性,用於判定某個型別是否為介面型別

 Console.WriteLine($"Func<T> IsInterface : {typeof(Func<>).IsInterface }");
 Console.WriteLine($"Shape IsInterface :{typeof(Shape).IsInterface}");
 Console.WriteLine($"Circle IsInterface :{typeof(Circle).IsInterface}");
 Console.WriteLine($"IShape IsInterface :{typeof(IShape).IsInterface}");

想當然耳,只有最後一個結果是 true。

6. IsGenericType property

Type class 的執行個體屬性,用於判定某個型別是否為泛型型別

 Console.WriteLine($"Func<T> IsGenericType : {typeof(Func<>).IsGenericType }");
 Console.WriteLine($"List<T> IsGenericType : {typeof(List<>).IsGenericType }");
 Console.WriteLine($"IComparable<T> IsGenericType : {typeof(IComparable<>).IsGenericType }");
 Console.WriteLine($"int[] IsGenericType : {typeof(int[]).IsGenericType }");

最後一個結果是 false,因為陣列不屬於泛型。

7. IsAssignableFrom method

Type class 的執行個體方法,TypeA.IsAssignableFrom(TypeB) 表示判別 TypeB 所產生的執行個體是否能指派給 TypeA 型別的變數,例如:

 Console.WriteLine($"IShape IsAssignableFrom Rect {typeof(IShape).IsAssignableFrom(typeof(Rect))}");
 Console.WriteLine($"Object IsAssignableFrom Rect {typeof(object).IsAssignableFrom(typeof(Rect))}");
 Console.WriteLine($"Rect IsAssignableFrom Shape {typeof(Rect).IsAssignableFrom(typeof(Shape))}");

前兩個結果是 true,因為 Rect 執行個體可以指派給 IShape 或 Object 型別的變數;而最後一個結果則是 false。

8. Type.IsInstanceOf method

 Type class 的執行個體方法,很類似 TypeA.IsAssignableFrom,但有點不一樣的是參數不是 Type 而是 object。適用於比較執行個體是否為某個型別。如果型別是可以直接使用的,我通常會使用 is operator;另外一種情境則是 Type 是透過反射取得的,這種情形沒辦法直接在程式碼使用該型別,那我就會選擇這個方式處理。

 Console.WriteLine($"Rect IsInstanceOfType rect {typeof(Rect).IsInstanceOfType(rect)}");
 Console.WriteLine($"Shape IsInstanceOfType rect {typeof(Shape).IsInstanceOfType(rect)}");
 Console.WriteLine($"IShape IsInstanceOfType rect {typeof(Shape).IsInstanceOfType(rect)}");
9. IsSubclassOf

  Type class 的執行個體方法,假設 TypeA.IsSubclassOf(TypeB) 表示要判斷 TypeB 是否為 TypeA 的基底類別,但若 TypeA 是介面的話,永遠都會是 false;換言之,這個只能判斷純粹的類別繼承。

  Console.WriteLine($"Rect IsSubclassOf Shape {typeof(Rect).IsSubclassOf(typeof(Shape))}");
  Console.WriteLine($"Rect IsSubclassOf IShape {typeof(Rect).IsSubclassOf(typeof(IShape))}");
  Console.WriteLine($"int IsSubclassOf ValueType {typeof(int).IsSubclassOf(typeof(ValueType))}");
  Console.WriteLine($"DayOfWeek IsSubclassOf Enum {typeof(DayOfWeek).IsSubclassOf(typeof(Enum))}");

其中第二個結果會是 false,其他則為 true。另外雖然這個方式可以判斷 ValueType 或是 Enum,但依據 Microsoft Docs 上的建議,此時使用 IsValueType 或 IsEnum 屬性來判斷是比較有效率的。

範例可以參考 github

註:這篇的重點在講型別比對,也許有人覺得怎麼沒提 type pattern matching,原因很簡單,因為那不是這篇要講的事,type pattern matching 其實之前就寫過了,但我知道有些人總是喜歡搞錯重點,關於 type pattern matching 可以參考之前的文章 Pattern Matching 使用 var 要謹慎。