Nameof () có được đánh giá tại thời điểm biên dịch không?


114

Trong C # 6, bạn có thể sử dụng nameof()toán tử để lấy một chuỗi chứa tên của một biến hoặc một kiểu.

Điều này được đánh giá tại thời điểm biên dịch hay trong thời gian chạy thông qua một số API Roslyn?


Roslyn là nền tảng trình biên dịch mới. Nó chỉ được sử dụng tại thời điểm biên dịch.
Paulo Morgado

2
@PauloMorgado điều đó không đúng, bạn có thể sử dụng Rosyln trong thời gian chạy để thực hiện mọi việc. Chẳng hạn như việc xây dựng một trình biên tập mã trực tiếp hoặc sử dụng công cụ phân tích cú pháp Rosyln để làm việc với cây hoặc biểu thức hoặc một cái gì đó
Chris Marisic

@ChrisMarisic đó là ấn tượng của tôi, nhưng tôi đã không trả lời vì kiến ​​thức của tôi về chủ đề này còn hạn chế (do đó câu hỏi của tôi). Tôi đã bắt gặp điều này: scriptcs.net , đây là một ví dụ khá tốt về sức mạnh của Roslyn và tôi tin rằng nó có nội dung về thời gian chạy, nhưng tôi có thể đã nhầm vì tôi không hiểu rõ về nó.
Gigi

@ChrisMarisic, vì vậy, những gì bạn đang nói là bạn có thể sử dụng Roslyn để tạo mã trực tiếp từ nguồn chứ không phải từ một tệp nhị phân đang chạy. Và bạn vẫn đang sử dụng Roslyn để chuyển đổi nguồn thành các mã nhị phân mà sẽ không sử dụng Roslyn để thay đổi thos binries. Nếu bạn không thể hoàn toàn sử dụng Roslyn trong thời gian chạy, thì bạn không bao giờ có thể biên dịch bất kỳ mã nào.
Paulo Morgado

Câu trả lời:


119

Đúng. nameof()được đánh giá tại thời điểm biên dịch. Nhìn vào phiên bản mới nhất của thông số kỹ thuật:

Biểu thức tên là một hằng số. Trong mọi trường hợp, nameof (...) được đánh giá tại thời điểm biên dịch để tạo ra một chuỗi. Đối số của nó không được đánh giá trong thời gian chạy và được coi là mã không thể truy cập (tuy nhiên nó không phát ra cảnh báo "mã không thể truy cập").

Từ toán tử nameof - v5

Bạn có thể thấy điều đó với ví dụ TryRoslyn này, trong đó:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(nameof(Foo));
    }
}

Được biên dịch và dịch ngược thành:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine("Foo");
    }
}

Thời gian chạy tương đương của nó là:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(typeof(Foo).Name);
    }
}

Như đã đề cập trong các nhận xét, điều đó có nghĩa là khi bạn sử dụng nameoftham số kiểu trong một kiểu chung, đừng mong đợi nhận được tên của kiểu động thực tế được sử dụng làm tham số kiểu thay vì chỉ tên của tham số kiểu. Vì vậy, điều này:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine(nameof(T));
    }
}

Sẽ trở thành thế này:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}

"Thời gian biên dịch" ở đây là gì? Biên dịch sang MSIL hay biên dịch sang mã gốc?
user541686.

6
@Mehrdad Trình biên dịch C # tạo IL.
i3arnon

3
Câu hỏi nhanh, tôi có thể sử dụng nameof trong trường hợp chuyển mạch không?
vần vào

2
@Spell
i3arnon

58

Tôi muốn làm phong phú thêm câu trả lời do @ I3arnon cung cấp với bằng chứng rằng nó được đánh giá tại thời điểm biên dịch.

Giả sử tôi muốn in tên của một biến trong Bảng điều khiển bằng nameoftoán tử:

 var firstname = "Gigi";
 var varname = nameof(firstname);
 Console.WriteLine(varname); // Prints "firstname" to the console

Khi bạn kiểm tra MSIL được tạo, bạn sẽ thấy rằng nó tương đương với một khai báo chuỗi vì một tham chiếu đối tượng đến một chuỗi được đẩy vào ngăn xếp bằng cách sử dụng ldstrtoán tử:

IL_0001: ldstr "Gigi"
IL_0006: stloc.0
IL_0007: ldstr "firstname"
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: call void [mscorlib]System.Console::WriteLine(string)

Bạn sẽ nhận thấy rằng việc khai báo chuỗi tên đầu tiên và sử dụng nameoftoán tử sẽ tạo ra cùng một mã trong MSIL, có nghĩa nameoflà hiệu quả như khai báo một biến chuỗi.


4
Nếu MSIL được dịch ngược thành mã nguồn, trình dịch ngược sẽ dễ dàng như thế nào để nhận ra đó là một nameoftoán tử, không phải là một chuỗi mã cứng thuần túy?
ADTC

11
Đó là một câu hỏi hay! bạn có thể đăng nó dưới dạng một câu hỏi mới trên SO nếu bạn muốn được giải thích chi tiết :) .. tuy nhiên câu trả lời ngắn gọn là trình dịch ngược sẽ không thể tìm ra đó là toán tử tên, mà sẽ sử dụng một chuỗi ký tự thay thế . Tôi đã xác minh đó là trường hợp của ILSpy và Reflector.
Faris Zacina

2
@ADTC: Vì nameof được thay thế hoàn toàn bằng load-a-string-on-the-stack, làm thế nào mà trình dịch ngược lại có thể cố gắng đoán đó là tên của nó chứ không phải là một tham số hằng đơn giản?
quetzalcoatl

2
Điều đó thật thú vị. Có lẽ trình dịch ngược có thể kiểm tra chuỗi so với ngữ cảnh hiện tại (tên của phương thức / thuộc tính / v.v. bạn đang sử dụng). Tuy nhiên, không có cách nào để nó đáng tin cậy 100% - dù gì thì bạn cũng có thể đã sử dụng một chuỗi mã cứng.
Gigi

2
Mặc dù tôi đồng ý rằng bạn không thể biết liệu đó có phải là tên của sau khi biên dịch hay không, nhưng tôi không thấy bất kỳ dấu hiệu nào cho thấy ILSpy hoặc Reflector hỗ trợ C # 6. Nếu thats trường hợp, bạn không thể kiểm tra nó @TheMinister
Millie Smith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.