簡體   English   中英

可選參數以及一系列參數

[英]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.

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