Mục đích của nameof là gì?


263

Phiên bản 6.0 có một tính năng mới nameof, nhưng tôi không thể hiểu mục đích của nó, vì nó chỉ lấy tên biến và thay đổi nó thành một chuỗi khi biên dịch.

Tôi nghĩ rằng nó có thể có một số mục đích khi sử dụng <T>nhưng khi tôi cố gắng nameof(T)nó chỉ in cho tôi một Tthay vì loại đã sử dụng.

Bất kỳ ý tưởng về mục đích?



28
Không có cách nào để có được điều đó Ttrước đây. Có một cách để có được loại sử dụng trước đây.
Jon Hanna

Ban đầu có vẻ như quá mức cần thiết và tôi vẫn không thấy một lý do thuyết phục nào để sử dụng nó. Có lẽ một ví dụ MVC?
Corey Alix

15
Chắc chắn hữu ích khi refactory / đổi tên tên trong nameof. Cũng giúp ngăn ngừa lỗi chính tả.
bvj

4
Tài liệu chính thức của nameof đã được chuyển đến đây: docs.microsoft.com/en-us/dotnet/csharp/lingu-reference/ trộm - Nó cũng liệt kê các trường hợp sử dụng chính phục vụ như một câu trả lời khá hay cho câu hỏi.
markus s

Câu trả lời:


323

Điều gì về các trường hợp bạn muốn sử dụng lại tên của một tài sản, ví dụ như khi ném ngoại lệ dựa trên tên thuộc tính hoặc xử lý một PropertyChangedsự kiện. Có rất nhiều trường hợp bạn muốn có tên của tài sản.

Lấy ví dụ này:

switch (e.PropertyName)
{
    case nameof(SomeProperty):
    { break; }

    // opposed to
    case "SomeOtherProperty":
    { break; }
}

Trong trường hợp đầu tiên, đổi tên SomePropertycũng sẽ thay đổi tên của tài sản hoặc nó sẽ phá vỡ quá trình biên dịch. Trường hợp cuối cùng không.

Đây là một cách rất hữu ích để giữ cho mã của bạn biên dịch và không có lỗi (sắp xếp).

(Một bài viết rất hay từ Eric Lippert tại sao infoofkhông làm nó, trong khi nameofđã làm)


1
Tôi hiểu vấn đề, chỉ cần thêm rằng chia sẻ lại thay đổi chuỗi khi tái cấu trúc tên, không chắc chắn nếu VS có chức năng tương tự.
Ash Burlaczenko

7
Nó có. Nhưng cả Resharper và VS đều không hoạt động trong các dự án chẳng hạn. Điều này không. Trong thực tế, đây là giải pháp tốt hơn.
Patrick Hofman

49
Một trường hợp sử dụng phổ biến khác là định tuyến trong MVC bằng cách sử dụng nameofvà tên của hành động thay vì một chuỗi mã hóa cứng.
RJ Cuthbertson

2
@sotn Tôi không chắc là tôi hiểu bạn đang hỏi gì. Không có gì ngăn bạn sử dụng nó như thế public class MyController { public ActionResult Index() { return View(nameof(Index)); } }- và bạn có thể sử dụng nameofcho các thành viên không tĩnh (ví dụ bạn có thể gọi nameof(MyController.Index)bằng cách sử dụng lớp ở trên và nó sẽ phát ra "Index"). Kiểm tra các ví dụ tại msdn.microsoft.com/en-us/l Library / từ
RJ Cuthbertson

2
Tôi không thấy lý do tại sao điều đó là đặc biệt. Tên biến luôn giống nhau, phải không? Cho dù bạn có cá thể hay không, tên biến sẽ không thay đổi @sotn
Patrick Hofman

176

Nó thực sự hữu ích cho ArgumentExceptionvà các dẫn xuất của nó:

public string DoSomething(string input) 
{
    if(input == null) 
    {
        throw new ArgumentNullException(nameof(input));
    }
    ...

Bây giờ nếu ai đó tái cấu trúc tên của inputtham số, ngoại lệ cũng sẽ được cập nhật.

Nó cũng hữu ích ở một số nơi mà sự phản chiếu trước đây phải được sử dụng để có được tên của các thuộc tính hoặc tham số.

Trong ví dụ của bạn nameof(T)có tên của tham số loại - điều này cũng có thể hữu ích:

throw new ArgumentException(nameof(T), $"Type {typeof(T)} does not support this method.");

Một cách sử dụng khác nameoflà cho enum - thông thường nếu bạn muốn tên chuỗi của enum bạn sử dụng .ToString():

enum MyEnum { ... FooBar = 7 ... }

Console.WriteLine(MyEnum.FooBar.ToString());

> "FooBar"

Điều này thực sự tương đối chậm vì .Net giữ giá trị enum (nghĩa là 7) và tìm thấy tên trong thời gian chạy.

Thay vào đó sử dụng nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Bây giờ .Net thay thế tên enum bằng một chuỗi tại thời gian biên dịch.


Tuy nhiên, một cách sử dụng khác là dành cho những thứ như INotifyPropertyChangedvà đăng nhập - trong cả hai trường hợp bạn muốn tên của thành viên mà bạn đang gọi sẽ được chuyển sang phương thức khác:

// Property with notify of change
public int Foo
{
    get { return this.foo; }
    set
    {
        this.foo = value;
        PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.Foo));
    }
}

Hoặc là...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}

9
Và bạn đã đặt một tính năng thú vị khác vào: nội suy chuỗi!
Patrick Hofman

1
@PatrickHofman và typeof(T), đó là một loại đường thời gian biên dịch khác hữu ích trong các trường hợp tương tự :-)
Keith

1
Một điều mà tôi đang thiếu là một cái gì đó như nameofthismethod. Bạn có thể sử dụng Log.Error($"Error in {nameof(DoSomething)}...")nhưng nếu bạn sao chép-dán nó vào các phương pháp khác, bạn sẽ không nhận thấy rằng nó vẫn đang đề cập đến DoSomething. Vì vậy, trong khi nó hoạt động hoàn hảo với các biến hoặc tham số cục bộ, tên phương thức là một vấn đề.
Tim Schmelter

3
Bạn có biết nếu nameOfsẽ sử dụng [DisplayName]thuộc tính nếu có? Ví enumdụ tôi sử dụng [DisplayName]thường xuyên với các dự án MVC
Luke T O'Brien

2
@AaronLS Vâng, nó khá chuyên biệt và không phải là thứ bạn sẽ sử dụng thường xuyên. Tuy nhiên, đó throw newlà một kiểu chống hoàn toàn khác - tôi thấy việc sử dụng quá catchmức là một vấn đề phổ biến với các nhà phát triển cơ sở vì cảm thấy như đang khắc phục vấn đề (khi hầu hết thời gian nó chỉ che giấu nó).
Keith

26

Một trường hợp sử dụng khác mà nameoftính năng của C # 6.0 trở nên tiện dụng - Hãy xem xét một thư viện như Dapper , giúp việc truy xuất DB dễ dàng hơn nhiều. Mặc dù đây là một thư viện tuyệt vời, bạn cần mã hóa tên thuộc tính / trường trong truy vấn. Điều này có nghĩa là nếu bạn quyết định đổi tên tài sản / trường của mình, có nhiều khả năng bạn sẽ quên cập nhật truy vấn để sử dụng tên trường mới. Với phép nội suy chuỗi và nameofcác tính năng, mã trở nên dễ bảo trì và an toàn hơn nhiều.

Từ ví dụ được đưa ra trong liên kết

không có tên

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });

với tên của

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });

3
Tôi yêu Dapper và tôi thực sự thích sự kết hợp chuỗi, nhưng IMO này trông rất xấu. Nguy cơ phá vỡ một truy vấn bằng cách đổi tên một cột dường như quá nhỏ so với các truy vấn xấu như vậy. Thoạt nhìn tôi muốn nói rằng tôi thích viết các truy vấn EF LINQ hoặc theo một quy ước như [Tên bảng]. [Tên cột] nơi tôi có thể dễ dàng tìm / thay thế các truy vấn của mình khi tôi cần.
Drizin

@drizin Tôi sử dụng Dapper FluentMap để ngăn chặn các truy vấn đó (và cũng để phân tách các mối quan tâm)
mamuesstack

21

Câu hỏi của bạn đã thể hiện mục đích. Bạn phải thấy điều này có thể hữu ích cho việc ghi nhật ký hoặc ném ngoại lệ.

ví dụ.

public void DoStuff(object input)
{
    if (input == null)
    {
        throw new ArgumentNullException(nameof(input));
    }
}

Điều này là tốt, nếu tôi thay đổi tên của biến, mã sẽ bị hỏng thay thế hoặc trả lại một ngoại lệ với một thông báo không chính xác .


Tất nhiên, việc sử dụng không giới hạn trong tình huống đơn giản này. Bạn có thể sử dụng nameofbất cứ khi nào sẽ hữu ích để mã tên của một biến hoặc thuộc tính.

Việc sử dụng rất đa dạng khi bạn xem xét các tình huống ràng buộc và phản ánh khác nhau. Đó là một cách tuyệt vời để mang lại những gì đã chạy lỗi thời gian để biên dịch thời gian.


6
@atikot: Nhưng sau đó, nếu bạn đổi tên biến, trình biên dịch sẽ không nhận thấy rằng chuỗi không khớp nữa.
HOẶC Mapper

1
Tôi thực sự sử dụng resharper sẽ đưa nó vào tài khoản, nhưng tôi thấy quan điểm của bạn.
atikot

4
@atikot, tôi cũng vậy, nhưng Resharper chỉ tạo cảnh báo, không phải lỗi biên dịch. Có một sự khác biệt giữa một sự chắc chắn và lời khuyên tốt.
Jodrell

1
@atikot, và, Resharper không kiểm tra thông điệp đăng nhập
Jodrell

2
@Jodrell: Và, tôi nghi ngờ, nó cũng không kiểm tra nhiều cách sử dụng khác - làm thế nào về các ràng buộc WPF được tạo trong mã phía sau, các OnPropertyChangedphương thức tùy chỉnh (chấp nhận trực tiếp tên thuộc tính thay vì PropertyChangedEventArgs) hoặc gọi phản ánh để tìm kiếm cụ thể thành viên hay loại?
HOẶC Mapper

13

Trường hợp sử dụng phổ biến nhất tôi có thể nghĩ đến là khi làm việc với INotifyPropertyChangedgiao diện. (Về cơ bản mọi thứ liên quan đến WPF và các ràng buộc đều sử dụng giao diện này)

Hãy xem ví dụ này:

public class Model : INotifyPropertyChanged
{
    // From the INotifyPropertyChanged interface
    public event PropertyChangedEventHandler PropertyChanged;

    private string foo;
    public String Foo
    {
        get { return this.foo; }
        set
        {
            this.foo = value;
            // Old code:
            PropertyChanged(this, new PropertyChangedEventArgs("Foo"));

            // New Code:
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));           
        }
    }
}

Như bạn có thể thấy theo cách cũ, chúng ta phải truyền một chuỗi để chỉ ra thuộc tính nào đã thay đổi. Với nameofchúng tôi có thể sử dụng tên của tài sản trực tiếp. Điều này có vẻ không phải là một vấn đề lớn. Nhưng hình ảnh những gì xảy ra khi ai đó thay đổi tên của tài sản Foo. Khi sử dụng một chuỗi, liên kết sẽ ngừng hoạt động, nhưng trình biên dịch sẽ không cảnh báo bạn. Khi sử dụng nameof, bạn gặp lỗi trình biên dịch không có thuộc tính / đối số với tên Foo.

Lưu ý rằng một số khung sử dụng một số phép thuật phản chiếu để có được tên của tài sản, nhưng bây giờ chúng ta có tên này không còn cần thiết nữa .


5
Mặc dù đây là một cách tiếp cận hợp lệ, nhưng cách tiếp cận (và DRY) thuận tiện hơn là sử dụng [CallerMemberName]thuộc tính trên param của phương thức mới để nâng cao sự kiện này.
Drew Noakes

1
Tôi đồng ý CallerMemberName cũng tốt, nhưng đó là trường hợp sử dụng riêng, vì (như bạn đã nói) bạn chỉ có thể sử dụng nó trong các phương thức. Đối với DRY, tôi không chắc [CallerMemberName]string x = nulllà tốt hơn nameof(Property). Bạn có thể nói tên thuộc tính được sử dụng hai lần, nhưng về cơ bản đó là đối số được truyền cho một hàm. Tôi thực sự không có ý nghĩa gì với DRY :).
Roy T.

Trên thực tế bạn có thể sử dụng nó trong tài sản. Họ cũng là thành viên. Lợi ích trên nameoflà trình thiết lập thuộc tính hoàn toàn không cần chỉ định tên thuộc tính, loại trừ khả năng sao chép / dán lỗi.
Drew Noakes

4
Đó là một tình huống 'tốt hơn cùng nhau' INotifyPropertyChanged, bằng cách sử dụng [CallerMemberNameAttribute]cho phép thông báo thay đổi được nêu rõ từ trình thiết lập thuộc tính, trong khi nameofcú pháp cho phép thông báo thay đổi được nêu rõ từ một vị trí khác trong mã của bạn.
Andrew Hanlon

9

Việc sử dụng phổ biến nhất sẽ là xác nhận đầu vào, chẳng hạn như

//Currently
void Foo(string par) {
   if (par == null) throw new ArgumentNullException("par");
}

//C# 6 nameof
void Foo(string par) {
   if (par == null) throw new ArgumentNullException(nameof(par));
}

Trong trường hợp đầu tiên, nếu bạn cấu trúc lại phương thức thay đổi tên của tham số par , có lẽ bạn sẽ quên thay đổi điều đó trong ArgumentNullException . Với tên của bạn, bạn không phải lo lắng về điều đó.

Xem thêm: nameof (C # và Visual Basic Reference)


7

Dự án ASP.NET Core MVC sử dụng nameoftrong AccountController.csManageController.csvới RedirectToActionphương thức để tham chiếu một hành động trong bộ điều khiển.

Thí dụ:

return RedirectToAction(nameof(HomeController.Index), "Home");

Điều này dịch là:

return RedirectToAction("Index", "Home");

và đưa người dùng đến hành động 'Chỉ mục' trong bộ điều khiển 'Trang chủ', nghĩa là /Home/Index.


Tại sao không đi cả con lợn và sử dụng return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000

@ Suncat2000 vì một trong những điều đó được thực hiện trong quá trình biên dịch còn những thứ khác thì không? :)
Dinerdo

6

Như những người khác đã chỉ ra, nameoftoán tử sẽ chèn tên mà phần tử được đưa ra trong mã nguồn.

Tôi muốn nói thêm rằng đây là một ý tưởng thực sự tốt về mặt tái cấu trúc vì nó làm cho chuỗi tái cấu trúc này an toàn. Trước đây, tôi đã sử dụng một phương thức tĩnh sử dụng sự phản chiếu cho cùng một mục đích, nhưng điều đó có tác động hiệu năng thời gian chạy. Các nameofnhà điều hành không có tác động hiệu suất thời gian chạy; nó thực hiện công việc của nó tại thời gian biên dịch. Nếu bạn xem MSILmã bạn sẽ tìm thấy chuỗi được nhúng. Xem phương pháp sau đây và mã tháo rời của nó.

static void Main(string[] args)
{
    Console.WriteLine(nameof(args));
    Console.WriteLine("regular text");
}

// striped nops from the listing
IL_0001 ldstr args
IL_0006 call System.Void System.Console::WriteLine(System.String)
IL_000C ldstr regular text
IL_0011 call System.Void System.Console::WriteLine(System.String)
IL_0017 ret

Tuy nhiên, đó có thể là một nhược điểm nếu bạn định làm xáo trộn phần mềm của mình. Sau khi mã hóa, chuỗi nhúng có thể không còn khớp với tên của phần tử. Các cơ chế dựa vào văn bản này sẽ phá vỡ. Ví dụ cho điều đó, bao gồm nhưng không giới hạn là: Reflection, NotifyPropertyChanged ...

Xác định tên trong thời gian chạy chi phí một số hiệu suất, nhưng an toàn cho obfuscation. Nếu obfuscation không bắt buộc và không có kế hoạch, tôi khuyên bạn nên sử dụng nameoftoán tử.


5

Hãy xem xét rằng bạn sử dụng một biến trong mã của mình và cần lấy tên của biến đó và giả sử in nó, bạn nên sử dụng

int myVar = 10;
print("myVar" + " value is " + myVar.toString());

Và nếu sau đó ai đó tái cấu trúc mã và sử dụng tên khác cho "myVar", anh ấy / cô ấy sẽ phải xem giá trị chuỗi trong mã của bạn và theo dõi nó.

Thay vào đó nếu bạn có

print(nameof(myVar) + " value is " + myVar.toString());

Nó sẽ giúp tái cấu trúc tự động!


Tôi ước rằng đã có một cú pháp tham số biến đặc biệt sẽ vượt qua một mảng các bộ dữ liệu, một cú pháp cho mỗi tham số, chứa biểu diễn mã nguồn Type, giá trị và giá trị. Điều đó sẽ làm cho mã có thể gọi các phương thức ghi nhật ký để loại bỏ rất nhiều sự dư thừa.
supercat

5

Bài viết MSDN liệt kê định tuyến MVC (ví dụ thực sự nhấp vào khái niệm cho tôi) trong số nhiều người khác. Đoạn mô tả (được định dạng) đọc:

  • Khi báo cáo lỗi trong mã,
  • kết nối các liên kết model-view-controller (MVC),
  • bắn tài sản thay đổi sự kiện, vv

bạn thường muốn nắm bắt tên chuỗi của một phương thức . Sử dụng nameof giúp giữ mã của bạn hợp lệ khi đổi tên định nghĩa.

Trước khi bạn phải sử dụng chuỗi ký tự chuỗi để chỉ các định nghĩa, điều này dễ vỡ khi đổi tên các thành phần mã bởi vì các công cụ không biết để kiểm tra các chuỗi ký tự này.

Các câu trả lời được chấp nhận / đánh giá cao nhất đã đưa ra một số ví dụ cụ thể xuất sắc.


3

Mục đích của nameof nhà điều hành là cung cấp tên nguồn của các vật phẩm.

Thông thường tên nguồn là cùng tên với tên siêu dữ liệu:

public void M(string p)
{
    if (p == null)
    {
        throw new ArgumentNullException(nameof(p));
    }
    ...
}

public int P
{
    get
    {
        return p;
    }
    set
    {
        p = value;
        NotifyPropertyChanged(nameof(P));
    }
}

Nhưng điều này có thể không phải luôn luôn như vậy:

using i = System.Int32;
...
Console.WriteLine(nameof(i)); // prints "i"

Hoặc là:

public static string Extension<T>(this T t)
{
    return nameof(T); returns "T"
}

Một cách sử dụng mà tôi đã đưa ra cho nó là để đặt tên tài nguyên:

[Display(
    ResourceType = typeof(Resources),
    Name = nameof(Resources.Title_Name),
    ShortName = nameof(Resources.Title_ShortName),
    Description = nameof(Resources.Title_Description),
    Prompt = nameof(Resources.Title_Prompt))]

Thực tế là, trong trường hợp này, tôi thậm chí không cần các thuộc tính được tạo để truy cập tài nguyên, nhưng bây giờ tôi có một thời gian biên dịch kiểm tra xem các tài nguyên có tồn tại không.


0

Một trong những cách sử dụng nameoftừ khóa là để thiết lập Bindingtrong wpf theo chương trình .

để đặt Bindingbạn phải đặt Pathbằng chuỗi và với nameoftừ khóa, có thể sử dụng tùy chọn Refactor.

Ví dụ: nếu bạn có IsEnablethuộc tính phụ thuộc trong bạn UserControlvà bạn muốn liên kết nó với IsEnablemột số CheckBoxtrong bạn UserControl, bạn có thể sử dụng hai mã này:

CheckBox chk = new CheckBox();
Binding bnd = new Binding ("IsEnable") { Source = this };
chk.SetBinding(IsEnabledProperty, bnd);

CheckBox chk = new CheckBox();
Binding bnd = new Binding (nameof (IsEnable)) { Source = this };
chk.SetBinding(IsEnabledProperty, bnd);

Rõ ràng là mã đầu tiên không thể cấu trúc lại nhưng mã ...


0

Trước đây chúng tôi đã sử dụng một cái gì đó như thế:

// Some form.
SetFieldReadOnly( () => Entity.UserName );
...
// Base form.
private void SetFieldReadOnly(Expression<Func<object>> property)
{
    var propName = GetPropNameFromExpr(property);
    SetFieldsReadOnly(propName);
}

private void SetFieldReadOnly(string propertyName)
{
    ...
}

Lý do - biên dịch an toàn thời gian. Không ai có thể âm thầm đổi tên tài sản và phá vỡ logic mã. Bây giờ chúng ta có thể sử dụng nameof ().


0

Nó có lợi thế khi bạn sử dụng ASP.Net MVC. Khi bạn sử dụng trình trợ giúp HTML để xây dựng một số điều khiển trong chế độ xem, nó sẽ sử dụng tên thuộc tính để xác định tên của đầu vào html:

@Html.TextBoxFor(m => m.CanBeRenamed)

Nó làm một cái gì đó như thế:

<input type="text" name="CanBeRenamed" />

Vì vậy, bây giờ, nếu bạn cần xác thực tài sản của mình trong phương thức Xác thực, bạn có thể thực hiện việc này:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
  if (IsNotValid(CanBeRenamed)) {
    yield return new ValidationResult(
      $"Property {nameof(CanBeRenamed)} is not valid",
      new [] { $"{nameof(CanBeRenamed)}" })
  }
}

Trong trường hợp nếu bạn đổi tên tài sản của mình bằng các công cụ tái cấu trúc, xác thực của bạn sẽ không bị phá vỡ.


0

Một trường hợp sử dụng khác nameoflà kiểm tra các trang tab, thay vì kiểm tra chỉ mục, bạn có thể kiểm tra thuộc Nametính của các trang tab như sau:

if(tabControl.SelectedTab.Name == nameof(tabSettings))
{
    // Do something
}

Ít lộn xộn hơn :)


0

Tôi thấy rằng nameoflàm tăng khả năng đọc của các câu lệnh SQL rất dài và phức tạp trong các ứng dụng của tôi. Nó làm cho các biến đứng ngoài biển chuỗi đó và loại bỏ công việc của bạn để tìm ra nơi các biến được sử dụng trong các câu lệnh SQL của bạn.

public bool IsFooAFoo(string foo, string bar)
{
    var aVeryLongAndComplexQuery = $@"SELECT yada, yada
    -- long query in here
    WHERE fooColumn = @{nameof(foo)}
    AND barColumn = @{nameof(bar)}
    -- long query here";


    SqlParameter[] parameters = {
        new SqlParameter(nameof(foo), SqlDBType.VarChar, 10){ Value = foo },
        new SqlParameter(nameof(bar), SqlDBType.VarChar, 10){ Value = bar },
    }
}
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.