从扩展方法中获取原始变量名称

时间:2016-08-03 15:16:10

标签: c# reflection extension-methods

我们目前正在开发一种日志记录解决方案,并实施了一个扩展方法调用' Log'。当写入日志文件时,我们理想地想要编写原始变量名称(而不是扩展方法中使用的变量名称)。

我们目前要做的是:

public void DoSomeWork()
{
    String testString = "Hey look I'm a string!";
    testString.Log("testString value");
}

使用扩展方法:

public static String Log(this String valueToStore, String name)
{
    // The logging code which uses the 'name' parameter
}

这里的问题是,在较长的代码行上看起来变得困难并且看起来是聚集的。理想的是:

public void DoSomeWork()
{
    String testString = "Hey look I'm a string!";
    testString.Log();
}

使用扩展方法:

public static String Log(this String valueToStore)
{
    // The logging code which is able to retrieve the 
    // value 'testString' from the 'valueToStore' value
}

使用Reflection可以实现这一切吗?我知道nameof选项,但只返回字符串' valueToStore'在扩展方法中使用时。

3 个答案:

答案 0 :(得分:2)

您可以使用public static void Log<T>(Expression<Func<T>> expr) { var memberExpr = expr.Body as MemberExpression; if (memberExpr == null) return; var varName = memberExpr.Member.Name; var varData = expr.Compile()(); // actual logging ... } 来实现这一目标,但在性能方面,它可能不是最佳选择:

var test = "Foo";
Log(() => test);

用法:

nameof

或者,如果您使用的是C#6.0,使用test.Log(nameof(test)); 运算符可以更好一些:

{{1}}

更好的解决方案是利用编译器功能(特别是&#34; Roslyn&#34;编译器)并在编译时提供成员名称。

答案 1 :(得分:1)

不是一个真正的答案,更多的是一个指针,但你可以尝试使用你正在使用的应用程序(例如visual studio),而不是在代码中执行。我的意思是让它重写[variable].Log();[variable].Log([variable])

的所有内容

我很确定必须有一些奇怪的宏或插件才能在编译之前为你做这件事。

答案 2 :(得分:1)

嗯,简短的回答是否定的。编译后,变量名称不保证以未更改的形式保留。该信息必须以某种方式持久存在(例如使用nameof())。此外,变量名称可能不存在("test".GetVarName())。

答案很长:是的,可能,但这是我生命中最荒谬的事情之一:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace Test1
{
    class Program
    {
        static void Main(string[] args)
        {
            var myVarName = "test";
            myVarName.Test();
            Console.ReadKey();
        }
    }

    static class Extensions
    {
        public static void Test(
            this string str,
            [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
            [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
            [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0
        )
        {
            var relevantLine = File.ReadAllLines(sourceFilePath)[sourceLineNumber-1];
            var currMethodName = MethodInfo.GetCurrentMethod().Name;
            var callIndex = relevantLine.IndexOf(currMethodName + "()");
            var sb = new Stack<char>();

            for (var i = callIndex - 2; i >= 0; --i)
            {
                if (Char.IsLetterOrDigit(relevantLine[i]))
                {
                    sb.Push(relevantLine[i]);
                }
            }

            Console.WriteLine(new String(sb.ToArray()));
        }
    }
}