7 zastosowań operatora nameof w C#

Czas czytania ~ 120 sekund

Co prawda mamy już oficjalnie C# w wersji siódmiej, ale wróćmy jeszcze na moment do wersji 6 i przyjrzyjmy się operatorowi nameof. Niby prosty operator zamieniający symbol (klasę, metodę, property, parametr metody …) na string odpowiadający jego nazwie. Tylko tyle i aż tyle, jeśli pomyślimy o tym w kontekście refaktoryzacji kodu.

Pod maską

Pierwsza rzecz, która przychodzi do głowy, to sprawdzić, jak to działa pod spodem. Może pojawić się obawa, że nameof wykorzystuje Reflection i przez to będzie działał wolniej niż wstawienie do kodu ciągu znaków. Ale na szczęście tak nie jest. Taki kod:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(nameof(Program.Main));
    }
}

Wygląda w Intermediate Language tak:

.class private auto ansi beforefieldinit 
  ConsoleApplication1.Program
    extends [mscorlib]System.Object
{

  .method private hidebysig static void 
    Main(
      string[] args
    ) cil managed 
  {
    .entrypoint
    .maxstack 8

    // [12 9 - 12 10]
    IL_0000: nop          

    // [13 13 - 13 53]
    IL_0001: ldstr        "Main"
    IL_0006: call         void [mscorlib]System.Console::WriteLine(string)
    IL_000b: nop          

    // [14 9 - 14 10]
    IL_000c: ret          

  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname instance void 
    .ctor() cil managed 
  {
    .maxstack 8

    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [mscorlib]System.Object::.ctor()
    IL_0006: nop          
    IL_0007: ret          

  } // end of method Program::.ctor
} // end of class ConsoleApplication1.Program

a więc nie ma powodu do obaw, przed wywołaniem funkcji WriteLine, na stos ładowany jest string. A więc krótko mówiąc, mamy fajny Syntactic Sugar. Teraz poszukajmy, gdzie można go wykorzystać.

I Przekierowania w ASP.NET Core MVC

W kontrolerach mamy do dyspozycji metodę RedirectToAction, która przyjmuje jako parametry stringowo nazwę akcji i nazwę kontrolera. Idealne miejsce dla nameof.

public IActionResult SampleRedirect()
{
    return RedirectToAction(nameof(MyController.Index), nameof(MyController));
}

II Walidacja – ArgumentNullException

Wyjątek ArgumentNullException przyjmuje w jednym ze swoich konstruktorów string będący nazwą parametru, który jest null-em.

if(args == null)
{
    throw new ArgumentNullException(nameof(args));
}

III NotifyPropertyChanged w WPF

W WPF pisząc code behind w celu odświeżenia widoku należało wywołać zdarzenie PropertyChanged podając nazwę property jako argument, również stringowo. Alternatywnie od C# 6:

public partial class MainWindow : INotifyPropertyChanged
{
    private bool _isHidden;
    public bool IsHidden
    {
        get { return _isHidden; }
        set
        {
            _isHidden = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsHidden)));
        }
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        IsHidden = !IsHidden;
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

IV DebuggerDisplay

Kompilator C# traktuje wyrażenie z nameof jako stałą, a więc można go używać w atrybutach. Pierwszym przykładem może być DebuggerDisplay, czyli customowe wyświetlanie stanu obiektu po najechaniu na zmienną podczas debugowania.

[DebuggerDisplay(nameof(Id)+"={"+nameof(Id)+"}")]
public class MyClass
{
    public string Name { get; set; }
    public string Id { get; set; }
}

Zapis co prawda jest nieco dłuższy, ale mamy za to odporność na refaktoryzację tego property.

V ElasticsearchType: IdProperty

Dotnetowy klient ElasticSearcha, Nest, pozwala na tworzenie indeksów na podstawie atrybutów zdefiniowanych nad klasą. Problem pojawia się, gdy chcemy wskazać jawnie, które property ma posłużyć jako identyfikator encji w samym indeksie. Z pomocą przychodzi nameof

[ElasticsearchType(IdProperty = nameof(Id))]
public class Person
{
    public int Id { get; set; }
    [Keyword]
    public string FirstName { get; set; }
    [Keyword]
    public string LastName { get; set; }
    [Text]
    public string Bio { get; set; }
}

VI Stałe aplikacji

Kolejne zastosowanie, może wydać się nieco kuriozalne jeśli chodzi o zapis, ale gwarantuje nam, że nie zrobimy literówki wpisując dwa razy ten sam ciąg znaków. Chodzi o statyczne klasy, w których przechowujemy stałe aplikacji, takie jak na przykład nazwy kolekcji w MongoDB. Możemy użyć nameof aby zachować symetrię pomiędzy nazwą pola a jego wartością.

public static class MongoDBCollectionNames
{
    public static string Documents = nameof(Documents);
    public static string Permissions = nameof(Permissions);
}

VII Logowanie zdarzeń

Czasami zdarza się, że chcemy zalogować fakt wejścia do danej metody. Aby być odpornym na przyszłe zmiany nazwy klasy czy metody możemy to zrobić za pomocą nameof.

public IActionResult Index()
{
    _logger.LogInformation($"{nameof(HomeController)}.{nameof(HomeController.Index)}");
    return View();
}

 

Podsumowując, mamy do dyspozycji bardzo prosty operator, który w wielu sytuacjach możne nam pomóc w wyłapaniu potencjalnych błędów, które mogą wystąpić po zmianach nazw klas, propertiesów, metod itd.

Advertisements

4 thoughts on “7 zastosowań operatora nameof w C#

  1. Hej, jest jeszcze jedna fajna sprawa z tym operatorem.

    Możemy go użyć do wyjęcia tekstów z plików .resx. Wtedy zmieniając klucz w pliku z resourcami – automatycznie zmieni nam się we wszystkich odwołaniach, np.

    [Display(Name = nameof(Resources.Labels.Email), ResourceType = typeof(Resources.Labels))]
    [Required(ErrorMessageResourceName = nameof(Resources.Messages.FieldRequired), ErrorMessageResourceType = typeof(Resources.Messages))]
    [EmailAddress(ErrorMessageResourceName = nameof(Resources.Messages.InvalidEmailAddressFormat), ErrorMessageResourceType = typeof(Resources.Messages))]
    public string Email { get; set; }

    Like

  2. Punkt 4.Jest: [DebuggerDisplay(nameof(Id)+”={“+nameof(Id)+”}”)]. A chyba powinno być: [DebuggerDisplay(nameof(Id)+”={“+Id+”}”)] 🙂

    Like

    • Hej, nie do końca. W atrybucie nie mogę się odwoływać bezpośrednio do instancyjnego property. Muszę podać coś, co jest stałe już na etapie kompilacji. To, co próbuję tu uzyskać, to po prostu: [DebuggerDisplay(“Id={Id}”)], Visual Studio wie, co z tym zrobić w runtime.

      Like

  3. Mnie nameof przydaje się bardzo przy mapowaniu kolumn z SQL-a. Przy użyciu Dappera – prostego mappera, kod moze wyglądać tak:
    var result = await connection.QueryAsync($@”
    SELECT PrdId as {nameof(ProductDetails.ProductId)},
    Desc as {nameof(ProductDetails.LongDescription)}
    FROM Products”);

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s