Apa tujuan dari nama?

263

Versi 6.0 mendapat fitur baru nameof , tetapi saya tidak dapat memahami tujuannya, karena hanya mengambil nama variabel dan mengubahnya ke string pada kompilasi.

Saya pikir itu mungkin memiliki beberapa tujuan ketika menggunakan <T>tetapi ketika saya mencoba nameof(T)hanya mencetak sayaT bukan tipe yang digunakan.

Ada ide tentang tujuannya?

atikot
sumber
28
Tidak ada cara untuk mendapatkan itu Tsebelumnya. Ada cara untuk mendapatkan jenis yang digunakan sebelumnya.
Jon Hanna
Awalnya sepertinya terlalu banyak dan saya masih belum melihat alasan kuat untuk menggunakannya. Mungkin contoh MVC?
Corey Alix
15
Sangat berguna saat memperbaiki / mengganti nama nama di dalamnya nameof. Juga membantu mencegah kesalahan ketik.
bvj
4
Dokumentasi resmi nameof telah pindah ke sini: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - Ini juga mencantumkan case use utama yang berfungsi sebagai jawaban yang cukup bagus untuk pertanyaan tersebut.
markus s

Jawaban:

323

Bagaimana dengan kasus-kasus di mana Anda ingin menggunakan kembali nama properti, misalnya saat melempar pengecualian berdasarkan nama properti, atau menangani suatu PropertyChangedperistiwa. Ada banyak kasus di mana Anda ingin memiliki nama properti.

Ambil contoh ini:

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

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

Dalam kasus pertama, penggantian nama SomeProperty akan mengubah nama properti, atau akan merusak kompilasi. Kasus terakhir tidak.

Ini adalah cara yang sangat berguna untuk menjaga kompilasi kode Anda dan bebas bug (sort-of).

(Artikel yang sangat bagus dari Eric Lippert mengapa infooftidak membuatnya, sementara nameofitu)

Patrick Hofman
sumber
1
Saya mengerti intinya, hanya menambahkan bahwa resharper mengubah string ketika refactoring nama, tidak yakin apakah VS memiliki fungsi yang sama.
Ash Burlaczenko
7
Memiliki. Tetapi baik Resharper dan VS tidak bekerja pada proyek misalnya. Ini tidak. Sebenarnya, ini adalah solusi yang lebih baik.
Patrick Hofman
49
Kasus penggunaan umum lainnya adalah perutean dalam penggunaan MVC nameofdan nama tindakan alih-alih string yang dikodekan keras.
RJ Cuthbertson
2
@ Sotn Saya tidak yakin saya mengerti apa yang Anda minta. Tidak ada yang menghentikan Anda dari menggunakannya public class MyController { public ActionResult Index() { return View(nameof(Index)); } }- dan Anda dapat menggunakan nameofanggota non-statis (misalnya Anda dapat memanggil nameof(MyController.Index)menggunakan kelas di atas dan itu akan mengeluarkan "Indeks"). Lihatlah contoh-contohnya di msdn.microsoft.com/en-us/library/…
RJ Cuthbertson
2
Saya tidak mengerti mengapa itu istimewa. Nama variabel selalu sama, bukan? Apakah Anda memiliki instance atau tidak, nama variabel tidak akan berubah @ sotn
Patrick Hofman
176

Ini sangat berguna untuk ArgumentExceptiondan turunannya:

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

Sekarang jika seseorang refactor nama inputparameter, pengecualian akan selalu diperbarui.

Ini juga berguna di beberapa tempat di mana refleksi sebelumnya harus digunakan untuk mendapatkan nama properti atau parameter.

Dalam contoh Anda nameof(T)mendapatkan nama parameter tipe - ini dapat berguna juga:

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

Penggunaan lain nameofadalah untuk enum - biasanya jika Anda ingin nama string enum yang Anda gunakan .ToString():

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

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

> "FooBar"

Ini sebenarnya relatif lambat karena .Net memegang nilai enum (yaitu 7) dan menemukan nama pada saat run time.

Alih-alih gunakan nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Sekarang .Net menggantikan nama enum dengan string pada waktu kompilasi.


Namun kegunaan lain adalah untuk hal-hal seperti INotifyPropertyChangeddan masuk - dalam kedua kasus Anda ingin nama anggota yang Anda panggil diteruskan ke metode lain:

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

Atau...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}
Keith
sumber
9
Dan Anda telah memasukkan fitur keren lainnya: interpolasi string!
Patrick Hofman
1
@ Patrickrickman dan typeof(T), yang merupakan bagian lain dari waktu kompilasi gula yang berguna dalam situasi yang sama :-)
Keith
1
Satu hal yang saya lewatkan adalah sesuatu seperti nameofthismethod. Anda dapat menggunakan Log.Error($"Error in {nameof(DoSomething)}...")tetapi jika Anda menyalin-menempel ini ke metode lain Anda tidak akan melihat bahwa itu masih mengacu pada DoSomething. Jadi sementara itu bekerja dengan sempurna dengan variabel atau parameter lokal, nama metode adalah masalah.
Tim Schmelter
3
Apakah Anda tahu apakah nameOfakan menggunakan [DisplayName]atribut jika ada? Sebagai enumcontoh saya [DisplayName]sering menggunakan proyek
Luke T O'Brien
2
@ AaronLS Ya, ini cukup khusus dan bukan sesuatu yang sering Anda gunakan. Namun itu throw newadalah anti-pola lain - saya menemukan penggunaan berlebihan catchmenjadi masalah umum dengan junior devs karena rasanya seperti memperbaiki masalah (ketika sebagian besar waktu hanya menyembunyikannya).
Keith
26

Kasus penggunaan lain di mana nameoffitur C # 6.0 menjadi praktis - Pertimbangkan perpustakaan seperti Dapper yang membuat pengambilan DB jauh lebih mudah. Meskipun ini adalah pustaka yang hebat, Anda perlu meng-hardcode nama properti / bidang dalam kueri. Ini artinya bahwa jika Anda memutuskan untuk mengubah nama properti / bidang Anda, ada kemungkinan besar Anda akan lupa memperbarui kueri untuk menggunakan nama bidang baru. Dengan interpolasi string dan nameoffitur, kode menjadi lebih mudah dipelihara dan disimpan dengan aman.

Dari contoh yang diberikan dalam tautan

tanpa nama

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

dengan nama

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });
sateesh
sumber
3
Saya suka Dapper dan saya sangat suka string interporlations, tapi IMO ini terlihat sangat jelek. Risiko memecah kueri dengan mengganti nama kolom tampak sangat kecil dibandingkan dengan kueri yang jelek. Pada pandangan pertama saya akan mengatakan saya lebih suka menulis kueri EF LINQ, atau mengikuti konvensi seperti [TableName]. [ColumnName] di mana saya dapat dengan mudah menemukan / mengganti permintaan saya saat saya membutuhkan.
drizin
@drizin Saya menggunakan Dapper FluentMap untuk mencegah pertanyaan seperti itu (dan juga untuk pemisahan masalah)
mamuesstack
21

Pertanyaan Anda sudah menyatakan tujuannya. Anda harus melihat ini mungkin berguna untuk mencatat atau melempar pengecualian.

sebagai contoh.

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

ini bagus, jika saya mengubah nama variabel, kode akan rusak atau mengembalikan pengecualian dengan pesan yang salah .


Tentu saja, penggunaannya tidak terbatas pada situasi sederhana ini. Anda dapat menggunakan nameofkapan saja akan berguna untuk kode nama variabel atau properti.

Penggunaannya bermacam-macam ketika Anda mempertimbangkan berbagai situasi ikatan dan refleksi. Ini cara yang sangat baik untuk membawa kesalahan waktu menjalankan untuk mengkompilasi waktu.

Jodrell
sumber
6
@atikot: Tapi kemudian, jika Anda mengganti nama variabel, kompiler tidak akan melihat bahwa string tidak cocok lagi.
ATAU Mapper
1
Saya sebenarnya menggunakan resharper yang memperhitungkannya, tetapi saya mengerti maksud Anda.
atikot
4
@atikot, begitu juga saya, tetapi Resharper hanya menghasilkan peringatan, bukan kesalahan kompiler. Ada perbedaan antara kepastian dan saran yang bagus.
Jodrell
1
@atikot, dan, Resharper tidak memeriksa pesan logging
Jodrell
2
@Jodrell: Dan, saya curiga, itu tidak memeriksa berbagai kegunaan lain, baik - bagaimana dengan binding WPF dibuat di belakang kode, OnPropertyChangedmetode kustom (yang langsung menerima nama properti daripada PropertyChangedEventArgs), atau panggilan untuk refleksi untuk mencari tertentu anggota atau tipe?
ATAU Mapper
13

Kasus penggunaan paling umum yang dapat saya pikirkan adalah ketika bekerja dengan INotifyPropertyChangedantarmuka. (Pada dasarnya semua yang terkait dengan WPF dan binding menggunakan antarmuka ini)

Lihatlah contoh ini:

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)));           
        }
    }
}

Seperti yang Anda lihat dengan cara lama, kami harus memberikan string untuk menunjukkan properti mana yang telah berubah. Dengan nameofkita dapat menggunakan nama properti secara langsung. Ini mungkin bukan masalah besar. Tapi bayangkan apa yang terjadi ketika seseorang mengubah nama properti Foo. Saat menggunakan string, binding akan berhenti bekerja, tetapi kompiler tidak akan memperingatkan Anda. Saat menggunakan nameof Anda mendapatkan kesalahan kompiler bahwa tidak ada properti / argumen dengan nama Foo.

Perhatikan bahwa beberapa kerangka kerja menggunakan beberapa sihir refleksi untuk mendapatkan nama properti, tetapi sekarang kita memiliki nama ini tidak lagi diperlukan .

Roy T.
sumber
5
Meskipun ini adalah pendekatan yang valid, pendekatan yang lebih mudah (dan KERING) adalah menggunakan [CallerMemberName]atribut pada param metode baru untuk meningkatkan peristiwa ini.
Drew Noakes
1
Saya setuju CallerMemberName juga bagus, tetapi ini merupakan use case yang terpisah, karena (seperti yang Anda katakan) Anda hanya dapat menggunakannya dalam metode. Sedangkan untuk KERING, saya tidak yakin apakah [CallerMemberName]string x = nulllebih baik dari nameof(Property). Anda bisa mengatakan nama properti digunakan dua kali, tetapi pada dasarnya argumen itu dilewatkan ke fungsi. Tidak benar-benar apa yang dimaksud dengan KERING saya pikir :).
Roy T.
Sebenarnya Anda bisa menggunakannya di properti. Mereka juga anggota. Keuntungannya nameofadalah bahwa setter properti tidak perlu menentukan nama properti sama sekali, menghilangkan kemungkinan bug copy / paste.
Drew Noakes
4
Ini adalah situasi 'lebih baik bersama' untuk INotifyPropertyChanged, menggunakan [CallerMemberNameAttribute]memungkinkan notifikasi perubahan dibesarkan dengan bersih dari setter properti, sementara nameofsintaks memungkinkan notifikasi perubahan dibangkitkan secara bersih dari lokasi yang berbeda dalam kode Anda.
Andrew Hanlon
9

Penggunaan paling umum adalah validasi input, seperti

//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));
}

Dalam kasus pertama, jika Anda refactor metode mengubah nama parameter par , Anda mungkin akan lupa untuk mengubahnya di ArgumentNullException . Dengan nama Anda tidak perlu khawatir tentang itu.

Lihat juga: nameof (C # dan Visual Basic Reference)

Massimo Prota
sumber
7

Proyek ASP.NET Inti MVC menggunakan nameofdalam AccountController.csdan ManageController.csdengan RedirectToActionmetode untuk referensi tindakan dalam controller.

Contoh:

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

Ini diterjemahkan menjadi:

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

dan mengambil membawa pengguna ke tindakan 'Indeks' di controller 'Rumah', yaitu /Home/Index.

Fred
sumber
Mengapa tidak menggunakan babi utuh dan menggunakannya return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000
@ Suncat2000 karena salah satu dari hal-hal itu dilakukan dalam kompilasi dan yang lainnya tidak? :)
Dinerdo
6

Seperti yang telah ditunjukkan orang lain, nameofoperator memasukkan nama elemen yang diberikan dalam kode sumber.

Saya ingin menambahkan bahwa ini adalah ide yang sangat bagus dalam hal refactoring karena membuat string ini refactoring aman. Sebelumnya, saya menggunakan metode statis yang menggunakan refleksi untuk tujuan yang sama, tetapi itu memiliki dampak kinerja runtime. The nameofOperator tidak memiliki dampak kinerja runtime; itu bekerja pada waktu kompilasi. Jika Anda melihat MSILkode Anda akan menemukan string yang tertanam. Lihat metode berikut dan kode yang dibongkar.

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

Namun, itu bisa menjadi kelemahan jika Anda berencana untuk mengaburkan perangkat lunak Anda. Setelah kebingungan, string yang disematkan mungkin tidak lagi cocok dengan nama elemen. Mekanisme yang bergantung pada teks ini akan pecah. Contoh untuk itu, termasuk tetapi tidak terbatas pada adalah: Refleksi, NotifyPropertyChanged ...

Menentukan nama selama runtime membutuhkan kinerja, tetapi aman untuk kebingungan. Jika kebingungan tidak diperlukan atau direncanakan, saya akan merekomendasikan menggunakan nameofoperator.

cel tajam
sumber
5

Pertimbangkan bahwa Anda menggunakan variabel dalam kode Anda dan perlu mendapatkan nama variabel dan katakanlah cetak itu, Anda harus menggunakan

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

Dan jika kemudian seseorang refactor kode dan menggunakan nama lain untuk "myVar", dia harus memperhatikan nilai string dalam kode Anda dan menebusnya sesuai.

Alih-alih jika Anda punya

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

Ini akan membantu untuk refactor secara otomatis!

bu
sumber
Saya berharap telah ada sintaks variabel-parameter khusus yang akan melewatkan array tupel, satu untuk setiap parameter, yang berisi representasi kode sumber, the Type, dan nilainya. Itu akan memungkinkan kode yang menerapkan metode logging untuk menghilangkan banyak redundansi.
supercat
5

Artikel MSDN mencantumkan perutean MVC (contoh yang benar-benar mengklik konsep untuk saya) di antara beberapa lainnya. Paragraf deskripsi (diformat) berbunyi:

  • Saat melaporkan kesalahan dalam kode,
  • menghubungkan model-view-controller (MVC) tautan,
  • menembakkan properti mengubah acara, dll.,

Anda sering ingin menangkap nama string suatu metode . Menggunakan nameof membantu menjaga kode Anda valid ketika mengganti nama definisi.

Sebelum Anda harus menggunakan string literal untuk merujuk pada definisi, yang rapuh ketika mengganti nama elemen kode karena alat tidak tahu untuk memeriksa string literal ini.

Jawaban yang diterima / berperingkat teratas sudah memberikan beberapa contoh nyata yang sangat baik.

brichins
sumber
3

Tujuan nameofoperator adalah untuk memberikan nama sumber artefak.

Biasanya nama sumber adalah nama yang sama dengan nama metadata:

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));
    }
}

Tapi ini mungkin tidak selalu terjadi:

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

Atau:

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

Satu kegunaan yang telah saya berikan untuk itu adalah untuk penamaan sumber daya:

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

Faktanya adalah, dalam hal ini, saya bahkan tidak memerlukan properti yang dihasilkan untuk mengakses sumber daya, tetapi sekarang saya memiliki waktu kompilasi memeriksa bahwa sumber daya ada.

Paulo Morgado
sumber
0

Salah satu penggunaan nameofkata kunci adalah untuk pengaturan Bindingdi WPF secara terprogram .

untuk mengatur BindingAnda harus mengatur Pathdengan string, dan dengan nameofkata kunci, dimungkinkan untuk menggunakan opsi Refactor.

Misalnya, jika Anda memiliki IsEnableproperti ketergantungan di Anda UserControldan Anda ingin mengikatnya ke IsEnablebeberapa CheckBoxdi Anda UserControl, Anda dapat menggunakan dua kode ini:

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

dan

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

Jelas kode pertama tidak dapat memperbaiki tetapi yang aman ...

Mehdi Khademloo
sumber
0

Sebelumnya kami menggunakan sesuatu seperti itu:

// 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)
{
    ...
}

Alasan - kompilasi keamanan waktu. Tidak ada yang dapat secara diam-diam mengganti nama properti dan memecahkan logika kode. Sekarang kita bisa menggunakan nameof ().

QtRoS
sumber
0

Ini memiliki keuntungan ketika Anda menggunakan ASP.Net MVC. Saat Anda menggunakan HTML helper untuk membangun kontrol dalam tampilan, ia menggunakan nama properti dalam nama input html:

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

Itu membuat sesuatu seperti itu:

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

Jadi sekarang, jika Anda perlu memvalidasi properti Anda dalam metode Validasi Anda dapat melakukan ini:

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

Jika Anda mengubah nama properti Anda menggunakan alat refactoring, validasi Anda tidak akan rusak.

dgtlfx
sumber
0

Contoh penggunaan lain nameofadalah memeriksa laman tab, alih-alih memeriksa indeks, Anda dapat memeriksa Nameproperti laman tab sebagai berikut:

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

Kurang berantakan :)

diedrop
sumber
0

Saya menemukan bahwa nameofmeningkatkan keterbacaan pernyataan SQL yang sangat panjang dan kompleks dalam aplikasi saya. Itu membuat variabel menonjol dari lautan string dan menghilangkan pekerjaan Anda mencari tahu di mana variabel digunakan dalam pernyataan SQL Anda.

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 },
    }
}
Kristianne Nerona
sumber