首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >不能简单过载时混合可选参数和参数

不能简单过载时混合可选参数和参数
EN

Stack Overflow用户
提问于 2014-07-24 14:31:50
回答 5查看 4.7K关注 0票数 17

这个问题类似,我希望将可选参数与params关键字混合使用,这当然会造成歧义。不幸的是,创建重载的答案不起作用,因为我想利用调用者信息属性,如下所示:

代码语言:javascript
运行
复制
    public void Info(string message, [CallerMemberName] string memberName = "", 
                     [CallerLineNumber] int lineNumber = 0, params object[] args)
    {
        _log.Info(BuildMessage(message, memberName, lineNumber), args);
    }

在没有可选参数的情况下创建重载会改变调用站点,从而阻止这些特定参数正常工作。

我找到了一个几乎可行的解决方案(尽管它很难看):

代码语言:javascript
运行
复制
    public void Info(string message, object arg0, [CallerMemberName] string memberName = "",
                     [CallerLineNumber] int lineNumber = 0)
    {
        _log.Info(BuildMessage(message, memberName, lineNumber), arg0);
    }

    public void Info(string message, object arg0, object arg1, [CallerMemberName] string memberName = "",
                     [CallerLineNumber] int lineNumber = 0)
    {
        _log.Info(BuildMessage(message, memberName, lineNumber), arg0, arg1);
    }

这里的问题是,如果您为最后一个参数指定了一个字符串,则重载解析假设您打算在重载中显式指定memberName,该重载占用较少的参数,这不是所希望的行为。

有什么方法可以做到这一点(也许使用一些我还没有学到的新属性?)或者,我们是否已经达到了自魔法编译器支持所能提供的极限?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-07-24 15:12:15

我更喜欢的方式是:只在头上设置了两个字符--丑陋的语言'hack‘;

代码语言:javascript
运行
复制
public delegate void WriteDelegate(string message, params object[] args);

public static WriteDelegate Info(
      [CallerMemberName] string memberName = "", 
      [CallerLineNumber] int lineNumber = 0)
 {
     return new WriteDelegate ((message,args)=>
     {
         _log.Info(BuildMessage(message, memberName , lineNumber ), args);
     });
 }

用法(提供您自己的BuildMessage实现

代码语言:javascript
运行
复制
Info()("hello world {0} {1} {2}",1,2,3);

替代

我的学院做这个工作的方式是这样的:

代码语言:javascript
运行
复制
public static class DebugHelper

    public static Tuple<string,int> GetCallerInfo(
      [CallerMemberName] string memberName = "", 
      [CallerLineNumber] int lineNumber = 0)
    {
        return Tuple.Create(memberName,lineNumber);
    }
}

InfoMethod:

代码语言:javascript
运行
复制
public void Info(Tuple<string,int> info, string message, params object[] args)
{
      _log.Info(BuildMessage(message, info.Item1, info.Item2), args);
}

用法:

代码语言:javascript
运行
复制
  instance.Info(DebugHelper.GetCallerInfo(),"This is some test {0} {1} {2}",1,2,3);
票数 17
EN

Stack Overflow用户

发布于 2014-11-06 16:46:12

所以,我实际上遇到了这个问题,但原因不同。最后我就这样解决了。

首先,C#中的过载解析(泛型方法是理想的候选方法)。我使用T4生成这些扩展方法重载,支持最多9个参数。下面是一个只有3个参数的例子。

代码语言:javascript
运行
复制
public static void WriteFormat<T1, T2, T3>(this ILogTag tag, string format, T1 arg0, T2 arg1, T3 arg2
    , [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0
    )
{
    if (tag != null)
    {
        var entry = new LogEntry(DateTimeOffset.Now, tag.TagName, new LogString(format, new object[] { arg0, arg1, arg2 }), callerMemberName, System.IO.Path.GetFileName(callerFilePath), callerLineNumber);
        tag.Write(entry);
    }
}

它可以工作一段时间,但当您使用与调用者info属性列表匹配的任何参数组合时,最终会产生歧义。为了防止这种情况发生,您需要一个类型来保护可选参数列表,并将其与可选参数列表分开。

一个空的结构会做得很好(我用长的和描述性的名称来描述这类事情)。

代码语言:javascript
运行
复制
/// <summary>
/// The purpose of this type is to act as a guard between 
/// the actual parameter list and optional parameter list.
/// If you need to pass this type as an argument you are using
/// the wrong overload.
/// </summary>
public struct LogWithOptionalParameterList
{
    // This type has no other purpose.
}

注意:,我想让它成为一个带有私有构造函数的抽象类,但实际上允许将null作为LogWithOptionalParameterList类型传递。struct没有这个问题。

在实际参数列表和可选参数列表之间插入此类型。

代码语言:javascript
运行
复制
public static void WriteFormat<T1, T2, T3>(this ILogTag tag, string format, T1 arg0, T2 arg1, T3 arg2
    , LogWithOptionalParameterList _ = default(LogWithOptionalParameterList)
    , [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0
    )
{
    if (tag != null)
    {
        var entry = new LogEntry(DateTimeOffset.Now, tag.TagName, new LogString(format, new object[] { arg0, arg1, arg2 }), callerMemberName, System.IO.Path.GetFileName(callerFilePath), callerLineNumber);
        tag.Write(entry);
    }
}

哇哦!

这种类型的唯一目的是扰乱过载解析过程,但如果您不小心填写调用方info属性值(编译器应该提供的),那么它也会导致编译器错误,当您的方法接受额外的参数时,我有一些这样的调用,会立即导致编译器错误。

票数 8
EN

Stack Overflow用户

发布于 2014-07-24 15:50:43

根据其他人提供的答案,我可以看到,它们主要是基于首先捕获上下文,然后使用捕获的上下文调用日志方法。我想出了这个:

代码语言:javascript
运行
复制
    public CallerContext Info([CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0)
    {
        return new CallerContext(_log, LogLevel.Info, memberName, lineNumber);
    }

    public struct CallerContext
    {
        private readonly Logger _logger;
        private readonly LogLevel _level;
        private readonly string _memberName;
        private readonly int _lineNumber;

        public CallerContext(Logger logger, LogLevel level, string memberName, int lineNumber)
        {
            _logger = logger;
            _level = level;
            _memberName = memberName;
            _lineNumber = lineNumber;
        }

        public void Log(string message, params object[] args)
        {
            _logger.Log(_level, BuildMessage(message, _memberName, _lineNumber), args);
        }

        private static string BuildMessage(string message, string memberName, int lineNumber)
        {
            return memberName + ":" + lineNumber + "|" + message;
        }
    }

如果您有一个名为LoggerProxy (类定义方法Info())的日志,其用法如下:

代码语言:javascript
运行
复制
Log.Info().Log("My Message: {0}", arg);

在我看来,语法似乎稍微清晰一些(复制日志仍然很难看,但它是这样的),我认为在上下文中使用一个结构可能会使它在性能上稍微好一些,尽管我必须对其进行分析才能确定。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24936586

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档