Quel est le but de nameof?

264

La version 6.0 a une nouvelle fonctionnalité nameof, mais je ne comprends pas le but de celle-ci, car elle prend simplement le nom de la variable et le change en chaîne lors de la compilation.

Je pensais que cela pourrait avoir un but lors de l'utilisation, <T>mais lorsque j'essaye, nameof(T)cela m'imprime juste un Tau lieu du type utilisé.

Une idée sur le but?

atikot
la source
28
Il n'y avait aucun moyen d'obtenir cela Tavant. Il y avait un moyen d'obtenir le type utilisé auparavant.
Jon Hanna
15
Certainement utile lors de la refonte / renommage du nom à l'intérieur nameof. Aide également à prévenir les fautes de frappe.
bvj
nameof est pratique avec nunit 3 TestCaseData . Les documents affichent un nom de méthode par chaîne [Test, TestCaseSource(typeof(MyDataClass), "TestCases")], qui peut être remplacé par [Test, TestCaseSource(typeof(MyDataClass), nameof(MyDataClass.TestCases))]. Beaucoup mieux.
Jeremy
4
La documentation officielle de nameof a été déplacée ici: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - Il répertorie également les cas d'utilisation clés qui constituent une assez bonne réponse à la question.
markus s

Réponses:

324

Qu'en est-il des cas où vous souhaitez réutiliser le nom d'une propriété, par exemple lors du lancement d'une exception basée sur un nom de propriété ou de la gestion d'un PropertyChangedévénement. Il existe de nombreux cas où vous voudriez avoir le nom de la propriété.

Prenez cet exemple:

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

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

Dans le premier cas, SomePropertyle changement de nom modifiera également le nom de la propriété ou interrompra la compilation. Le dernier cas ne fonctionne pas.

C'est un moyen très utile de garder votre code compilé et sans bug (sorte de).

(Un très bel article d'Eric Lippert pourquoi infoofne l'a pas fait, alors qu'il l'a nameoffait)

Patrick Hofman
la source
2
Je comprends le point, en ajoutant simplement que le resharper change les chaînes lors de la refactorisation des noms, je ne sais pas si VS a des fonctionnalités similaires.
Ash Burlaczenko
8
Il a. Mais Resharper et VS ne fonctionnent pas sur les projets par exemple. C'est le cas. En fait, c'est la meilleure solution.
Patrick Hofman
50
Un autre cas d'utilisation courant est le routage dans MVC en utilisant nameofet le nom de l'action au lieu d'une chaîne codée en dur.
RJ Cuthbertson
2
@sotn Je ne suis pas sûr de comprendre ce que vous demandez. Rien ne vous empêche de l'utiliser comme public class MyController { public ActionResult Index() { return View(nameof(Index)); } }- et vous pouvez l'utiliser nameofsur des membres non statiques (par exemple, vous pouvez appeler en nameof(MyController.Index)utilisant la classe ci-dessus et elle émettra "Index"). Consultez les exemples sur msdn.microsoft.com/en-us/library/…
RJ Cuthbertson
2
Je ne vois pas pourquoi c'est spécial. Les noms de variables sont toujours les mêmes, non? Que vous ayez une instance ou non, les noms de variables ne changeront pas @sotn
Patrick Hofman
177

C'est vraiment utile pour ArgumentExceptionet ses dérivés:

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

Maintenant, si quelqu'un refactorise le nom du inputparamètre, l'exception sera également mise à jour.

Il est également utile dans certains endroits où auparavant la réflexion devait être utilisée pour obtenir les noms des propriétés ou des paramètres.

Dans votre exemple, nameof(T)obtient le nom du paramètre type - cela peut également être utile:

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

Une autre utilisation de nameofest pour les énumérations - généralement si vous voulez le nom de chaîne d'une énumération que vous utilisez .ToString():

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

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

> "FooBar"

C'est en fait relativement lent car .Net détient la valeur enum (c'est-à-dire 7) et trouve le nom au moment de l'exécution.

Utilisez plutôt nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Désormais .Net remplace le nom de l'énumération par une chaîne au moment de la compilation.


Encore une autre utilisation est pour des choses comme INotifyPropertyChangedet la journalisation - dans les deux cas, vous voulez que le nom du membre que vous appelez soit transmis à une autre méthode:

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

Ou...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}
Keith
la source
9
Et vous avez ajouté une autre fonctionnalité intéressante: l'interpolation de chaînes!
Patrick Hofman
1
@PatrickHofman et typeof(T), qui est un autre morceau de sucre à la compilation qui est utile dans des circonstances similaires :-)
Keith
1
Une chose qui me manque est quelque chose comme nameofthismethod. Vous pouvez utiliser, Log.Error($"Error in {nameof(DoSomething)}...")mais si vous copiez-collez cela vers d'autres méthodes, vous ne remarquerez pas qu'il fait toujours référence à DoSomething. Ainsi, même s'il fonctionne parfaitement avec des variables ou des paramètres locaux, le nom de la méthode pose problème.
Tim Schmelter
3
Savez-vous si nameOfva utiliser l' [DisplayName]attribut s'il est présent? Pour l' enumexemple que j'utilise [DisplayName]souvent avec les projets MVC
Luke T O'Brien
2
@AaronLS Oui, c'est assez spécialisé et ce n'est pas quelque chose que vous utiliseriez souvent. Cependant, c'est throw newun tout autre anti-modèle - je trouve catchque la surutilisation est un problème commun avec les développeurs juniors parce que cela ressemble à résoudre le problème (la plupart du temps, il ne fait que le cacher).
Keith
26

Un autre cas d'utilisation où la nameoffonctionnalité de C # 6.0 devient pratique - Considérez une bibliothèque comme Dapper qui facilite les récupérations de bases de données. Bien qu'il s'agisse d'une excellente bibliothèque, vous devez coder en dur les noms de propriété / champ dans la requête. Cela signifie que si vous décidez de renommer votre propriété / champ, il y a de fortes chances que vous oubliez de mettre à jour la requête pour utiliser de nouveaux noms de champ. Avec l'interpolation et les nameoffonctionnalités de chaîne , le code devient beaucoup plus facile à maintenir et à sécuriser.

De l'exemple donné dans le lien

sans nom

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

avec nameof

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });
sateesh
la source
3
J'adore Dapper et j'aime beaucoup les interporlations de cordes, mais à mon avis, cela semble si moche. Le risque de casser une requête en renommant une colonne semble si faible par rapport à de telles requêtes laides. À première vue, je dirais que je préfère écrire des requêtes EF LINQ ou suivre une convention comme [TableName]. [ColumnName] où je peux facilement trouver / remplacer mes requêtes quand j'en ai besoin.
drizin
@drizin J'utilise Dapper FluentMap pour empêcher de telles requêtes (et aussi pour la séparation des préoccupations)
mamuesstack
21

Votre question exprime déjà le but. Vous devez voir que cela peut être utile pour la journalisation ou le lancement d'exceptions.

par exemple.

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

c'est bien, si je change le nom de la variable, le code se cassera à la place ou retournera une exception avec un message incorrect .


Bien entendu, les utilisations ne se limitent pas à cette simple situation. Vous pouvez utiliser nameofchaque fois qu'il serait utile de coder le nom d'une variable ou d'une propriété.

Les utilisations sont multiples lorsque l'on considère diverses situations de reliure et de réflexion. C'est un excellent moyen d'apporter ce qui était des erreurs d'exécution pour compiler le temps.

Jodrell
la source
6
@atikot: Mais alors, si vous renommez la variable, le compilateur ne remarquera plus que la chaîne ne correspond plus.
OR Mapper
1
J'utilise en fait resharper qui en tient compte, mais je vois votre point.
atikot
4
@atikot, moi aussi, mais Resharper ne génère qu'un avertissement, pas une erreur de compilation. Il y a une différence entre une certitude et un bon conseil.
Jodrell
1
@atikot et Resharper ne vérifie pas les messages de journalisation
Jodrell
2
@Jodrell: Et, je soupçonne, il ne vérifie pas non plus diverses autres utilisations - que diriez-vous des liaisons WPF créées en code-behind, des OnPropertyChangedméthodes personnalisées (qui acceptent directement les noms de propriété plutôt que PropertyChangedEventArgs), ou des appels à la réflexion pour rechercher un particulier membre ou type?
OR Mapper
13

Le cas d'utilisation le plus courant auquel je peux penser est lorsque vous travaillez avec l' INotifyPropertyChangedinterface. (Fondamentalement, tout ce qui concerne WPF et les liaisons utilise cette interface)

Jetez un œil à cet exemple:

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

Comme vous pouvez le voir à l'ancienne, nous devons passer une chaîne pour indiquer quelle propriété a changé. Avec nameofnous pouvons utiliser directement le nom de la propriété. Cela pourrait ne pas sembler être un gros problème. Mais imaginez ce qui se passe lorsque quelqu'un change le nom de la propriété Foo. Lorsque vous utilisez une chaîne, la liaison cessera de fonctionner, mais le compilateur ne vous avertira pas. Lorsque vous utilisez nameof, vous obtenez une erreur de compilation qu'il n'y a pas de propriété / argument avec le nom Foo.

Notez que certains frameworks utilisent de la magie de réflexion pour obtenir le nom de la propriété, mais maintenant nous avons nameof ce n'est plus nécessaire .

Roy T.
la source
5
Bien qu'il s'agisse d'une approche valide, une approche plus pratique (et SECHE) consiste à utiliser l' [CallerMemberName]attribut sur le paramètre d'une nouvelle méthode pour déclencher cet événement.
Drew Noakes
1
Je suis d'accord que CallerMemberName est également agréable, mais c'est un cas d'utilisation distinct, car (comme vous l'avez dit), vous ne pouvez l'utiliser que dans les méthodes. Quant à DRY, je ne sais pas si [CallerMemberName]string x = nullc'est mieux que nameof(Property). Vous pourriez dire que le nom de la propriété est utilisé deux fois, mais c'est essentiellement l'argument passé à une fonction. Pas vraiment ce que l'on entend par DRY je pense :).
Roy
En fait, vous pouvez l'utiliser dans les propriétés. Ils sont aussi membres. L'avantage nameofest que le configurateur de propriétés n'a pas besoin de spécifier du tout le nom de la propriété, éliminant ainsi la possibilité de copier / coller de bogues.
Drew Noakes
4
C'est une situation `` mieux ensemble '', car l' INotifyPropertyChangedutilisation de [CallerMemberNameAttribute]permet à la notification de changement d'être proprement levée à partir d'un configurateur de propriétés, tandis que la nameofsyntaxe permet à une notification de changement d'être proprement levée à partir d'un emplacement différent dans votre code.
Andrew Hanlon
9

L'utilisation la plus courante sera dans la validation des entrées, comme

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

En premier cas, si vous refactoring la méthode changer par le nom de paramètre, vous oublierez probablement changer que dans le ArgumentNullException . Avec nameof, vous n'avez pas à vous en soucier.

Voir aussi: nameof (référence C # et Visual Basic)

Massimo Prota
la source
7

Le projet ASP.NET Core MVC utilise nameofdans AccountController.cset ManageController.csavec la RedirectToActionméthode pour référencer une action dans le contrôleur.

Exemple:

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

Cela se traduit par:

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

et prend prend l'utilisateur à l'action «Index» dans le contrôleur «Accueil», à savoir /Home/Index.

Fred
la source
Pourquoi ne pas utiliser le porc entier et l'utiliser return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000
@ Suncat2000 parce qu'une de ces choses se fait en compilation et l'autre non? :)
Dinerdo
6

Comme d'autres l'ont déjà souligné, l' nameofopérateur insère le nom que l'élément a été donné dans le code source.

Je voudrais ajouter que c'est une très bonne idée en termes de refactoring car cela rend cette refactoring de chaîne sûre. Auparavant, j'utilisais une méthode statique qui utilisait la réflexion dans le même but, mais qui a un impact sur les performances d'exécution. L' nameofopérateur n'a aucun impact sur les performances d'exécution; il fait son travail au moment de la compilation. Si vous regardez le MSILcode, vous trouverez la chaîne intégrée. Voir la méthode suivante et son code désassemblé.

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

Cependant, cela peut être un inconvénient si vous prévoyez d'obscurcir votre logiciel. Après l'obscurcissement, la chaîne incorporée peut ne plus correspondre au nom de l'élément. Les mécanismes qui s'appuient sur ce texte se briseront. Des exemples pour cela, y compris, mais sans s'y limiter, sont: Reflection, NotifyPropertyChanged ...

La détermination du nom pendant l'exécution coûte quelques performances, mais est sans danger pour l'obscurcissement. Si l'obscurcissement n'est ni requis ni prévu, je recommanderais d'utiliser l' nameofopérateur.

cel sharp
la source
5

Considérez que vous utilisez une variable dans votre code et que vous devez obtenir le nom de la variable et disons l'imprimer, vous devriez avoir à utiliser

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

Et si quelqu'un refactorise le code et utilise un autre nom pour "myVar", il devra surveiller la valeur de la chaîne dans votre code et la modifier en conséquence.

Au lieu de cela, si vous aviez

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

Cela aiderait à refactoriser automatiquement!

cnom
la source
Je souhaite qu'il y ait eu une syntaxe de paramètre variable spéciale qui passerait un tableau de tuples, un pour chaque paramètre, contenant la représentation du code source, le Typeet la valeur. Cela permettrait au code d'invoquer des méthodes de journalisation pour éliminer beaucoup de redondance.
supercat
5

L'article MSDN répertorie le routage MVC (l'exemple qui a vraiment cliqué sur le concept pour moi) parmi plusieurs autres. Le paragraphe de description (formaté) se lit comme suit:

  • Lorsque vous signalez des erreurs de code,
  • raccordement de liaisons modèle-vue-contrôleur (MVC),
  • le tir des biens a changé des événements, etc.,

vous souhaitez souvent capturer le nom de chaîne d'une méthode . L'utilisation de nameof permet de garder votre code valide lors du changement de nom des définitions.

Avant, vous deviez utiliser des littéraux de chaîne pour faire référence aux définitions, ce qui est fragile lorsque vous renommez des éléments de code car les outils ne savent pas vérifier ces littéraux de chaîne.

Les réponses acceptées / les mieux notées donnent déjà plusieurs excellents exemples concrets.

brichins
la source
3

Le but de la nameof opérateur est de fournir le nom source des artefacts.

Habituellement, le nom de la source est le même que le nom des métadonnées:

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

Mais ce n'est pas toujours le cas:

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

Ou:

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

Une utilisation que je lui ai donnée est pour nommer les ressources:

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

Le fait est que, dans ce cas, je n'avais même pas besoin des propriétés générées pour accéder aux ressources, mais maintenant j'ai un temps de compilation pour vérifier que les ressources existent.

Paulo Morgado
la source
0

L'une des utilisations de nameofmot-clé est de définir Bindingpar programme wpf .

pour définir, Bindingvous devez définir Pathavec une chaîne et avec un nameofmot clé, il est possible d'utiliser l'option Refactor.

Par exemple, si vous avez IsEnableune propriété de dépendance dans votre UserControlet que vous souhaitez la lier à IsEnablecertaines CheckBoxde vos UserControl, vous pouvez utiliser ces deux codes:

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

et

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

Il est évident que le premier code ne peut pas être refactorisé mais le second ...

Mehdi Khademloo
la source
0

Auparavant, nous utilisions quelque chose comme ça:

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

Raison - compiler la sécurité du temps. Personne ne peut renommer silencieusement une propriété et rompre la logique du code. Maintenant, nous pouvons utiliser nameof ().

QtRoS
la source
0

Il présente un avantage lorsque vous utilisez ASP.Net MVC. Lorsque vous utilisez l'aide HTML pour créer un contrôle dans la vue, il utilise des noms de propriété dans l'attribution de nom de l'entrée html:

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

Cela fait quelque chose comme ça:

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

Alors maintenant, si vous devez valider votre propriété dans la méthode Validate, vous pouvez le faire:

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

Si vous renommez votre propriété à l'aide d'outils de refactoring, votre validation ne sera pas interrompue.

dgtlfx
la source
0

Un autre cas d'utilisation de nameofconsiste à vérifier les pages à onglets, au lieu de vérifier l'index, vous pouvez vérifier la Namepropriété des pages à onglets comme suit:

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

Moins salissant :)

deadrop
la source
0

Je trouve que cela nameofaugmente la lisibilité des instructions SQL très longues et complexes dans mes applications. Il fait ressortir les variables de cet océan de chaînes et élimine votre tâche de déterminer où les variables sont utilisées dans vos instructions SQL.

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
la source