将CallerMemberName与params结合使用

Emi*_*ile 26 c# string-formatting optional-parameters c#-5.0

现在(C#4.0)我们的日志记录方法看起来像

public void Log(string methodName, string messageFormat, params object[] messageParameters)
Run Code Online (Sandbox Code Playgroud)

记录器执行字符串格式化的位置,以便调用者没有放置String.Format来创建一个很好的日志消息(如果没有附加logviewer,则允许记录器跳过字符串格式).

使用c#5.0我想通过使用新的CallerMemberName属性来摆脱methodName参数,但我不知道如何将它与'params'关键字结合起来.有没有办法做到这一点?

小智 33

你可以这样做:

protected static object[] Args(params object[] args)
{
    return args;
}

protected void Log(string message, object[] args = null, [CallerMemberName] string method = "")
{
    // Log
}
Run Code Online (Sandbox Code Playgroud)

要使用日志,请执行以下操作:

Log("My formatted message a1 = {0}, a2 = {2}", Args(10, "Nice"));
Run Code Online (Sandbox Code Playgroud)


svi*_*ick 16

我相信你根本无法组合params和可选参数,这是必需的CallerMemberName.您可以做的最好的事情是使用实际数组而不是params.


Dai*_*Dai 5

为了以 @guilhermekmelo 的答案为基础,我可能建议使用链式方法:

所以保留你当前的Log(string,string,object[]方法:

public void Log(string methodName, string messageFormat, params object[] messageParameters)
Run Code Online (Sandbox Code Playgroud)

并添加这个新的重载 ( Log(string,string)):

public LogMessageBuilder Log(string messageFormat, [CallerMemberName] string methodName = null)
{
    // Where `this.Log` is 
    return new LogMessageBuilder( format: messageFormat, logAction: this.Log );
}

public struct LogMessageBuilder
{
    private readonly String format;
    private readonly String callerName;
    private readonly Action<String,String,Object[]> logAction;

    public LogMessageBuilder( String format, String callerName, Action<String,String,Object[]> logAction )
    {
        this.format = format;
        this.callerName = callerName;
        this.logAction = logAction;
    }

    public void Values( params Object[] values )
    {
        this.logAction( this.format, this.callerName, values );
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用:

this.Log( "My formatted message a1 = {0}, a2 = {2}" ).Values( 10, "Nice" );
Run Code Online (Sandbox Code Playgroud)

请注意,它LogMessageBuilder是 a struct,因此它是值类型,这意味着它不会导致另一次 GC 分配 - 尽管使用params Object[]会导致调用站点处的数组分配。(我希望 C# 和 .NET 支持基于堆栈的可变参数,而不是使用堆分配的参数数组来伪造它)。


另一种选择是使用FormattableString- 但请注意,由于 C#编译器具有内置的特殊情况魔法,因此FormattableString您需要小心不要让它隐式转换为String(而且您无法添加扩展方法也很糟糕)直接FormattableString抱怨):

public void Log(FormattableString fs, [CallerMemberName] string methodName = null)
{
    
    this.Log( messageFormat: fs.Format, methodName: methodName, messageParameters: fs.GetArguments() );
}
Run Code Online (Sandbox Code Playgroud)

用法:

this.Log( $"My formatted message a1 = {10}, a2 = {"Nice"}" );
Run Code Online (Sandbox Code Playgroud)