Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Từ khóa is trong C#: Kiểm tra kiểu và Pattern Matching

Tổng quan

Từ khóa is dùng để kiểm tra kiểu (type checking) và pattern matching. Trả về bool.

1. Type Checking Cơ bản


object obj = "Hello";

if (obj is string) 
{
    Console.WriteLine("Là string");
}

// Kết hợp với declaration pattern (C# 7+)
if (obj is string s) 
{
    Console.WriteLine($"String length: {s.Length}"); // s có scope trong if block
}

2. Pattern Matching Nâng cao (C# 7+)

Declaration Pattern


object obj = 42;

if (obj is int i) 
{
    Console.WriteLine($"Int: {i * 2}"); // 84
}

// Scope: biến 'i' chỉ tồn tại trong if block

Constant Pattern


object obj = 5;

if (obj is 5) 
{
    Console.WriteLine("Equals 5");
}

if (obj is null) 
{
    Console.WriteLine("Is null");
}

Var Pattern (Luôn true, gán vào biến)


object obj = "Test";

if (obj is var x) 
{
    Console.WriteLine(x.GetType().Name); // String
}
// Tương đương: var x = obj;

Property Pattern (C# 8+)


class Person 
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Address Address { get; set; }
}

class Address 
{
    public string City { get; set; }
}

var person = new Person { Name = "John", Age = 25, Address = new Address { City = "Hanoi" } };

// Kiểm tra property
if (person is { Age: >= 18, Name: "John" }) 
{
    Console.WriteLine("Adult John");
}

// Nested property pattern
if (person is { Address: { City: "Hanoi" } }) 
{
    Console.WriteLine("From Hanoi");
}

Tuple Pattern (C# 8+)


var point = (X: 10, Y: 20);

if (point is (10, 20)) 
{
    Console.WriteLine("Origin point");
}

if (point is (var x, var y)) 
{
    Console.WriteLine($"({x}, {y})");
}

Type Pattern với When Clause (C# 7+)


object obj = "Hello";

if (obj is string s && s.Length > 5) // C# 7: is + when
if (obj is string s when s.Length > 5) // C# 7 syntax
{
    Console.WriteLine($"Long string: {s}");
}

3. is not Pattern (C# 9+)


object obj = "Hello";

if (obj is not null) 
{
    Console.WriteLine("Not null");
}

if (obj is not string) 
{
    Console.WriteLine("Not a string");
}

// Kết hợp
if (obj is string s and not null) 
{
    // s guaranteed non-null string
}

4. Switch Expression với is (C# 8+)


object obj = 42;

string result = obj switch 
{
    int i when i > 0 => $"Positive int: {i}",
    int i when i < 0 => $"Negative int: {i}",
    int _ => "Zero",
    string s => $"String: {s}",
    null => "Null",
    _ => "Unknown"
};

Console.WriteLine(result); // Positive int: 42

5. is vs as vs GetType()

Toán tửMục đíchTrả vềNull handling
isKiểm tra kiểu + pattern matchingboolAn toàn (false nếu null)
asSafe castType hoặc nullTrả null nếu fail
GetType()Lấy Type runtime chính xácType objectThrow nếu null
typeof()Lấy Type compile-timeType objectStatic

// is - kiểm tra
if (obj is string s) { /* use s */ }

// as - cast an toàn
string s = obj as string;
if (s != null) { /* use s */ }

// GetType - exact type (không tính inheritance)
if (obj.GetType() == typeof(string)) { } // Chỉ true nếu chính xác string

// typeof - compile-time type
if (typeof(string).IsAssignableFrom(obj.GetType())) { } // Polymorphic check

6. Generic Type Checking


public void Process(T item) 
{
    // Kiểm tra generic type
    if (item is IEnumerable intList) 
    {
        Console.WriteLine($"List of ints: {intList.Count()}");
    }
    
    // Kiểm tra interface
    if (item is IDisposable disposable) 
    {
        disposable.Dispose();
    }
}

7. Performance Tips

  • is với pattern matching compile thành code hiệu quả (không double-check).
  • Tránh obj.GetType() == typeof(T) trong hot path - dùng is T.
  • as + null check nhanh hơn is + cast riêng biệt (trước C# 7). Từ C# 7, is T var tối ưu tương đương.

Ví dụ thực tế: Visitor Pattern với Pattern Matching


abstract class Shape { }

class Circle : Shape { public double Radius { get; set; } }
class Rectangle : Shape { public double Width { get; set; } public double Height { get; set; } }
class Triangle : Shape { public double Base { get; set; } public double Height { get; set; } }

double GetArea(Shape shape) => shape switch 
{
    Circle c => Math.PI * c.Radius * c.Radius,
    Rectangle r => r.Width * r.Height,
    Triangle t => 0.5 * t.Base * t.Height,
    _ => throw new ArgumentException("Unknown shape")
};