【问题标题】:Combining CallerMemberName with params将 CallerMemberName 与参数组合
【发布时间】:2012-12-30 14:32:48
【问题描述】:

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

public void Log(string methodName, string messageFormat, params object[] messageParameters)

记录器在哪里进行字符串格式化,这样调用者就不必输入 String.Format 来创建漂亮的日志消息(如果没有附加日志查看器,记录器可以跳过字符串格式化)。

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

【问题讨论】:

    标签: c# string-formatting optional-parameters c#-5.0


    【解决方案1】:

    我相信您根本无法将paramsCallerMemberName 所需的可选参数结合起来。最好的办法是使用实​​际数组而不是 params

    【讨论】:

      【解决方案2】:

      你可以这样做:

      protected static object[] Args(params object[] args)
      {
          return args;
      }
      
      protected void Log(string message, object[] args = null, [CallerMemberName] string method = "")
      {
          // Log
      }
      

      要使用日志,请这样做:

      Log("My formatted message a1 = {0}, a2 = {2}", Args(10, "Nice"));
      

      【讨论】:

        【解决方案3】:

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

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

        public void Log(string methodName, string messageFormat, params object[] messageParameters)
        

        并添加这个新的重载 (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 );
            }
        }
        

        这样使用:

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

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


        另一种选择是使用FormattableString - 但请注意,由于FormattableString 的 C# compiler has built-in special-case magic 您需要小心不要让它被隐式转换为 String(也很糟糕,您可以'不要直接将扩展方法添加到FormattableString,抱怨):

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

        用法:

        this.Log( $"My formatted message a1 = {10}, a2 = {"Nice"}" );
        

        【讨论】:

        • 感谢您对这个老问题的详尽回复。随着插值字符串的引入,问题本身就解决了。在调用站点,代码看起来好多了。
        猜你喜欢
        • 2013-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-21
        相关资源
        最近更新 更多