Các tính năng mới của C# 7.x

Khác với các phiên bản C# trước đây - mỗi phiên bản chỉ phát hành một lần duy nhất. Kể từ phiên bản C# 7 Microsoft đã có những thay đổi để tăng tốc độ phát hành.

Sau khi phát hành phiên bản chính là C# 7.0, Microsoft tiếp tục phát hành thêm các phiên bản như C# 7.1, C# 7.2 và C# 7.3 để bổ sung thêm các tính năng mới.

Nếu bạn bỏ lỡ bài viết về các tính năng mới của C# 7.0 thì có thể xem tại đây:

Các tính năng mới của C# 7.0 | Comdy
Các tính năng mới của C# 7.0 như biến out, tuple, discards, pattern matching, expression-bodied member, ...

Các tính năng mới của C# 7.1

C# 7.1 là bản phát hành bổ sung đầu tiên cho ngôn ngữ C#. Nó đánh dấu một nhịp phát hành nhanh cho ngôn ngữ.

Bạn có thể sử dụng các tính năng mới sớm hơn, lý tưởng nhất là mỗi khi có tính năng mới đã sẵn sàng.

C# 7.1 thêm khả năng cấu hình trình biên dịch để khớp với phiên bản ngôn ngữ đã chỉ định. Điều đó cho phép bạn tách việc nâng cấp các công cụ phát triển ra khỏi việc nâng cấp các phiên bản ngôn ngữ.

C# 7.1 thêm thành phần cấu hình lựa chọn phiên bản ngôn ngữ, ba tính năng ngôn ngữ mới và hành vi trình biên dịch mới.

Các tính năng ngôn ngữ mới trong phiên bản này là:

  • Phương thức Main không đồng bộ.
  • Biểu thức default.
  • Tên phần tử của tuple.
  • Khớp mẫu trên các tham số kiểu generic.

Phương thức Main không đồng bộ

Phương thức Main không đồng bộ cho phép bạn sử dụng await trong phương thức Main của mình. Trước đây bạn sẽ cần phải viết:

static int Main()
{
    return DoAsyncWork().GetAwaiter().GetResult();
}

Bây giờ bạn có thể viết:

static async Task<int> Main()
{
    // This could also be replaced with the body
    // DoAsyncWork, including its await expressions:
    return await DoAsyncWork();
}

Nếu chương trình của bạn không trả về mã thoát (exit code), bạn có thể khai báo một phương thức Main trả về Task như sau:

static async Task Main()
{
    await SomeAsyncMethod();
}

Biểu thức default

Trước đây bạn sẽ sử dụng biểu thức default để khai báo giá trị mặc định như sau:

Func<string, bool> whereClause = default(Func<string, bool>);

Bây giờ bạn có thể bỏ qua phần khai báo kiểu dữ liệu ở phía bên phải của khởi tạo giá trị mặc định như sau:

Func<string, bool> whereClause = default;

Tên phần tử của tuple

Tính năng này là một cải tiến nhỏ cho tuple được giới thiệu trong C# 7.0. Nhiều khi bạn khởi tạo một tuple, các biến được sử dụng cho phía bên phải của phép gán giống như tên bạn muốn cho các phần tử tuple như sau:

int count = 5;
string label = "Colors used in the map";
var pair = (count: count, label: label);

Trong C# 7.1 tên của các phần tử tuple có thể được suy ra từ các biến được sử dụng để khởi tạo tuple:

int count = 5;
string label = "Colors used in the map";
var pair = (count, label); // element names are "count" and "label"

Khớp mẫu trên các tham số kiểu generic

Bắt đầu từ phiên bản C# 7.1, khớp mẫu cho biểu thức isswitch hỗ trợ tham số kiểu generic. Điều này rất hữu ích khi kiểm tra các kiểu struct hoặc class và bạn muốn tránh việc sử dụng boxing và unboxing.

Các tính năng mới của C# 7.2

C# 7.2 là một bản phát hành bổ sung khác có thêm một số tính năng hữu ích. Phiên bản này hoạt động hiệu quả hơn với các kiểu giá trị bằng cách tránh các bản sao hoặc phân bổ bộ nhớ không cần thiết.

Các tính năng ngôn ngữ mới trong phiên bản C# 7.2 là:

  • Cải tiến hiệu năng
  • Thứ tự các đối số được đặt tên
  • Dấu gạch dưới trong chữ số
  • Chỉ thị truy cập private protected
  • Biểu thức điều kiện ref

Cải tiến hiệu năng

Các tính năng ngôn ngữ được giới thiệu trong phiên bản C# 7.2 cho phép bạn làm việc với các kiểu giá trị trong khi sử dụng như kiểu tham chiếu.

Chúng được thiết kế để tăng hiệu suất bằng cách giảm thiểu việc sao chép các kiểu giá trị mà không phát sinh phân bổ bộ nhớ liên quan đến việc sử dụng các kiểu tham chiếu. Các tính năng bao gồm:

  • Sử dụng từ khóa in cho các tham số để chỉ định rằng một đối số được truyền bằng tham chiếu nhưng không được thay đổi giá trị trong phương thức.
  • Ở phiên bản C# 7.0 đã giới thiệu tính năng trả về ref. C# 7.2 bổ sung thêm cho tính năng này đó là sử dụng khai báo ref readonly trên phương thức để chỉ ra rằng phương thức trả về giá trị của nó bằng tham chiếu nhưng không cho phép ghi vào đối tượng đó.
  • Sử dụng khai báo readonly struct để cho biết rằng một struct là không thay đổi và có thể được sử dụng làm một tham số in cho phương thức.
  • Khai báo ref struct để cho biết rằng một struct không thể là thành viên của một lớp hoặc được sử dụng ở những nơi mà nó có thể được phân bổ trên bộ nhớ heap. Một ref struct phải luôn luôn được phân bổ trên bộ nhớ stack.

Thứ tự các đối số được đặt tên

Từ C# 7.2 bạn có thể gọi phương thức với các đối số được đặt tên ở vị trí bất kỳ, không cần phải tuân theo thứ tự được khai báo trong phương thức.

class NamedExample
{
    static void Main(string[] args)
    {
        // The method can be called in the normal way, by using positional arguments.
        PrintOrderDetails("Gift Shop", 31, "Red Mug");

        // Named arguments can be supplied for the parameters in any order.
        PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");
        PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);

        // Named arguments mixed with positional arguments are valid
        // as long as they are used in their correct position.
        PrintOrderDetails("Gift Shop", 31, productName: "Red Mug");
        PrintOrderDetails(sellerName: "Gift Shop", 31, productName: "Red Mug");    // C# 7.2 onwards
        PrintOrderDetails("Gift Shop", orderNum: 31, "Red Mug");
    }

    static void PrintOrderDetails(string sellerName, int orderNum, string productName)
    {
        if (string.IsNullOrWhiteSpace(sellerName))
        {
            throw new ArgumentException(message: "Seller name cannot be null or empty.", paramName: nameof(sellerName));
        }

        Console.WriteLine($"Seller: {sellerName}, Order #: {orderNum}, Product: {productName}");
    }
}

Dấu gạch dưới trong chữ số

C# 7.0 không hỗ trợ sử dụng ký tự _ để tách các chữ số. Tuy nhiên từ C# 7.2 đã hỗ trợ như ví dụ dưới đây:

// decimal notation
var balance = 2_435_951.68;

// hexadecimal notation
var num = 0x01_00;

// binary notation
int binaryValue = 0b_0101_0101;

Chỉ thị truy cập private protected

Một chỉ thị truy cập hỗn hợp mới: private protected chỉ ra rằng một thành viên có thể được truy cập bằng cách chứa các lớp hoặc các lớp dẫn xuất được khai báo trong cùng một assembly.

Trong khi protected internal cho phép truy cập bởi các lớp dẫn xuất hoặc các lớp trong cùng một assembly, private protected giới hạn truy cập vào các kiểu dẫn xuất được khai báo trong cùng một assembly.

Biểu thức điều kiện ref

Cuối cùng, biểu thức điều kiện có thể tạo ra kết quả ref thay vì kết quả giá trị. Ví dụ: bạn có thể viết như sau để lấy tham chiếu đến phần tử đầu tiên trong một trong hai mảng:

ref var r = ref (arr != null ? ref arr[0] : ref otherArr[0]);

Biến r là một tham chiếu đến giá trị đầu tiên trong một arr hoặc otherArr.

Các tính năng mới của C# 7.3

Ở bản phát hành C# 7.3, Microsoft đã cung cấp các tính năng mã an toàn có thể hoạt động như mã không an toàn (unsafe code) và các cải tiến cho các tính năng hiện có.

Ở phần này mình chỉ tập trung vào những cải tiến cho các tính năng hiện có:

  • Tuple hỗ trợ toán tử == và toán tử !=.
  • Đính kèm attribute vào trường sao lưu cho thuộc tính được triển khai tự động.
  • Quá tải phương thức in.
  • Mở rộng các biến biểu thức trong trình khởi tạo.

Tuple hỗ trợ toán tử == và toán tử !=

Bắt đầu với C# 7.3, tuple hỗ trợ các toán tử == và toán tử !=. Các toán tử này hoạt động bằng cách so sánh từng thành viên của đối số bên trái với từng thành viên của đối số bên phải theo thứ tự. Chúng sẽ ngừng đánh giá các thành viên ngay khi một cặp không bằng nhau. Ví dụ:

var left = (a: 5, b: 10);
var right = (a: 5, b: 10);
Console.WriteLine(left == right); // displays 'true'

(int a, int b)? nullableTuple = right;
Console.WriteLine(left != nullableTuple); // displays 'false'

Đính kèm attribute vào trường sao lưu cho thuộc tính được triển khai tự động

Trường sao lưu tự động là trường được trình biên dịch tạo ra cho thuộc tính được triển khai tự động (auto-implemented properties). Chúng ta sẽ không thấy được các trường sao lưu này.

Bắt đầu từ C# 7.3, các attribute có thể được gắn vào trường sao lưu được tạo bởi trình biên dịch cho các thuộc tính được triển khai tự động.

Ví dụ, lớp Person có một thuộc tính được triển khai tự động là Id, nhưng thiết kế của bạn không yêu cầu duy trì thuộc tính Id. NonSerializedAttribute chỉ có thể được sử dụng cho các trường, không sử dụng cho thuộc tính.

Bạn có thể đính kèm NonSerializedAttribution vào trường sao lưu cho thuộc tính Id bằng cách sử dụng khai báo field: trên thuộc tính, như trong ví dụ sau:

public class Person
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    [field:NonSerialized]
    public int Id { get; set; }

    public string FullName => $"{FirstName} {LastName}";
}

Quá tải phương thức in

Khi từ khóa in được thêm vào đối số, hai phương thức này sẽ gây ra sự mơ hồ:

static void M(S arg);
static void M(in S arg);

Bây giờ, để gọi phiên bản với đối số tham chiếu chỉ đọc (dòn thứ hai), bạn phải sử dụng từ khóa in khi gọi phương thức.

Mở rộng các biến biểu thức trong trình khởi tạo

Cú pháp khai báo biến out được thêm vào trong C# 7.0 đã được mở rộng cho trình khởi tạo trường, trình khởi tạo phương thức và phương thức khởi tạo và mệnh đề truy vấn. Nó cho phép mã như ví dụ sau:

public class B
{
   public B(int i, out int j)
   {
      j = i;
   }
}

public class D : B
{
   public D(int i) : base(i, out var j)
   {
      Console.WriteLine($"The value of 'j' is {j}");
   }
}
Lập Trình C#Tính năng mới của C#
Bài Viết Liên Quan:
int[] và int[,] trong C#: Ai nhanh hơn
Trung Nguyen 10/10/2020
int[] và int[,] trong C#: Ai nhanh hơn

Hiểu được sự khác biệt giữa các loại mảng trong C# sẽ giúp bạn chọn cấu trúc dữ liệu chính xác cho mọi trường hợp.

Struct và class trong C#: Ai nhanh hơn
Trung Nguyen 09/10/2020
Struct và class trong C#: Ai nhanh hơn

Trong bài viết này, tôi sẽ so sánh sự khác biệt về hiệu suất giữa struct và class trong C#: Ai nhanh hơn.

Best practice cho performance trong C#
Trung Nguyen 03/10/2020
Best practice cho performance trong C#

Mục tiêu của bài viết này là cung cấp một danh sách không đầy đủ các code mẫu cần tránh, vì chúng rủi ro hoặc performance kém.

Đọc ghi file (File I/O) trong C#
Trung Nguyen 26/04/2020
Đọc ghi file (File I/O) trong C#

Hướng dẫn này sẽ giúp bạn tìm hiểu về đọc ghi file (File I/O) trong C# và sử dụng các lớp tiện ích để đọc ghi file.