[英]Optional parameters together with an array of parameters
我有一個日志記錄界面,我擴展了一些有用的擴展方法,使我可以傳入一個格式和一個參數列表,以避免每次調用方法時都必須使用字符串格式。 (它也幫助我遵循FXCops文化信息規則)
所以我可以打電話:
logger.Debug("Created {0} with id {1}",typeof(MyObject).Name ,myObject.Id);
代替:
logger.Debug(string.Format("Created {0} with id {1}", typeof(MyObject).Name, myObject.Id));
我現在發現自己處於一個棘手的狀態,因為在日志中獲取有關日志寫入位置的一些信息(例如文件,方法和行號)會非常有幫助。 這可以通過整潔的[CallerMemberName]
, [CallerFilePath]
和[CallerLineNumber]
屬性來實現。
logger.Debug("Created {0} with id {1}", typeof(MyObject).Name, myObject.Id);
然后會給我一個日志條目,例如:
“MyObjectProvider.cs,Provide,line:50 |創建了ID為1564的MyObject”
這里的問題是方法簽名看起來像這樣:
public static void Debug(this ILogger logger, string format [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, params object[] args)
這是不可能的,因為[Caller*]
屬性使參數成為可選參數,並且不適用於args參數。
我還嘗試使用固定數量的字符串作為參數進行多次實現,如下所示:
public static void Debug(this ILogger logger, string format [CallerMemberName] string callerMemberName = "",string arg, string arg2 , ...etc... , [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
但后來我得到編譯器錯誤,說“以下方法或屬性之間的調用是模糊的”
我現在幾乎放棄了這個問題,但我心想,“也許我可以為我找到一個解決方案”。 所以這里是...... 是否有可能以任何方式使用params object[] args
和[CallerFilePath]
,還是有另一種方法來獲得預期的結果?
您不能在方法簽名中組合這兩者。 你可以做的是一個或另一個並傳入null
到你需要可選參數的地方,這對你有用嗎?
Foo(s, null);
public void Foo(string s, params string[] sArray)
{
}
Foo(new string[] {""});
private static void Foo(string[] sArray, string s = "")
{
}
要么
為什么不使用一個處理格式化的類並使其可選?
public class LogArgs
{
private string _formatString;
private string[] _args;
public LogArgs(string formatString, params string[] args)
{
_formatString = formatString;
_args = args;
}
public override string ToString()
{
return string.Format(_formatString, _args);
}
}
public void Foo(string mandatory, LogArgs optionalParam = null)
{
//Do Stuff
}
Foo("", new LogArgs("{0} is formatted", ""));
我找到了另一種使用StackTrace獲取所需信息的方法。 它在優化的代碼中有點不安全,而且速度非常慢但是為了進行調試,只要可以在發布版本中將其關閉,它就能很好地工作。
StackTrace stackTrace = new StackTrace();
var callerMember = stackTrace.GetFrame(1).GetMethod();
var callerMemberName = callerMember.Name;
var callerType = callerMember.ReflectedType.Name;
我遇到了同樣的問題,但解決方法有點不同。 這不是最優雅的解決方案,但它的工作原理相對干凈:
public class SrcLoc
{
public string sourceFile { get; set; }
public int lineNumber { get; set; }
public SrcLoc([CallerFilePath] string sourceFile = "",
[CallerLineNumber] int lineNumber = 0)
{
this.sourceFile = sourceFile;
this.lineNumber = lineNumber;
}
}
public class Logger
{
public void Log(SrcLoc location,
int level = 1,
string formatString = "",
params object[] parameters)
{
string message = String.Format(formatString, parameters);
}
}
public MainTest
{
public static void Main()
{
string file="filename";
logger.Log(new SrcLoc(), (int)LogLevel.Debug, "My File: {0}", file);
}
}
我發現最優雅的方式(或者最不優雅的方式)是創建一個具有所需名稱的方法,該方法提取屬性信息並返回一個Action
委托。 然后,使用您實際要調用的簽名設置此委托。
所以,來自
public static void Debug(this ILogger logger, string format, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, params object[] args)
創建一個委托
public delegate void LogDelegate(string format, params object[] args);
從您的方法調用返回:
public static void Debug(this ILogger logger, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
return (format, args)
{
LogWithCallerSiteInfo(format, args, callerMemberName, callerFilePath, callerLineNumber, logAction);
}
}
並使用捕獲的數據調用輔助方法:
private static void LogWithCallerSiteInfo(string format, object[] args, string callerMemberName, string callerFilePath, int callerLineNumber, Action<string, object[]> logRequest)
{
if (args == null)
{
args = new object[0];
}
var args2 = new object[args.Length + 3];
args.CopyTo(args2, 0);
args2[args.Length] = sourceFile;
args2[args.Length + 1] = memberName;
args2[args.Length + 2] = lineNumber;
logRequest(format + " [{callerFilePath:l}.{callerMemberName:l}-{callerLineNumber}]", args2);
}
然后撥打電話:
logger.Debug()("Created {0} with id {1}",typeof(MyObject).Name ,myObject.Id);
因此,在使用術語中,您插入一組額外的()
,它捕獲呼叫站點信息,而集合組則在委托上進行調用。 這就像我設法做到的那樣整潔。
我重新創建了捕獲數據中的params
數組,否則,(至少在SeriLog
,結果是不可預測的。
將所有默認參數移動到右側。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.