¿Cuál es el propósito de nameof?

264

La versión 6.0 tiene una nueva característica de nameof, pero no puedo entender el propósito, ya que solo toma el nombre de la variable y lo cambia a una cadena en la compilación.

Pensé que podría tener algún propósito cuando lo uso, <T>pero cuando lo intento nameof(T)simplemente me imprime un tipo en Tlugar del tipo utilizado.

¿Alguna idea sobre el propósito?

atikot
fuente
28
No había forma de conseguir eso Tantes. Había una manera de obtener el tipo usado antes.
Jon Hanna
15
Definitivamente útil cuando refactory / renombrar el nombre dentro nameof. También ayuda a prevenir errores tipográficos.
bvj
nameof es útil con nunit 3 TestCaseData . Los documentos muestran un nombre de método por cadena [Test, TestCaseSource(typeof(MyDataClass), "TestCases")], que se puede reemplazar por [Test, TestCaseSource(typeof(MyDataClass), nameof(MyDataClass.TestCases))]. Mucho más bonito.
Jeremy
44
La documentación oficial de nameof se trasladó a aquí: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - También enumera casos de uso clave que sirven como una respuesta bastante buena a la pregunta.
Markus s

Respuestas:

324

¿Qué sucede con los casos en los que desea reutilizar el nombre de una propiedad, por ejemplo, cuando lanza una excepción basada en el nombre de una propiedad o maneja un PropertyChangedevento? Existen numerosos casos en los que desea tener el nombre de la propiedad.

Toma este ejemplo:

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

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

En el primer caso, el cambio de nombre SomePropertytambién cambiará el nombre de la propiedad o interrumpirá la compilación. El último caso no.

Esta es una forma muy útil de mantener su código compilado y libre de errores (más o menos).

(Un muy buen artículo de Eric Lippert por qué infoofno lo hizo, mientras lo nameofhizo)

Patrick Hofman
fuente
2
Entiendo el punto, solo agregando que resharper cambia las cadenas al refactorizar nombres, no estoy seguro de si VS tiene una funcionalidad similar.
Ash Burlaczenko
8
Tiene. Pero Resharper y VS no funcionan en proyectos, por ejemplo. Esto hace. De hecho, esta es la mejor solución.
Patrick Hofman
50
Otro caso de uso común es el enrutamiento en MVC nameofy el nombre de la acción en lugar de una cadena codificada.
RJ Cuthbertson
2
@sotn No estoy seguro de entender lo que estás preguntando. No hay nada que le impida usarlo como public class MyController { public ActionResult Index() { return View(nameof(Index)); } }, y puede usarlo nameofen miembros no estáticos (por ejemplo, puede llamar nameof(MyController.Index)usando la clase anterior y emitirá "Índice"). Vea los ejemplos en msdn.microsoft.com/en-us/library/…
RJ Cuthbertson el
2
No veo por qué eso es especial. Los nombres de las variables son siempre iguales, ¿verdad? Ya sea que tenga una instancia o no, los nombres de las variables no cambiarán @sotn
Patrick Hofman
177

Es realmente útil para ArgumentExceptiony sus derivados:

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

Ahora, si alguien refactoriza el nombre del inputparámetro, la excepción también se mantendrá actualizada.

También es útil en algunos lugares donde previamente se tuvo que usar la reflexión para obtener los nombres de propiedades o parámetros.

En su ejemplo, nameof(T)obtiene el nombre del parámetro de tipo; esto también puede ser útil:

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

Otro uso de nameofes para enumeraciones, generalmente si desea el nombre de cadena de una enumeración que utiliza .ToString():

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

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

> "FooBar"

En realidad, esto es relativamente lento ya que .Net mantiene el valor de enumeración (es decir 7) y encuentra el nombre en tiempo de ejecución.

En su lugar use nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Ahora .Net reemplaza el nombre de enumeración con una cadena en tiempo de compilación.


Sin embargo, otro uso es para cosas como el INotifyPropertyChangedregistro: en ambos casos, desea que el nombre del miembro que está llamando se pase a otro método:

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

O...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}
Keith
fuente
9
Y has puesto otra característica genial: ¡interpolación de cuerdas!
Patrick Hofman
1
@PatrickHofman y typeof(T), que es otra pieza de azúcar en tiempo de compilación que es útil en circunstancias similares :-)
Keith
1
Una cosa que me estoy perdiendo es algo así nameofthismethod. Puede usarlo, Log.Error($"Error in {nameof(DoSomething)}...")pero si copia y pega esto a otros métodos, no notará que todavía se está refiriendo DoSomething. Entonces, si bien funciona perfectamente con variables o parámetros locales, el nombre del método es un problema.
Tim Schmelter
3
¿Sabes si nameOfusará el [DisplayName]atributo si está presente? Para el enumejemplo que uso a [DisplayName]menudo con proyectos MVC
Luke T O'Brien
2
@AaronLS Sí, es bastante especializado y no es algo que usarías a menudo. Sin embargo, ese throw newes un antipatrón completamente diferente: encuentro que el uso excesivo catches un problema común con los desarrolladores junior porque se siente como solucionar el problema (cuando la mayoría de las veces lo está ocultando).
Keith
26

Otro caso de uso en el que la nameoffunción de C # 6.0 se vuelve útil : considere una biblioteca como Dapper que hace que las recuperaciones de DB sean mucho más fáciles. Aunque esta es una gran biblioteca, necesita codificar los nombres de propiedad / campo dentro de la consulta. Lo que esto significa es que si decide cambiar el nombre de su propiedad / campo, hay muchas posibilidades de que se olvide de actualizar la consulta para usar nuevos nombres de campo. Con la interpolación de cadenas y las nameofcaracterísticas, el código se vuelve mucho más fácil de mantener y seguro.

Del ejemplo dado en el enlace

sin nombre de

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

con nombre de

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });
sateesh
fuente
3
Amo a Dapper y realmente me gustan las interpolaciones de cuerdas, pero en mi opinión, esto se ve tan feo. El riesgo de romper una consulta al cambiar el nombre de una columna parece tan pequeño en comparación con consultas tan feas. A primera vista, diría que prefiero escribir consultas EF LINQ, o seguir una convención como [TableName]. [ColumnName] donde puedo encontrar / reemplazar mis consultas fácilmente cuando lo necesito.
Drizin
@drizin Uso Dapper FluentMap para evitar tales consultas (y también para la separación de preocupaciones)
mamuesstack
21

Su pregunta ya expresa el propósito. Debe ver que esto puede ser útil para iniciar sesión o lanzar excepciones.

por ejemplo.

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

Esto es bueno, si cambio el nombre de la variable, el código se romperá o devolverá una excepción con un mensaje incorrecto .


Por supuesto, los usos no se limitan a esta simple situación. Puede usarlo nameofsiempre que sea útil codificar el nombre de una variable o propiedad.

Los usos son múltiples cuando considera varias situaciones vinculantes y de reflexión. Es una excelente manera de llevar los errores de tiempo de ejecución al tiempo de compilación.

Jodrell
fuente
66
@atikot: Pero luego, si cambia el nombre de la variable, el compilador no notará que la cadena ya no coincide.
O Mapper
1
De hecho, uso resharper que lo tiene en cuenta, pero veo su punto.
atikot
44
@atikot, yo también, pero Resharper solo genera una advertencia, no un error del compilador. Hay una diferencia entre una certeza y un buen consejo.
Jodrell
1
@atikot y Resharper no verifica los mensajes de registro
Jodrell
2
@Jodrell: Y, sospecho, tampoco verifica varios otros usos, ¿qué hay de los enlaces WPF creados en código subyacente, OnPropertyChangedmétodos personalizados (que aceptan directamente nombres de propiedades en lugar de PropertyChangedEventArgs) o llamadas a la reflexión para buscar un determinado miembro o tipo?
O Mapper
13

El caso de uso más común que se me ocurre es cuando trabajo con la INotifyPropertyChangedinterfaz. (Básicamente, todo lo relacionado con WPF y enlaces utiliza esta interfaz)

Echale un vistazo a éste ejemplo:

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

Como puede ver en la forma anterior, tenemos que pasar una cadena para indicar qué propiedad ha cambiado. Con nameofpodemos usar el nombre de la propiedad directamente. Esto podría no parecer un gran problema. Pero imagina lo que sucede cuando alguien cambia el nombre de la propiedad Foo. Cuando use una cadena, el enlace dejará de funcionar, pero el compilador no le avisará. Cuando usa nameof, obtiene un error del compilador de que no hay propiedad / argumento con el nombre Foo.

Tenga en cuenta que algunos marcos usan algo de magia de reflexión para obtener el nombre de la propiedad, pero ahora tenemos el nombre de esto ya no es necesario .

Roy T.
fuente
55
Si bien este es un enfoque válido, un enfoque más conveniente (y SECO) es utilizar el [CallerMemberName]atributo en el parámetro de un nuevo método para generar este evento.
Drew Noakes
1
Estoy de acuerdo en que CallerMemberName también es bueno, pero es un caso de uso separado, ya que (como dijiste) solo puedes usarlo en métodos. En cuanto a DRY, no estoy seguro de si [CallerMemberName]string x = nulles mejor que nameof(Property). Se podría decir que el nombre de la propiedad se usa dos veces, pero ese es básicamente el argumento pasado a una función. No es realmente lo que significa DRY, creo :).
Roy T.
En realidad puedes usarlo en propiedades. Ellos también son miembros. El beneficio nameofes que el configurador de propiedades no necesita especificar el nombre de la propiedad, eliminando la posibilidad de copiar / pegar errores.
Drew Noakes
44
Es una situación de "mejor juntos" INotifyPropertyChanged, ya que [CallerMemberNameAttribute]permite que la notificación de cambio se genere limpiamente desde un configurador de propiedades, mientras que la nameofsintaxis permite que una notificación de cambio se genere limpiamente desde una ubicación diferente en su código.
Andrew Hanlon
9

El uso más común será en la validación de entrada, como

//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 el primer caso, si refactoriza el método que cambia el nombre del parámetro par , probablemente se olvide de cambiar eso en ArgumentNullException . Con nameof no tienes que preocuparte por eso.

Vea también: nameof (C # y Referencia de Visual Basic)

Massimo Prota
fuente
7

El proyecto ASP.NET Core MVC se usa nameofen AccountController.csy ManageController.cscon el RedirectToActionmétodo para hacer referencia a una acción en el controlador.

Ejemplo:

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

Esto se traduce en:

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

y lleva al usuario a la acción 'Índice' en el controlador 'Inicio', es decir /Home/Index.

Fred
fuente
¿Por qué no ir por completo y usar return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000
@ Suncat2000 porque una de esas cosas se hace en compilación y la otra no. :)
Dinerdo
6

Como otros ya han señalado, el nameofoperador inserta el nombre que se le dio al elemento en el código fuente.

Me gustaría agregar que esta es una muy buena idea en términos de refactorización, ya que hace que esta refactorización de cadenas sea segura. Anteriormente, utilicé un método estático que utilizaba la reflexión para el mismo propósito, pero que tiene un impacto en el rendimiento del tiempo de ejecución. El nameofoperador no tiene impacto en el rendimiento del tiempo de ejecución; hace su trabajo en tiempo de compilación. Si observa el MSILcódigo, encontrará la cadena incrustada. Consulte el siguiente método y su código desmontado.

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

Sin embargo, eso puede ser un inconveniente si planea ofuscar su software. Después de la ofuscación, la cadena incrustada ya no puede coincidir con el nombre del elemento. Los mecanismos que dependen de este texto se romperán. Ejemplos de eso, incluidos, entre otros, son: Reflection, NotifyPropertyChanged ...

Determinar el nombre durante el tiempo de ejecución cuesta algo de rendimiento, pero es seguro para la ofuscación. Si la ofuscación no es necesaria ni planificada, recomendaría usar el nameofoperador.

cel sharp
fuente
5

Tenga en cuenta que usa una variable en su código y necesita obtener el nombre de la variable y digamos imprimirla, debe usar

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

Y si alguien refactoriza el código y usa otro nombre para "myVar", él / ella tendría que estar atento al valor de la cadena en su código y cambiarlo en consecuencia.

En cambio si tuvieras

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

¡Sería útil refactorizar automáticamente!

cnom
fuente
Desearía que hubiera habido una sintaxis especial de parámetros variables que pasaría una matriz de tuplas, una para cada parámetro, que contiene la representación del código fuente, el Typey el valor. Eso haría posible que los métodos de registro de invocación de código eliminen mucha redundancia.
supercat
5

El artículo de MSDN enumera el enrutamiento MVC (el ejemplo que realmente hizo clic en el concepto para mí) entre varios otros. El párrafo de descripción (formateado) dice:

  • Al informar errores en el código,
  • conectar enlaces modelo-vista-controlador (MVC),
  • La propiedad de disparo cambió los eventos, etc.

a menudo desea capturar el nombre de cadena de un método . Usar nameof ayuda a mantener su código válido al cambiar el nombre de las definiciones.

Antes tenía que usar literales de cadena para referirse a las definiciones, lo cual es frágil al cambiar el nombre de los elementos de código porque las herramientas no saben verificar estos literales de cadena.

Las respuestas aceptadas / mejor valoradas ya ofrecen varios ejemplos concretos excelentes.

brichins
fuente
3

El propósito del nameofoperador es proporcionar el nombre de origen de los artefactos.

Por lo general, el nombre de origen es el mismo nombre que el nombre de metadatos:

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

Pero esto puede no ser siempre el caso:

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

O:

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

Un uso que le he estado dando es para nombrar recursos:

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

El hecho es que, en este caso, ni siquiera necesitaba las propiedades generadas para acceder a los recursos, pero ahora tengo un tiempo de compilación para verificar que los recursos existan.

Paulo Morgado
fuente
0

Uno de los usos de la nameofpalabra clave es para configurar Bindingwpf mediante programación .

para configurar Bindingdebe configurar Pathcon una cadena, y con una nameofpalabra clave, es posible usar la opción Refactorizar.

Por ejemplo, si tiene una IsEnablepropiedad de dependencia en su UserControly desea vincularla a IsEnablealguna CheckBoxen su UserControl, puede usar estos dos códigos:

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

y

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

Es obvio que el primer código no puede refactorizar, pero el primero ...

Mehdi Khademloo
fuente
0

Anteriormente estábamos usando algo así:

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

Motivo: seguridad en el tiempo de compilación. Nadie puede renombrar silenciosamente la propiedad y romper la lógica del código. Ahora podemos usar nameof ().

QtRoS
fuente
0

Tiene ventaja cuando usa ASP.Net MVC. Cuando usa el ayudante HTML para generar algún control a la vista, usa nombres de propiedad en la atribución de nombre de la entrada html:

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

Hace algo así:

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

Entonces, si necesita validar su propiedad en el método Validar, puede hacer esto:

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

En caso de que cambie el nombre de su propiedad utilizando herramientas de refactorización, su validación no se interrumpirá.

dgtlfx
fuente
0

Otro caso de uso nameofes verificar las páginas de pestañas, en lugar de verificar el índice, puede verificar la Namepropiedad de las páginas de pestañas de la siguiente manera:

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

Menos desordenado :)

muerte
fuente
0

Me parece que nameofaumenta la legibilidad de las declaraciones SQL muy largas y complejas en mis aplicaciones. Hace que las variables se destaquen de ese mar de cadenas y elimina su trabajo de averiguar dónde se usan las variables en sus declaraciones 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
fuente