如何在不将其写入C#中的字符串文字的情况下引用标识符?

ygo*_*tti 5 c#

我经常想这样做:

public void Foo(Bar arg)
{
  throw new ArgumentException("Argument is incompatible with " + name(Foo));
}
Run Code Online (Sandbox Code Playgroud)

因为如果我更改了Foo的名称,IDE也会重构我的错误消息,如果我将方法的名称(或任何其他类型的成员标识符)放在字符串文字中,将不会发生什么.我知道实现"名称"的唯一方法是使用反射,但我认为性能损失超过了可持续性增益,并且它不会涵盖所有类型的标识符.

括号之间的表达式的值可以在编译时计算(如typeof),并通过更改语言规范进行优化以成为一个字符串文字.你认为这是一个有价值的功能吗?

PS:第一个例子看起来问题只与例外有关,但事实并非如此.想想您可能想要引用类型成员标识符的每种情况.你必须通过字符串文字来做,对吗?

另一个例子:

[RuntimeAcessibleDocumentation(Description="The class " + name(Baz) +
  " does its job. See method " + name(DoItsJob) + " for more info.")]
public class Baz
{
  [RuntimeAcessibleDocumentation(Description="This method will just pretend " +
    "doing its job if the argument " + name(DoItsJob.Arguments.justPretend) +
    " is true.")]
  public void DoItsJob(bool justPretend) 
  {
    if (justPretend)
      Logger.log(name(justPretend) + "was true. Nothing done.");
  }
}
Run Code Online (Sandbox Code Playgroud)

更新:这个问题是在C#6之前发布的,但可能仍然适用于那些使用该语言的早期版本的人.如果您使用的是C#6,请查看nameof运算符,它与name上面示例中的运算符完全相同.

Mar*_*ell 11

好吧,你可以欺骗和使用类似的东西:

public static string CallerName([CallerMemberName]string callerName = null)
{
    return callerName;
}
Run Code Online (Sandbox Code Playgroud)

和:

public void Foo(Bar arg)
{
  throw new ArgumentException("Argument is incompatible with " + CallerName());
}
Run Code Online (Sandbox Code Playgroud)

在这里,所有工作都由编译器完成(在编译时),因此如果重命名方法,它将立即返回正确的内容.

  • 实际上,@ ItNotALie,**是**编译时构造; 在编译时,它变成:`... + CallerName("Foo")` - 在运行时没有**发生在这里,除了`return callerName`,应该内联 - 所以不会一旦被JIT打破,甚至是静态调用 (4认同)
  • @Gusdor实际上,因为它是一个编译器功能,它将在任何框架版本中都可用,但需要注意的是你可能需要*在正确的命名空间中创建属性*. (2认同)
  • @Gusdor我可以在几分钟内检查一下,但是:我希望*它可以工作,因为其他类似的基于属性的功能(扩展方法等)一直都很好用. (2认同)

xan*_*tos 6

如果您只想要当前的方法名称: MethodBase.GetCurrentMethod().Name

如果是一种类型 typeof(Foo).Name

如果你想要一个变量/参数/字段/属性的名称,有一个Expression小树

public static string GetFieldName<T>(Expression<Func<T>> exp)
{
    var body = exp.Body as MemberExpression;

    if (body == null)
    {
        throw new ArgumentException();
    }

    return body.Member.Name;
}

string str = "Hello World";
string variableName = GetFieldName(() => str);
Run Code Online (Sandbox Code Playgroud)

对于方法名称,它有点棘手:

public static readonly MethodInfo CreateDelegate = typeof(Delegate).GetMethod("CreateDelegate", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(Type), typeof(object), typeof(MethodInfo) }, null);

public static string GetMethodName<T>(Expression<Func<T>> exp)
{
    var body = exp.Body as UnaryExpression;

    if (body == null || body.NodeType != ExpressionType.Convert)
    {
        throw new ArgumentException();
    }

    var call = body.Operand as MethodCallExpression;

    if (call == null)
    {
        throw new ArgumentException();
    }

    if (call.Method != CreateDelegate)
    {
        throw new ArgumentException();
    }

    var method = call.Arguments[2] as ConstantExpression;

    if (method == null)
    {
        throw new ArgumentException();
    }

    MethodInfo method2 = (MethodInfo)method.Value;

    return method2.Name;
}
Run Code Online (Sandbox Code Playgroud)

当你打电话给他们,你必须指定一个兼容的委托类型(Action,Action<...>,Func<...>...)

string str5 = GetMethodName<Action>(() => Main);
string str6 = GetMethodName<Func<int>>(() => Method1);
string str7 = GetMethodName<Func<int, int>>(() => Method2);
Run Code Online (Sandbox Code Playgroud)

或者更简单地说,不使用表达式:-)

public static string GetMethodName(Delegate del)
{
    return del.Method.Name;
}

string str8 = GetMethodName((Action)Main);
string str9 = GetMethodName((Func<int>)Method1);
string str10 = GetMethodName((Func<int, int>)Method2);
Run Code Online (Sandbox Code Playgroud)

  • 这相对来说很慢,但是如果例外则无关紧要.但是不要在循环中这样做;) (4认同)

ygo*_*tti 0

C# 版本 6 引入了nameof运算符,其工作方式类似于name问题示例中描述的运算符,但有一些限制。以下是C# FAQ 博客中的一些示例和摘录中的一些示例和摘录:

\n\n
(if x == null) throw new ArgumentNullException(nameof(x));\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

您可以在 nameof 表达式中放置更复杂的点名称,但 \xe2\x80\x99s 只是为了告诉编译器在哪里查找:仅使用最终标识符:

\n
\n\n
WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode"\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

注意:自预览版构建以来,nameof 有一些小的设计更改。在预览中,不允许使用像上一个示例中那样的点表达式,其中 person 是范围内的变量。相反,您必须在类型中添加点。

\n
\n