添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
登录

如何在C#中使用params关键字和调用者信息?

内容来源于 Stack Overflow,遵循 CC BY-SA 4.0 许可协议进行翻译与使用。IT领域专用引擎提供翻译支持

腾讯云小微IT领域专用引擎提供翻译支持

原文
Stack Overflow用户 提问于2014-11-03

我正在尝试将C# 5.0调用者信息与C# params关键字结合起来。我们的目的是为日志框架创建一个包装器,我们希望记录器能够像String.Format那样格式化文本。在以前的版本中,该方法如下所示:

void Log(
   string message,
   params object[] messageArgs = null);

我们这样称呼它:

log.Log("{0}: I canna do it cap'n, the engines can't handle warp {1}!", 
        "Scotty", warpFactor);

现在,我们希望捕获调用者的信息并将其记录下来。所以签名变成:

void Log(
    string message,
    params object[] messageArgs,
    [CallerMemberName] string sourceMemberName = null);

这不能编译,因为params必须是最后一个参数。所以我试试看:

void Log(
    string message,
    [CallerMemberName] string sourceMemberName = null,
    params object[] messageArgs);

有没有一种方法可以在不提供sourceMembername或显式地将messageArgs参数赋值为命名参数的情况下调用它?这样做违背了params关键字的目的:

// params is defeated:
log.Log("message", 
        messageArgs: new object[] { "Scotty", warpFactor });
// CallerMemberName is defeated:
log.Log("message", null,
        "Scotty", warpFactor);

有办法这样做吗?似乎调用者信息被传递的"hacky“方式排除了使用params关键字。如果C#编译器认识到调用者成员信息参数根本不是真正的参数,那就太棒了。我看不出有必要显式地传递它们。

我的备份是跳过params关键字,调用方必须在最后一个示例中使用长签名。

浏览 21 关注 0 得票数 9
  • 得票数为Stack Overflow原文数据
原文
Stack Overflow用户
已采纳
修改于2014-11-03
  • 该回答已被编辑
  • 回答者: Stack Overflow用户
  • 回答时间: 2014-11-03 17:00
得票数 8

我认为这不可能完全按照你想要的方式去做。然而,我可以想到一些可行的解决办法,它们可能会给您带来几乎相同的好处。

  1. 使用中间方法调用来捕获调用方成员名。第一个方法调用返回一个委托,然后调用该委托来提供附加参数。这看起来很奇怪,但它应该能用: log.Log()("{0}:我要这样做,引擎不能处理翘曲{1}!","Scotty",warpFactor); 这里的一个缺点是可以调用 log.Log("something") ,期望您的消息将被记录,不会发生任何事情。如果您使用Resharper,您可以通过向 [Pure] 方法添加一个 Log() 属性来减轻这一点,这样,如果有人没有对结果对象做任何事情,就会得到警告。你也可以稍微调整一下这个方法,说: var log = logFactory.GetLog();// <--注入方法名。日志(“{0}:我要这样做,引擎不能处理翘曲{1}!","Scotty",warpFactor);
  2. 使用lambdas生成日志消息,并让string.Format处理params数组: log.Log(() => string.Format("{0}:我要这样做,引擎不能处理翘曲{1}!","Scotty",warpFactor)); 这是我通常使用的方法,它有一些副作用:

1. Your log method can catch exceptions produced while producing the debug string, so instead of breaking your system you just get an error that says: "Failed to produce log message: [exception details]".
2. Sometimes the object you pass to your format string might incur additional cost, which you'd only want to incur when you need it:

log.Info(() => string.Format("{0}:我要做,引擎不能处理翘曲{1}!",_db.GetCurrentUsername(),warpFactor));

如果没有打开信息级别的日志记录,您希望不让上面的代码执行数据库访问。

顺便提一句,我发现自己经常使用string.Format,以至于我创建了一个帮助器方法来稍微缩短语法:

log.Log(() ) => "{0}:我要做它,引擎不能处理翘曲{1}!“.With("Scotty",warpFactor);

回答于2014-11-03
得票数 1

让我举一个选项;您可以使用反射来获得与 CallMemberName 完全相同的名称。这肯定会慢一些。我相信,假设你不是每毫秒记录一次,那就足够承受压力了。

var stackTrace = new StackTrace(); var methodName = stackTrace.GetFrame(1).GetMethod().Name;

回答于2014-11-03
得票数 5

要使用StriplingWarrior建议而不是委托,可以使用流畅的语法。

public static class Logger
    public static LogFluent Log([CallerMemberName] string sourceMemberName = null)
        return new LogFluent(sourceMemberName);
public class LogFluent
    private string _callerMemeberName;
    public LogFluent(string callerMamberName)
        _callerMemeberName = callerMamberName;
    public void Message(string message, params object[] messageArgs)
}

那就叫它吧

Logger.Log().Message("{0}: I canna do it cap'n, the engines can't handle warp {1}!", "Scotty", 10);

记录器不一定是静态的,但它是演示这个概念的简单方法

回答于2017-03-01
得票数 1

我喜欢跟随(@beefarino) pluralsight课程来设置我自己的日志项目。为了将ILog接口克隆为ILogger,我实现了它--比如在一个名为LoggerAdapter的类中--然后我使用Jim拥有一个GetLogger(类型类型)-Method,该-Method返回一个包装的log4net-logger 'LoggerAdapter':

namespace CommonLogging
    public class LogManager : ILogManager
        private static readonly ILogManager _logManager;
        static LogManager()
            log4net.Config.XmlConfigurator.Configure(new FileInfo("log4net.config"));
            _logManager = new LogManager();
        public static ILogger GetLogger<T>()
            return _logManager.GetLogger(typeof(T));
        public ILogger GetLogger(Type type)
            var logger = log4net.LogManager.GetLogger(type);
            return new LoggerAdapter(logger);
}

下一步是创建像这样的泛型扩展,将调用者信息设置为ThreadContext.Property:

public static class GenericLoggingExtensions
    public static ILogger Log<TClass>(this TClass klass, [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0)
        where TClass : class