2013-01-24 36 views
4

我有一個日誌記錄界面,我用一些有用的擴展方法進行了擴展,以便我可以傳遞格式和參數列表以避免每次都使用字符串格式一個電話的方法。 (這也幫助我跟着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,提供,行:50 |創建myObject的ID爲1564」

這裏的問題是,方法簽名是這樣的:

public static void Debug(this ILogger logger, string format [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, params object[] args) 

,這是不可能的,因爲[Caller*]屬性MAK es參數是可選的,並且不適用於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) 

但後來我得到編譯器錯誤稱「的號召是以下方法或屬性之間的曖昧」

現在我幾乎已經放棄了這個問題,但我想,「也許SO可以爲我找到解決方案。」所以這裏是... 是否有可能以任何方式同時使用params object[] args[CallerFilePath],或者有其他方法可以獲得預期結果嗎?

+0

另見http://stackoverflow.com/questions/3948971/c-sharp -4-0-optional-parameters-and-params-do-not-together-together – nawfal

回答

1

您不能在方法簽名中組合這兩者。你可以做的是一個或另一個,並通過null到你需要可選參數的地方,這會對你有用嗎?

Foo(s, null); 
public void Foo(string s, params string[] sArray) 
{ 

} 

Foo(new string[] {""}); 
private static void Foo(string[] sArray, string s = "") 
{ 
} 

OR

爲什麼不使用它處理您的格式類,並作出這樣的可選?

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", "")); 
+0

@Animal我所說的是你可以保持你的方法一樣,但你只需要犧牲使用可選參數或參數。就個人而言,我會用'object [] args'替換'params object [] args',然後以這種方式傳遞數組 - 你明白我的意思嗎? – LukeHennerley

+0

這會工作,但對該方法的調用看起來非常混亂,並且它們不會很簡單,因爲您必須創建string.format參數的數組。如果這是唯一的解決方案,那麼在記錄日誌之前格式化消息可能會更好。 – Moriya

+0

@Animal我明白了......你對我的編輯有什麼想法?這可能有幫助嗎? – LukeHennerley

-2

將所有默認參數移至右側。

+0

生成編譯器錯誤:「無法爲參數數組指定默認值」 – Moriya

+1

不能這樣做,至少要把你的代碼到視覺工作室並看看它是否先編譯 – LukeHennerley

+0

將默認參數移動到右側會使參數位於中間,並且不會編譯 「參數數組必須是形式參數列表中的最後一個參數」 – Moriya

1

我發現另一種方法來使用StackTrace獲取我想要的信息。這在優化代碼中有點不安全,而且非常慢,但爲了調試目的,只要可以在發佈版本中關閉它,它就可以很好地工作。

StackTrace stackTrace = new StackTrace(); 
var callerMember = stackTrace.GetFrame(1).GetMethod(); 
var callerMemberName = callerMember.Name; 
var callerType = callerMember.ReflectedType.Name; 
1

我遇到了同樣的問題,但解決它有點不同。這不是最優雅的解決方案,但它的工作原理,它是比較乾淨的:

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

我發現的最優雅的方式(或至少不優雅的方式)是創建與提取的屬性信息所需的名稱的方法並返回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); 
    } 
} 

,並呼籲與捕獲的數據的helper方法:

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,結果是不可預知的。