如何使用模式比對和 is 及 as 運算子來進行安全轉換

因為物件都是多型的,所以可能會針對基底類別類型的變數來保存衍生類型。 若要存取衍生類型的執行個體成員,就需要將值轉換回衍生類型。 不過,轉換帶有擲回 InvalidCastException 的風險。 C# 提供模式比對陳述式,只有在成功時才會有條件地執行轉換。 C# 也提供 isas 運算子,藉此測試值是否為特定類型。

下列範例示範如何使用模式比對 is 陳述式:

var g = new Giraffe();
var a = new Animal();
FeedMammals(g);
FeedMammals(a);
// Output:
// Eating.
// Animal is not a Mammal

SuperNova sn = new SuperNova();
TestForMammals(g);
TestForMammals(sn);

static void FeedMammals(Animal a)
{
    if (a is Mammal m)
    {
        m.Eat();
    }
    else
    {
        // variable 'm' is not in scope here, and can't be used.
        Console.WriteLine($"{a.GetType().Name} is not a Mammal");
    }
}

static void TestForMammals(object o)
{
    // You also can use the as operator and test for null
    // before referencing the variable.
    var m = o as Mammal;
    if (m != null)
    {
        Console.WriteLine(m.ToString());
    }
    else
    {
        Console.WriteLine($"{o.GetType().Name} is not a Mammal");
    }
}
// Output:
// I am an animal.
// SuperNova is not a Mammal

class Animal
{
    public void Eat() { Console.WriteLine("Eating."); }
    public override string ToString()
    {
        return "I am an animal.";
    }
}
class Mammal : Animal { }
class Giraffe : Mammal { }

class SuperNova { }

上述範例示範模式比對語法的一些功能。 if (a is Mammal m) 陳述式會結合測試與初始化指派。 只有在測試成功時,才會進行指派。 變數 m 僅會在已指派的內嵌 if 陳述式範圍內。 您隨後無法使用相同的方法存取 m。 上述範例也會示範如何使用 as 運算子 將物件轉換成指定型別。

如果可為 Null 的實值型別具有值,您也可以使用相同的語法進行測試,如下列範例所示:

int i = 5;
PatternMatchingNullable(i);

int? j = null;
PatternMatchingNullable(j);

double d = 9.78654;
PatternMatchingNullable(d);

PatternMatchingSwitch(i);
PatternMatchingSwitch(j);
PatternMatchingSwitch(d);

static void PatternMatchingNullable(ValueType? val)
{
    if (val is int j) // Nullable types are not allowed in patterns
    {
        Console.WriteLine(j);
    }
    else if (val is null) // If val is a nullable type with no value, this expression is true
    {
        Console.WriteLine("val is a nullable type with the null value");
    }
    else
    {
        Console.WriteLine("Could not convert " + val.ToString());
    }
}

static void PatternMatchingSwitch(ValueType? val)
{
    switch (val)
    {
        case int number:
            Console.WriteLine(number);
            break;
        case long number:
            Console.WriteLine(number);
            break;
        case decimal number:
            Console.WriteLine(number);
            break;
        case float number:
            Console.WriteLine(number);
            break;
        case double number:
            Console.WriteLine(number);
            break;
        case null:
            Console.WriteLine("val is a nullable type with the null value");
            break;
        default:
            Console.WriteLine("Could not convert " + val.ToString());
            break;
    }
}

上述範例示範模式比對轉換的其他功能,以便與轉換一起使用。 您可以特別檢查 null 值,藉此測試 Null 模式的變數。 當執行階段變數的值是 null,則 is 陳述式的類型檢查一律會傳回 false。 模式比對 is 陳述式不允許可為 Null 的值型別 (例如 int?Nullable<int>),但您可以測試其他值的型別。 上述範例中的 is 模式不限於可為 Null 的實值型別。 您也可以使用這些模式來測試參考型別的變數是否有值或值為 null

上述範例中也示範了如何在 switch 陳述式中使用型別模式,其中的變數可能是多種型別之一。

如果您想要測試變數是否為指定型別,但不將它指派給新的變數,您可以將 isas 運算子用於參考型別與可為 Null 的實質型別。 下列程式碼示範在模式比對推出之前,如何使用屬於 C# 語言的 isas 陳述式測試變數是否屬於特定類型:

// Use the is operator to verify the type.
// before performing a cast.
Giraffe g = new();
UseIsOperator(g);

// Use the as operator and test for null
// before referencing the variable.
UseAsOperator(g);

// Use pattern matching to test for null
// before referencing the variable
UsePatternMatchingIs(g);

// Use the as operator to test
// an incompatible type.
SuperNova sn = new();
UseAsOperator(sn);

// Use the as operator with a value type.
// Note the implicit conversion to int? in
// the method body.
int i = 5;
UseAsWithNullable(i);

double d = 9.78654;
UseAsWithNullable(d);

static void UseIsOperator(Animal a)
{
    if (a is Mammal)
    {
        Mammal m = (Mammal)a;
        m.Eat();
    }
}

static void UsePatternMatchingIs(Animal a)
{
    if (a is Mammal m)
    {
        m.Eat();
    }
}

static void UseAsOperator(object o)
{
    Mammal? m = o as Mammal;
    if (m is not null)
    {
        Console.WriteLine(m.ToString());
    }
    else
    {
        Console.WriteLine($"{o.GetType().Name} is not a Mammal");
    }
}

static void UseAsWithNullable(System.ValueType val)
{
    int? j = val as int?;
    if (j is not null)
    {
        Console.WriteLine(j);
    }
    else
    {
        Console.WriteLine("Could not convert " + val.ToString());
    }
}
class Animal
{
    public void Eat() => Console.WriteLine("Eating.");
    public override string ToString() => "I am an animal.";
}
class Mammal : Animal { }
class Giraffe : Mammal { }

class SuperNova { }

透過將此程式碼與模式比對程式碼進行比較,明顯可見,模式比對語法藉著在單一陳述式中結合測試和指派來提供更強大的功能。 請盡可能使用模式比對語法。