簡體   English   中英

從擴展方法中獲取原始變量名

[英]Obtaining original variable name from within an extension method

我們目前正在開發一個日志記錄解決方案,並已經實現了一個名為“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
}

這完全可以通過使用反射來實現嗎? 我知道nameof選項,但它只在擴展方法中使用時返回字符串“valueToStore”。

C# 10 有CallerArgumentExpressionAttribute可以做到這一點

public static void PrintNameAndValue(
    this object obj,
    [System.Runtime.CompilerServices.CallerArgumentExpression("obj")] string callerExp = ""
    )
{
    Console.WriteLine(callerExp + " = " + obj.ToString());
}

它將捕獲傳遞的整個表達式:

public void TestPrintNameAndValue()
{
    string mystring = "test";
    int myint = 5;

    mystring.PrintNameAndValue();             // mystring = test
    myint.PrintNameAndValue();                // myint = 5
    (myint + 10).PrintNameAndValue();         // myint + 10 = 15
    mystring.ToUpper().PrintNameAndValue();   // mystring.ToUpper() = TEST
}

您可以使用Expression來實現這一點,但在性能方面,它可能不是最佳選擇:

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

或者,如果您使用的是C#6.0,使用nameof運算符可以獲得更好的nameof

test.Log(nameof(test));

一個更好的解決方案是利用編譯器功能(特別是“Roslyn”編譯器)並在編譯時提供成員名稱。

不是一個答案,更多的是一個指針,但你可以嘗試用你正在使用的應用程序(例如visual studio)做一些事情,而不是在代碼中做。 我的意思是讓它重寫看起來像[variable].Log(); [variable].Log([variable])

我很確定在編譯之前必須有一些奇怪的宏或插件為你做這件事。

好吧,簡短的回答是否定的。 編譯后,變量名稱不保證以未更改的形式保留。 該信息必須以某種方式持久化(例如通過使用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()));
        }
    }
}

我知道這篇文章真的很老了,但是由於 .Net 6 現在有一種方法,我想我會分享讓其他人知道。

您現在可以使用 CallerArgumentExpressionAttribute 如圖所示

    /// <summary>
    /// Will throw argument exception if string IsNullOrEmpty returns true
    /// </summary>
    /// <param name="str"></param>
    /// <param name="str_name"></param>
    /// <exception cref="ArgumentException"></exception>
    public static void ValidateNotNullorEmpty(this string str,
        [CallerArgumentExpression("str")]string str_name=null)
    {       
        if (string.IsNullOrEmpty(str))
        {
            throw new ArgumentException($"'{str_name}' cannot be null or empty.", str_name);
        }
    }

現在打電話給:

param.ValidateNotNullorEmpty();

將拋出錯誤:“參數不能為 null 或為空。”

而不是“str不能為空或為空”

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM