6.0版获得了nameof的新功能,但我不能理解它的目的,因为它只是接受变量名并在编译时将其更改为字符串。

我认为它在使用<T>时可能有一些目的,但当我尝试命名(T)时,它只是打印我一个T而不是使用的类型。

知道目的吗?


当前回答

MSDN文章列出了MVC路由(这个例子让我真正理解了这个概念)。(格式化的)描述段落如下:

当报告代码中的错误时, 连接模型-视图-控制器(MVC)链接, 触发属性改变事件,等等, 你经常想要 捕获方法的字符串名称。使用nameof有助于保存代码 在重命名定义时有效。 在你必须使用字符串字面量之前 来引用定义,这在重命名代码元素时是很脆弱的 因为工具不知道检查这些字符串字面量。

公认的/评分最高的答案已经给出了几个很好的具体例子。

其他回答

正如其他人已经指出的那样,操作符的名称确实插入了源代码中给出的元素名称。

我想补充一点,这在重构方面是一个非常好的想法,因为它使得字符串重构是安全的。以前,我使用了一个静态方法,它利用反射来达到同样的目的,但这对运行时性能有影响。操作符的名称对运行时性能没有影响;它在编译时完成它的工作。如果查看MSIL代码,就会发现嵌入了字符串。请参阅下面的方法及其反汇编代码。

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

然而,如果您打算让软件变得模糊,这可能是一个缺点。经过混淆处理后,嵌入的字符串可能不再匹配元素的名称。依赖于此文本的机制将会崩溃。例如,包括但不限于:Reflection, NotifyPropertyChanged…

在运行时确定名称会损失一些性能,但对于混淆是安全的。如果混淆既不需要也不计划,我建议使用操作符的名称。

MSDN文章列出了MVC路由(这个例子让我真正理解了这个概念)。(格式化的)描述段落如下:

当报告代码中的错误时, 连接模型-视图-控制器(MVC)链接, 触发属性改变事件,等等, 你经常想要 捕获方法的字符串名称。使用nameof有助于保存代码 在重命名定义时有效。 在你必须使用字符串字面量之前 来引用定义,这在重命名代码元素时是很脆弱的 因为工具不知道检查这些字符串字面量。

公认的/评分最高的答案已经给出了几个很好的具体例子。

c# 6.0的nameof特性的另一个用例变得很方便——考虑像Dapper这样的库,它使数据库检索变得更容易。虽然这是一个很棒的库,但您需要在查询中硬编码属性/字段名。这意味着如果你决定重命名你的属性/字段,你很有可能会忘记更新查询来使用新的字段名。使用字符串插值和特性名称,代码变得更容易维护和类型安全。

从链接中给出的例子

没有nameof

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

与nameof

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

它对ArgumentException及其衍生物非常有用:

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

现在,如果有人重构输入参数的名称,异常也将保持最新。

在以前必须使用反射来获取属性或参数名称的某些地方,它也很有用。

在你的例子中,nameof(T)获取类型参数的名称-这也很有用:

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

nameof的另一种用法是用于枚举——通常如果你想要枚举的字符串名称,你可以使用.ToString():

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

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

> "FooBar"

这实际上相对较慢,因为. net保存枚举值(即7)并在运行时查找名称。

用nameof代替:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

现在。net在编译时将枚举名称替换为字符串。


还有一种用法是INotifyPropertyChanged和日志记录——在这两种情况下,你都想把你调用的成员的名字传递给另一个方法:

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

还是……

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

我能想到的最常见的用例是使用INotifyPropertyChanged接口时。(基本上所有与WPF和绑定相关的东西都使用这个接口)

看看这个例子:

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

正如您可以看到的,在旧的方法中,我们必须传递一个字符串来指示哪个属性已经更改。使用nameof,我们可以直接使用属性的名称。这可能看起来没什么大不了的。但是想象一下如果有人改变了属性Foo的名字会发生什么。当使用字符串时,绑定将停止工作,但编译器不会警告你。当使用nameof时,你会得到一个编译器错误,没有名称Foo的属性/参数。

请注意,有些框架使用一些反射魔法来获取属性的名称,但现在我们不再需要nameof this了。