Qual é o propósito do nome?


263

A versão 6.0 possui um novo recurso nameof, mas não consigo entender o objetivo, pois apenas pega o nome da variável e altera para uma string na compilação.

Eu pensei que poderia ter algum objetivo ao usar, <T>mas quando eu tento nameof(T)apenas me imprime um em Tvez do tipo usado.

Alguma idéia do propósito?



28
Não havia como conseguir isso Tantes. Havia uma maneira de obter o tipo usado antes.
31915 Jon Jon

Inicialmente, parecia um exagero e ainda não vejo uma razão convincente para usá-lo. Talvez um exemplo do MVC?
Corey Alix

15
Definitivamente útil ao refinar / renomear o nome dentro nameof. Também ajuda a evitar erros de digitação.
BVJ

4
A documentação oficial do nome do foi movida para aqui: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - Ela também lista os principais casos de uso que servem como uma resposta muito boa para a pergunta.
Markus s

Respostas:


323

E os casos em que você deseja reutilizar o nome de uma propriedade, por exemplo, ao lançar uma exceção com base no nome de uma propriedade ou ao manipular um PropertyChangedevento. Existem inúmeros casos em que você gostaria de ter o nome da propriedade.

Veja este exemplo:

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

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

No primeiro caso, renomear SomePropertytambém mudará o nome da propriedade ou interromperá a compilação. O último caso não.

Essa é uma maneira muito útil de manter seu código compilado e sem erros (mais ou menos).

(Um artigo muito bom de Eric Lippert, por infoofque não conseguiu, enquanto o nameoffez)


1
Eu entendo o ponto, apenas adicionando que o re-afiador altera as strings ao refatorar nomes, não tenho certeza se o VS tem funcionalidade semelhante.
Ash Burlaczenko

7
Tem. Mas o Resharper e o VS não funcionam em projetos, por exemplo. Isso faz. De fato, esta é a melhor solução.
Patrick Hofman 29/07

49
Outro caso de uso comum é o roteamento no MVC usando nameofe o nome da ação em vez de uma sequência codificada.
RJ Cuthbertson

2
@sotn Não sei se entendi o que você está perguntando. Não há nada que o impeça de usá-lo como public class MyController { public ActionResult Index() { return View(nameof(Index)); } }- e você pode usar nameofem membros não estáticos (por exemplo, você pode chamar nameof(MyController.Index)usando a classe acima e ela emitirá "Índice"). Confira os exemplos em msdn.microsoft.com/en-us/library/…
RJ Cuthbertson

2
Não vejo por que isso é especial. Nomes de variáveis ​​são sempre os mesmos, certo? Se você tiver uma instância ou não, os nomes das variáveis não vai mudar @sotn
Patrick Hofman

176

É realmente útil para ArgumentExceptione seus derivados:

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

Agora, se alguém refatorar o nome do inputparâmetro, a exceção também será mantida atualizada.

Também é útil em alguns lugares onde anteriormente era necessário usar a reflexão para obter os nomes de propriedades ou parâmetros.

No seu exemplo, nameof(T)obtém o nome do parâmetro type - isso também pode ser útil:

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

Outro uso de nameofé para enumerações - geralmente se você quiser o nome da string de uma enumeração, use .ToString():

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

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

> "FooBar"

Na verdade, isso é relativamente lento, já que o .Net mantém o valor da enumeração (ie 7) e encontra o nome no tempo de execução.

Em vez disso, use nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

Agora .Net substitui o nome da enumeração por uma sequência em tempo de compilação.


Ainda outro uso é para coisas como INotifyPropertyChangede registro - nos dois casos, você deseja que o nome do membro que você está chamando seja passado para outro 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));
    }
}

Ou...

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

9
E você colocou outro recurso interessante em: interpolação de strings!
Patrick Hofman 29/07

1
@PatrickHofman e typeof(T), que é outro pedaço de açúcar em tempo de compilação que é útil em circunstâncias semelhantes :-)
Keith

1
Uma coisa que estou perdendo é algo como nameofthismethod. Você pode usar, Log.Error($"Error in {nameof(DoSomething)}...")mas se copiar e colar isso para outros métodos, não perceberá que ainda está se referindo DoSomething. Portanto, enquanto trabalha perfeitamente com variáveis ​​ou parâmetros locais, o nome do método é um problema.
Tim Schmelter

3
Você sabe se nameOfusará o [DisplayName]atributo se presente? Para o enumexemplo, eu uso [DisplayName]muitas vezes com projetos MVC
Luke T O'Brien

2
@AaronLS Sim, é bastante especializado e não é algo que você usaria com frequência. No entanto, esse throw newé um outro antipadrão - acho que o uso excessivo catché um problema comum com desenvolvedores juniores porque parece consertar o problema (quando na maioria das vezes o oculta).
Keith

26

Outro caso de uso em que o nameofrecurso do C # 6.0 se torna útil - considere uma biblioteca como o Dapper que facilita muito as recuperações de banco de dados. Embora essa seja uma ótima biblioteca, você precisa codificar os nomes de propriedades / campos na consulta. O que isso significa é que, se você decidir renomear sua propriedade / campo, há grandes chances de você esquecer de atualizar a consulta para usar novos nomes de campo. Com a interpolação de nameofcaracteres e os recursos, o código se torna muito mais fácil de manter e tipificar.

Do exemplo dado no link

sem nome

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

com nome

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

3
Eu amo Dapper e gosto muito de interporções de cordas, mas na IMO isso parece tão feio. O risco de quebrar uma consulta renomeando uma coluna parece tão pequeno em comparação com essas consultas feias. À primeira vista, eu diria que prefiro escrever consultas EF LINQ ou seguir uma convenção como [TableName]. [ColumnName], onde posso encontrar / substituir facilmente minhas consultas quando necessário.
drizin

@drizin Eu uso o Dapper FluentMap para evitar essas consultas (e também para separar preocupações)
mamuesstack

21

Sua pergunta já expressa o objetivo. Você deve ver que isso pode ser útil para registrar ou lançar exceções.

por exemplo.

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

isso é bom, se eu alterar o nome da variável, o código será interrompido ou retornará uma exceção com uma mensagem incorreta .


Obviamente, os usos não se limitam a essa situação simples. Você pode usar nameofsempre que for útil codificar o nome de uma variável ou propriedade.

Os usos são múltiplos quando você considera várias situações de ligação e reflexão. É uma excelente maneira de trazer os erros de tempo de execução para compilar o tempo.


6
@ atikot: Mas, se você renomear a variável, o compilador não notará que a string não corresponde mais.
OR Mapper

1
Na verdade, eu uso o resharper, que leva isso em conta, mas entendo o seu ponto.
Atikot 29/07

4
@ atikot, eu também, mas o Resharper gera apenas um aviso, não um erro do compilador. Há uma diferença entre uma certeza e um bom conselho.
Jodrell

1
@atikot e, Resharper não verifica as mensagens de logon
Jodrell

2
@Jodrell: E, suspeito, ele também não verifica vários outros usos - que tal as ligações WPF criadas em code-behind, OnPropertyChangedmétodos personalizados (que aceitam diretamente nomes de propriedades em vez de PropertyChangedEventArgs) ou chama a reflexão para procurar por um determinado membro ou tipo?
OR Mapper

13

O caso de uso mais comum em que consigo pensar é ao trabalhar com a INotifyPropertyChangedinterface. (Basicamente, tudo relacionado ao WPF e ligações usa essa interface)

Veja este exemplo:

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 você pode ver da maneira antiga, temos que passar uma string para indicar qual propriedade foi alterada. Com nameofpodemos usar o nome da propriedade diretamente. Isso pode não parecer grande coisa. Mas imagine o que acontece quando alguém muda o nome da propriedade Foo. Ao usar uma string, a ligação deixará de funcionar, mas o compilador não avisará. Ao usar nameof, você obtém um erro do compilador de que não há propriedade / argumento com o nome Foo.

Observe que algumas estruturas usam alguma mágica de reflexão para obter o nome da propriedade, mas agora temos o nome disso, que não é mais necessário .


5
Embora essa seja uma abordagem válida, uma abordagem mais conveniente (e DRY) é usar o [CallerMemberName]atributo no parâmetro de um novo método para gerar esse evento.
de Drew Noakes

1
Concordo que CallerMemberName também é bom, mas é um caso de uso separado, pois (como você disse) você só pode usá-lo nos métodos. Quanto ao DRY, não sei se [CallerMemberName]string x = nullé melhor que nameof(Property). Você poderia dizer que o nome da propriedade é usado duas vezes, mas esse é basicamente o argumento passado para uma função. Não é realmente o que se entende com DRY, eu acho :).
Roy T.

Na verdade, você pode usá-lo em propriedades. Eles são membros também. O benefício nameofé que o configurador de propriedades não precisa especificar o nome da propriedade, eliminando a possibilidade de copiar / colar erros.
de Drew Noakes

4
É uma situação 'melhor juntos', para INotifyPropertyChangedusar a opção [CallerMemberNameAttribute]permite que a notificação de alteração seja levantada de maneira limpa a partir de um configurador de propriedades, enquanto a nameofsintaxe permite que a notificação de alteração seja levantada de maneira limpa a partir de um local diferente no seu código.
Andrew Hanlon

9

O uso mais comum será na validação 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));
}

No primeiro caso, se você refatorar o método que altera o nome do parâmetro par , provavelmente esquecerá de alterá-lo na ArgumentNullException . Com o nome de você não precisa se preocupar com isso.

Consulte também: nameof (C # e referência do Visual Basic)


7

O projeto ASP.NET Core MVC usa nameofno AccountController.cse ManageController.cscom o RedirectToActionmétodo para referenciar uma ação no controlador.

Exemplo:

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

Isso se traduz em:

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

e leva o usuário para a ação 'Index' no controlador 'Home', ou seja /Home/Index.


Por que não usar o porco inteiro return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));?
Suncat2000

@ Suncat2000 porque uma dessas coisas é feita em compilação e a outra não? :)
Dinerdo 16/08/18

6

Como outros já apontaram, o nameofoperador insere o nome que o elemento recebeu no código-fonte.

Gostaria de acrescentar que essa é uma idéia muito boa em termos de refatoração, pois torna a refatoração de string segura. Anteriormente, usei um método estático que utilizava a reflexão para o mesmo objetivo, mas que afeta o desempenho em tempo de execução. O nameofoperador não tem impacto no desempenho do tempo de execução; ele faz seu trabalho em tempo de compilação. Se você der uma olhada no MSILcódigo, encontrará a string incorporada. Veja o seguinte método e seu 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

No entanto, isso pode ser uma desvantagem se você planeja ofuscar seu software. Após a ofuscação, a cadeia incorporada pode não corresponder mais ao nome do elemento. Mecanismos que dependem desse texto serão interrompidos. Exemplos para isso, incluindo, entre outros: Reflection, NotifyPropertyChanged ...

Determinar o nome durante o tempo de execução custa algum desempenho, mas é seguro para ofuscação. Se a ofuscação não for necessária nem planejada, eu recomendaria o uso do nameofoperador.


5

Considere que você usa uma variável em seu código e precisa obter o nome da variável e, digamos, imprimi-la, você deve usar

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

E se alguém refatorar o código e usar outro nome para "myVar", ele / ela terá que observar o valor da string no seu código e usá-lo de acordo.

Em vez disso, se você tivesse

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

Ajudaria a refatorar automaticamente!


Eu gostaria que houvesse uma sintaxe especial de parâmetro variável que passasse uma matriz de tuplas, uma para cada parâmetro, contendo a representação do código-fonte, oe Typeo valor. Isso tornaria possível o código que invocava métodos de log para eliminar muita redundância.
Supercat 29/07

5

O artigo do MSDN lista o roteamento MVC (o exemplo que realmente clicou no conceito para mim) entre vários outros. O parágrafo da descrição (formatado) diz:

  • Ao relatar erros no código,
  • conectando links model-view-controller (MVC),
  • propriedade de disparo alterou eventos etc.,

você frequentemente deseja capturar o nome da string de um método . O uso de nameof ajuda a manter seu código válido ao renomear definições.

Antes, era necessário usar literais de cadeia de caracteres para se referir às definições, o que é frágil ao renomear elementos de código, porque as ferramentas não sabem como verificar esses literais de cadeia de caracteres.

As respostas aceitas / com melhor classificação já oferecem vários exemplos concretos excelentes.


3

O objetivo do nameofoperador é fornecer o nome da fonte dos artefatos.

Geralmente, o nome da fonte é o mesmo que o nome dos metadados:

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

Mas isso nem sempre pode ser o caso:

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

Ou:

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

Um uso que tenho dado a ele é para nomear 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))]

O fato é que, nesse caso, eu nem precisava das propriedades geradas para acessar os recursos, mas agora tenho um tempo de compilação para verificar se os recursos existem.


0

Um dos usos da nameofpalavra-chave é para definir programaticamente oBinding wpf .

para definir, Bindingvocê precisa definir Pathcom string e com a nameofpalavra - chave, é possível usar a opção Refatorar.

Por exemplo, se você possui IsEnableuma propriedade de dependência no seu UserControle deseja vinculá-lo a IsEnablealgum CheckBoxno seu UserControl, pode usar estes dois códigos:

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

e

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

É óbvio que o primeiro código não pode refatorar, mas o segundo ...


0

Anteriormente, estávamos usando algo assim:

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

Razão - compile a segurança do tempo. Ninguém pode renomear silenciosamente propriedades e quebrar a lógica do código. Agora podemos usar nameof ().


0

Tem vantagem quando você usa o ASP.Net MVC. Quando você usa o auxiliar HTML para criar algum controle na exibição, ele usa nomes de propriedades no atributo attribure da entrada html:

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

Faz algo assim:

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

Portanto, agora, se você precisar validar sua propriedade no método Validar, faça isso:

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

Caso você renomeie sua propriedade usando ferramentas de refatoração, sua validação não será interrompida.


0

Outro caso de uso de nameofé verificar as guias, em vez de verificar o índice, você pode verificar a Namepropriedade das guias, como segue:

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

Menos bagunçado :)


0

Acho que nameofaumenta a legibilidade de instruções SQL muito longas e complexas em meus aplicativos. Faz com que as variáveis ​​se destacem nesse mar de strings e elimina seu trabalho de descobrir onde as variáveis ​​são usadas em suas instruções 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 },
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.