如何:使用模式匹配以及 is 和 as 运算符安全地进行强制转换How to: safely cast by using pattern matching and the is and as operators

由于是多态对象,基类类型的变量可以保存派生类型Because objects are polymorphic, it's possible for a variable of a base class type to hold a derived type. 要访问派生类型的实例成员,必须将值强制转换回派生类型。To access the derived type's instance members, it's necessary to cast the value back to the derived type. 但是,强制转换会引发 InvalidCastException 风险。However, a cast creates the risk of throwing an InvalidCastException. C# 提供模式匹配语句,该语句只有在成功时才会有条件地执行强制转换。C# provides pattern matching statements that perform a cast conditionally only when it will succeed. C# 还提供 isas 运算符来测试值是否属于特定类型。C# also provides the is and as operators to test if a value is of a certain type.

下面的代码演示模式匹配 is 语句。The following code demonstrates the pattern matching is statement. 它包含测试方法参数的方法,以确定它是否是一组可能的派生类型:It contains methods that test a method argument to determine if it is one of a possible set of derived types:

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 { }

class Program
{
    static void Main(string[] args)
    {
        Giraffe g = new Giraffe();
        FeedMammals(g);

        TestForMammals(g);

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

    static void FeedMammals(Animal a)
    {
        // Use the is operator to verify the type
        // before performing a cast.
        if (a is Mammal m)
        {
            m.Eat();
        }
    }

    static void TestForMammals(object o)
    {
        // Alternatively, use the as operator and test for null
        // before referencing the variable.
        if (o is Mammal m)
        {
            Console.WriteLine(m.ToString());
        }
        else
        {
            // variable 'm' is not in scope here, and can't be used.
            Console.WriteLine($"{o.GetType().Name} is not a Mammal");
        }
    }
}

前面的示例演示了模式匹配语法的一些功能。The preceding sample demonstrates a few features of pattern matching syntax. if (a is Mammal m)if (o is Mammal m) 语句将测试与初始化赋值相结合。The if (a is Mammal m) and if (o is Mammal m) statements combine the test with an initialization assignment. 只有在测试成功时才会进行赋值。The assignment occurs only when the test succeeds. 变量 m 仅在已赋值的嵌入式 if 语句的范围内。The variable m is only in scope in the embedded if statement where it has been assigned. 以后无法在同一方法中访问 mYou cannot access m later in the same method. 在交互式窗口中尝试操作。Try it in the interactive window.

也可以使用同一语法来测试可为空的值类型是否具有值,如以下示例代码所示:You can also use the same syntax for testing if a nullable value type has a value, as shown in the following sample code:

class Program
{
    static void Main(string[] args)
    {
        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(System.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(System.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;
        }
    }
}

前面的示例演示了模式匹配用于转换的其他功能。The preceding sample demonstrates other features of pattern matching to use with conversions. 可以通过专门检查 null 值来测试 NULL 模式的变量。You can test a variable for the null pattern by checking specifically for the null value. 当变量的运行时值为 null 时,用于检查类型的 is 语句始终返回 falseWhen the runtime value of the variable is null, an is statement checking for a type always returns false. 模式匹配 is 语句不允许可以为 null 值的类型,如 int?Nullable<int>,但你可以测试任何其他值类型。The pattern matching is statement doesn't allow a nullable value type, such as int? or Nullable<int>, but you can test for any other value type. 上述示例中的 is 模式不局限于可为空的值类型。The is patterns from the preceding example are not limited to the nullable value types. 也可以使用这些模式测试引用类型的变量具有值还是为 nullYou also can use those patterns to test if a variable of a reference type has a value or it's null.

前面的示例还演示如何在变量为其他类型的 switch 语句中使用模式匹配 is 表达式。The preceding sample also shows how you use the pattern matching is expression in a switch statement where the variable may be one of many different types.

如果想要测试变量是否为给定类型,但不将其分配给新变量,则可以对引用类型和可以为 null 的类型使用 isas 运算符。If you want to test if a variable is a given type, but not assign it to a new variable, you can use the is and as operators for reference types and nullable types. 以下代码演示如何在引入模式匹配以测试变量是否为给定类型前,使用 C# 语言中的 isas 语句:The following code shows how to use the is and as statements that were part of the C# language before pattern matching was introduced to test if a variable is of a given type:

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 { }


class Program
{
    static void Main(string[] args)
    {
        // Use the is operator to verify the type.
        // before performing a cast.
        Giraffe g = new Giraffe();
        UseIsOperator(g);

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

        // Use the as operator to test
        // an incompatible type.
        SuperNova sn = new SuperNova();
        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 != 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 != null)
        {
            Console.WriteLine(j);
        }
        else
        {
            Console.WriteLine("Could not convert " + val.ToString());
        }
    }
}

正如你所看到的,将此代码与模式匹配代码进行比较,模式匹配语法通过在单个语句中结合测试和赋值来提供更强大的功能。As you can see by comparing this code with the pattern matching code, the pattern matching syntax provides more robust features by combining the test and the assignment in a single statement. 尽量使用模式匹配语法。Use the pattern matching syntax whenever possible.

可通过查看 GitHub 存储库中的代码来尝试这些示例。You can try these samples by looking at the code in our GitHub repository. 也可以下载这些示例的 zip 文件Or you can download the samples as a zip file.