![](/img/trans.png)
[英]Obtaining the vcxproj AdditionalOptions field from within a VSIX extension
[英]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.