【问题标题】:How can I get any expression in C# as string?如何将 C# 中的任何表达式作为字符串?
【发布时间】:2011-08-23 09:07:27
【问题描述】:

在很多情况下,我们需要一个表达式、参数、语句等的名称。例如:

public abstract void Log(string methodName, string parameterName, string message);

public void FooMethod(string value)
{
    if (value == null)
    {
        this.Log("FooMethod", "value", "The value must be whatever...");
        throw new ArgumentNullException("value");
    }

    if (value.Length < 5)
    {
        this.Log("FooMethod", "value.Length", "The value length must be whatever...");
        throw new ArgumentException("value");
    }
}

有什么方法可以自动获取这些字符串文字,例如使用可以使用typeof(string) 之类的关键字? 或者有没有一种基于反射的简单高效的方法?

我不是在寻找检查和记录此参数的方法(这实际上只是一个示例)。我正在寻找一种方法来获取部分代码为string

以下会更准确,可以由编译器检查,并且在重构代码时也会考虑:

public void FooMethod(string value)
{
    if (value == null)
    {
        this.Log(literal(this.FooMethod), literal(value), "The parameter '" + literal(value) + "' must be whatever...");
        throw new ArgumentNullException(literal(value));            
    }

    if (value.Length < 5)
    {
        this.Log(literal(this.FooMethod), literal(value.Length), "The value length must be whatever...");
        throw new ArgumentException(literal(value));
    }
}

【问题讨论】:

  • 您的第二个样本是假的。在throw 之后不会执行任何代码。
  • 感谢您的建议。我更改了示例。

标签: .net keyword string-literals


【解决方案1】:

您可以为所有可能的类型创建这样的静态方法。下面是方法名。

public static string GetString(Action obj)
{
    return obj.Method.Name;
}

public static string GetString(Delegate obj )
{
    return obj.Method.Name;
}

【讨论】:

    【解决方案2】:

    听起来你需要StackTrace.GetFrame。此方法将为您提供所需的 StackFrame 对象,您可以从中找到此信息。

    查看以下代码示例

    StackFrame fr = new StackFrame(1,true);
    StackTrace st = new StackTrace(fr);
    this.Log.WriteEntry(fr.GetMethod().Name,
                        fr.GetMethod().GetParameters()[0].Name,
                        "The value must be whatever...");
    

    顺便说一句,在您的第二个代码示例中,Log 方法调用将永远不会执行,因为在调用之前会引发异常。抛出异常后,方法中的任何代码都不会执行,finally 块中的代码除外。见this article

    【讨论】:

    • 不幸的是,获取堆栈帧可能需要几微秒甚至几毫秒,这超出了预期的持续时间几个数量级!
    【解决方案3】:

    这取决于你的目标是什么。如果您希望获得运行时值 Martin Doms 答案可能是解决方案。如果您希望能够将 ArgumentNullException 链接到特定的参数名称,并确保在更改名称时可以使用重构,您可以执行以下操作:

     public static void NotNull<T>(Expression<Func<IEnumerable<T>>> exp)
            {
                var expBody = exp.Body as MemberExpression;
                    T value = exp.Compile().Invoke();
                    if (value == null){
                        string memberName;
                        if (expBody != null){
                            memberName = expBody.Member.Name;
                        } else{
                            memberName = "member";
                        }
                        throw new ArgumentNullException(memberName, memberName + " cannot be null");
                    }
            }
    

    你可以像这样使用它:

    public void Method(string value){
      Ensure.NotNull(()=>value);
    }
    

    它将检查值是否为空,如果值为空,则将适当的消息格式化到 ArgumentNullException。 但是,如果您使用的是 .NET 4.0,我建议您使用合同来代替这些东西。并不是说它实际上解决了您在这里的特殊需求,但与“老式方式”相比,使用 Contracts 进行参数检查在文档和静态分析方面有很多好处

    【讨论】:

    • 其实我不是在寻找检查价值的解决方案,这已经可以通过代码合同完成。我正在寻找一种将部分代码作为字符串获取的方法。
    • @matthias:在您的示例中,您正在检查值,然后如果值是 X,则使用代码的属性让您到达那里,我认为这是您的使用场景。这就是上面的代码 sn-p 所做的。您无法使用合同获取名称,您可以使用表达式。但正如我所说,这取决于你什么时候不会。如果你想运行时值去堆栈帧,如果你想检查一个表达式,你需要将它表示为一个表达式树
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-04
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多