与这个问题类似,我希望将可选参数与params关键字混合使用,这当然会造成歧义。不幸的是,创建重载的答案不起作用,因为我想利用调用者信息属性,如下所示:
public void Info(string message, [CallerMemberName] string memberName = "",
[CallerLineNumber] int lineNumber = 0, params object[] args)
{
_log.Info(BuildMessage(message, memberName, lineNumber), args);
}在没有可选参数的情况下创建重载会改变调用站点,从而阻止这些特定参数正常工作。
我找到了一个几乎可行的解决方案(尽管它很难看):
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,该重载占用较少的参数,这不是所希望的行为。
有什么方法可以做到这一点(也许使用一些我还没有学到的新属性?)或者,我们是否已经达到了自魔法编译器支持所能提供的极限?
发布于 2014-07-24 15:12:15
我更喜欢的方式是:只在头上设置了两个字符--丑陋的语言'hack‘;
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实现
Info()("hello world {0} {1} {2}",1,2,3);替代
我的学院做这个工作的方式是这样的:
public static class DebugHelper
public static Tuple<string,int> GetCallerInfo(
[CallerMemberName] string memberName = "",
[CallerLineNumber] int lineNumber = 0)
{
return Tuple.Create(memberName,lineNumber);
}
}InfoMethod:
public void Info(Tuple<string,int> info, string message, params object[] args)
{
_log.Info(BuildMessage(message, info.Item1, info.Item2), args);
}用法:
instance.Info(DebugHelper.GetCallerInfo(),"This is some test {0} {1} {2}",1,2,3);发布于 2014-11-06 16:46:12
所以,我实际上遇到了这个问题,但原因不同。最后我就这样解决了。
首先,C#中的过载解析(泛型方法是理想的候选方法)。我使用T4生成这些扩展方法重载,支持最多9个参数。下面是一个只有3个参数的例子。
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属性列表匹配的任何参数组合时,最终会产生歧义。为了防止这种情况发生,您需要一个类型来保护可选参数列表,并将其与可选参数列表分开。
一个空的结构会做得很好(我用长的和描述性的名称来描述这类事情)。
/// <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没有这个问题。
在实际参数列表和可选参数列表之间插入此类型。
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属性值(编译器应该提供的),那么它也会导致编译器错误,当您的方法接受额外的参数时,我有一些这样的调用,会立即导致编译器错误。
发布于 2014-07-24 15:50:43
根据其他人提供的答案,我可以看到,它们主要是基于首先捕获上下文,然后使用捕获的上下文调用日志方法。我想出了这个:
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())的日志,其用法如下:
Log.Info().Log("My Message: {0}", arg);在我看来,语法似乎稍微清晰一些(复制日志仍然很难看,但它是这样的),我认为在上下文中使用一个结构可能会使它在性能上稍微好一些,尽管我必须对其进行分析才能确定。
https://stackoverflow.com/questions/24936586
复制相似问题