2017年9月25日 星期一

C# : is (C# 7.0 類型模式) 與 as 的使用練習

我們宣告了三個類別 ClassA 是最上層的基底類別,Class B 則是繼承了 ClassA,ClassC 繼承了 ClassC。
ClassA -> ClassB -> ClasssC
這三個類別,都有分別覆寫 ToString 這個方法,他們的建立程式碼如下所示。
class ClassA : ICloneable
{
    public object Clone()
    {
        return this.MemberwiseClone();
    }

    public override string ToString()
    {
        return "It is ClassA";
    }
}

class ClassB : ClassA
{
    public override string ToString()
    {
        return "It is ClassB";
    }
}

class ClassC : ClassB
{
    public override string ToString()
    {
        return "It is ClassC";
    }
}

as 的用法

從微軟官方的說明文件中,我們可以知道
使用 as 運算子,以在相容的參考型別或可為 Null 的型別之間執行特定轉換類型
在這裡,我們建立起六個物件,分別指定到型別為 object 的陣列中。
接著,我們使用 as 運算子,將執行時期的物件型別,強制轉型成為 string 這個型別,若當時物件的實際產生的型別不是 string,則,轉型出來的內容將會是空值 null。
object[] objArray = new object[6];
objArray[0] = new ClassA();
objArray[1] = new ClassB();
objArray[2] = "hello";
objArray[3] = 123;
objArray[4] = 123.4;
objArray[5] = null;

for (int i = 0; i < objArray.Length; ++i)
{
    // 在這裡進行型別轉換
    string s = objArray[i] as string;
    Console.Write("{0}:", i);
    if (s != null)
    {
        Console.WriteLine("'" + s + "'");
    }
    else
    {
        Console.WriteLine("not a string");
    }
}
底下是輸出結果
0:not a string
1:not a string
2:'hello'
3:not a string
4:not a string
5:not a string
Press any key for continuing...

is 的用法

從微軟官方的說明文件中,我們可以知道
is 運算子用於檢查物件是否與指定的類型相容
在這裡,我們分別建立 ClassA, ClassB, ClassC 這個三型別物件,在這三個物件,接著使用 is 運算子來檢驗是否與我們指定的型別運算之後,回得到 true 的回傳值。
在這裡,我們看到了,只要執行時期所產生的物件搭配 is 運算子運算之後,只要檢驗的型別,是當時物件的型別、繼承的型別、有繼承的介面,驗證結果皆會為真。
var cl1 = new ClassA();
Console.WriteLine($"cl1 is IFormatProvider : {cl1 is IFormatProvider}");
Console.WriteLine($"cl1 is ICloneable : {cl1 is ICloneable}");
Console.WriteLine($"cl1 is Object : {cl1 is Object}");
Console.WriteLine($"cl1 is ClassA : {cl1 is ClassA}");
Console.WriteLine($"cl1 is ClassB : {cl1 is ClassB}");
Console.WriteLine($"cl1 is ClassC : {cl1 is ClassC}");
Console.WriteLine($"Press any key for continuing...{Environment.NewLine}");
Console.ReadKey();

var cl2 = new ClassB();
Console.WriteLine($"cl2 is IFormatProvider : {cl2 is IFormatProvider}");
Console.WriteLine($"cl2 is ICloneable : {cl2 is ICloneable}");
Console.WriteLine($"cl2 is Object : {cl2 is Object}");
Console.WriteLine($"cl2 is ClassA : {cl2 is ClassA}");
Console.WriteLine($"cl2 is ClassB : {cl2 is ClassB}");
Console.WriteLine($"cl2 is ClassC : {cl2 is ClassC}");
Console.WriteLine($"Press any key for continuing...{Environment.NewLine}");
Console.ReadKey();

var cl3 = new ClassC();
Console.WriteLine($"cl3 is IFormatProvider : {cl3 is IFormatProvider}");
Console.WriteLine($"cl3 is ICloneable : {cl3 is ICloneable}");
Console.WriteLine($"cl3 is Object : {cl3 is Object}");
Console.WriteLine($"cl3 is ClassA : {cl3 is ClassA}");
Console.WriteLine($"cl3 is ClassB : {cl3 is ClassB}");
Console.WriteLine($"cl3 is ClassC : {cl3 is ClassC}");
Console.WriteLine($"Press any key for continuing...{Environment.NewLine}");
Console.ReadKey();
底下是輸出結果
cl1 is IFormatProvider : False
cl1 is ICloneable : True
cl1 is Object : True
cl1 is ClassA : True
cl1 is ClassB : False
cl1 is ClassC : False
Press any key for continuing...

cl2 is IFormatProvider : False
cl2 is ICloneable : True
cl2 is Object : True
cl2 is ClassA : True
cl2 is ClassB : True
cl2 is ClassC : False
Press any key for continuing...

cl3 is IFormatProvider : False
cl3 is ICloneable : True
cl3 is Object : True
cl3 is ClassA : True
cl3 is ClassB : True
cl3 is ClassC : True
Press any key for continuing...
我們在進行另外一個測試,那就是若產生的物件為一個衍生型別,而宣告的持有物件變數的型別為基底型別,這樣使用 is 運算子的結果會如何呢?
從底下的執行結果輸出內容可以清楚的觀察到, is 運算子會使用執行時期實際產生的物件型別,來進行型別檢驗。
Console.WriteLine($"ClassA clxl = cl2;");
ClassA clx1 = cl2;
Console.WriteLine($"ClassB clx2 = cl3;");
ClassB clx2 = cl3;
Console.WriteLine($"ClassA clx3 = cl3;");
ClassA clx3 = cl3;
Console.WriteLine($"{Environment.NewLine}");

Console.WriteLine($"clx1 is ClassA : {clx1 is ClassA}");
Console.WriteLine($"clx1 is ClassB : {clx1 is ClassB}");
Console.WriteLine($"clx1 is ClassC : {clx1 is ClassC}");
Console.WriteLine($"clx2 is ClassA : {clx2 is ClassA}");
Console.WriteLine($"clx2 is ClassB : {clx2 is ClassB}");
Console.WriteLine($"clx2 is ClassC : {clx2 is ClassC}");
Console.WriteLine($"clx3 is ClassA : {clx3 is ClassA}");
Console.WriteLine($"clx3 is ClassB : {clx3 is ClassB}");
Console.WriteLine($"clx3 is ClassC : {clx3 is ClassC}");
Console.WriteLine($"Press any key for continuing...{Environment.NewLine}");
Console.ReadKey();
底下是輸出結果
ClassA clxl = cl2;
ClassB clx2 = cl3;
ClassA clx3 = cl3;

clx1 is ClassA : True
clx1 is ClassB : True
clx1 is ClassC : False
clx2 is ClassA : True
clx2 is ClassB : True
clx2 is ClassC : True
clx3 is ClassA : True
clx3 is ClassB : True
clx3 is ClassC : True
Press any key for continuing...

類型模式執行模式比對

從微軟的官方文件中,可以得到
從 C# 7 開始,您可以搭配類型模式使用模式比對來撰寫更簡潔的程式碼,以使用 is 陳述式。
我們在這裡來使用看看,他的用法如下:
expr is type varname
例如,在底下的測試程式碼中,使用的 MyTest is ClassC e1 就是這樣的用法;若使用類型模式執行模式比對得到為否,e1 這個變數便會為空值 null。
var cl1 = new ClassA();
var cl2 = new ClassB();
var cl3 = new ClassC();

object MyTest;

// 用類型模式執行模式比對時,is 會測試運算式是否可轉換成指定的類型;如果可以的話,則會將它轉換成該類型的變數。 它是 is 陳述式的直接延伸,允許精簡類型的評估和轉換。
// https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/is#type
MyTest = cl2;
if (MyTest is ClassC e1)
{
    Console.WriteLine($"cl2 is ClassC : {e1.ToString()}");
}
if (MyTest is ClassA e2)
{
    Console.WriteLine($"cl2 is ClassA : {e2.ToString()}");
}
Console.WriteLine($"Press any key for continuing...{Environment.NewLine}");
Console.ReadKey();
底下是輸出結果
cl2 is ClassA : It is ClassB
Press any key for continuing...

Press any key for continuing...

沒有留言:

張貼留言