这是我想做的一个例子:
MessageBox.Show("Error line number " + CurrentLineNumber);
上面代码中的
CurrentLineNumber
,应该是这段代码在源码中的行号。
我该怎么做?
在 .NET 4.5 / C# 5 中,您可以通过编写使用新调用者属性的实用程序方法,让编译器为您完成这项工作:
using System.Runtime.CompilerServices;
static void SomeMethodSomewhere()
{
ShowMessage("Boo");
}
...
static void ShowMessage(string message,
[CallerLineNumber] int lineNumber = 0,
[CallerMemberName] string caller = null)
{
MessageBox.Show(message + " at line " + lineNumber + " (" + caller + ")");
}
这将显示,例如:
第 39 行嘘(SomeMethodSomewhere)
还有
[CallerFilePath]
告诉你原始代码文件的路径。
使用StackFrame.GetFileLineNumber方法,例如:
private static void ReportError(string message)
{
StackFrame callStack = new StackFrame(1, true);
MessageBox.Show("Error: " + message + ", File: " + callStack.GetFileName()
+ ", Line: " + callStack.GetFileLineNumber());
}
请参阅 Scott Hanselman 的博客条目了解更多信息。
[编辑:添加以下内容]
对于使用 .Net 4.5 或更高版本的用户,请考虑 System.Runtime.CompilerServices 命名空间中的 CallerFilePath、CallerMethodName 和 CallerLineNumber 属性。例如:
public void TraceMessage(string message,
[CallerMemberName] string callingMethod = "",
[CallerFilePath] string callingFilePath = "",
[CallerLineNumber] int callingFileLineNumber = 0)
{
// Write out message
}
对于
string
和 CallerMemberName
,参数必须是 CallerFilePath
,对于 int
,参数必须是 CallerLineNumber
,并且必须具有默认值。在方法参数上指定这些属性会指示编译器在编译时在调用代码中插入适当的值,这意味着它通过混淆来工作。请参阅来电者信息了解更多信息。
我更喜欢一个衬垫,所以:
int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
在.NET 4.5中,您可以通过创建函数来获取行号:
static int LineNumber([System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0)
{
return lineNumber;
}
然后每次您拨打
LineNumber()
您都会看到当前线路。与使用 StackTrace 的任何解决方案相比,它的优点是它应该在调试和发布中都可以工作。
因此,按照所需的原始请求,它将变成:
MessageBox.Show("Error enter code here line number " + LineNumber());
这是建立在 Marc Gravell 出色答案的基础上的。
对于那些需要 .NET 4.0+ 方法解决方案的人:
using System;
using System.IO;
using System.Diagnostics;
public static void Log(string message) {
StackFrame stackFrame = new System.Diagnostics.StackTrace(1).GetFrame(1);
string fileName = stackFrame.GetFileName();
string methodName = stackFrame.GetMethod().ToString();
int lineNumber = stackFrame.GetFileLineNumber();
Console.WriteLine("{0}({1}:{2})\n{3}", methodName, Path.GetFileName(fileName), lineNumber, message);
}
如何致电:
void Test() {
Log("Look here!");
}
输出:
无效测试()(FILENAME.cs:104)
看这里!
按照您喜欢的方式更改 Console.WriteLine 格式!
如果它在 try catch 块中,请使用它。
try
{
//Do something
}
catch (Exception ex)
{
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);
Console.WriteLine("Line: " + trace.GetFrame(0).GetFileLineNumber());
}
您只询问行号和可为空的项目类型,然后您需要使用类似这样的东西
internal class Utils
{
public static int Line([CallerLineNumber] int? lineNumber =null)=>lineNumber;
}
在您的代码中,如果您想获取行号,则只需调用
var line=Utils.Line();
如果您正在记录日志并且您想记录日志记录中的行号,而不是调用这样的方法
public void MyMethod(int someValue)
{
switch(someValue)
{
case 1:
if(abc<xyz)
{
logger.LogInformation("case value {someValue} this line {line} was true", someValue ,Utils.Line()-2);
}
break;
case 2:
logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
break;
caste 3:
logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
break;
}
}
您可以使用任何其他 [CallerXXX] 方法扩展此模式,并且不要在任何地方使用它们,而不仅仅是在方法参数中。
在 Nuget 包中Walter我使用了一个名为 ExceptionObject 的超酷类
如果导入 NuGet 包,您可以在 Exception 类上获得一些不错的扩展方法,并可以访问 CallStack,显示调用链,包括方法参数和所有调用方法的参数值。
它就像一堆异常,只有值显示你如何得到你得到的值。
public void MyMethod()
{
try
{
//get me all methods, signatures, parameters line numbers file names etc used as well as assembly info of all assemblies used for documentation of how the code got here
var stack= new CallStack();
foreach( var frame in StackedFrames)
{
logger.LogDebug(frame.ToString());
}
}
catch(SqlException e)
{
var ex = new ExceptionObject(e);
logger.LogException(e,"{this} exception due to {message} {server} {procedure} TSQL-line:{sqlline}\n{TSQL}"
,e.GetType().Name
,e.Message
,ex.SqlServer
,ex.SqlProcedureName
,ex.SqlLineNumber
,ex.Tsql
,ex.CallStack);
}
catch(Exception e)
{
var ex = new ExceptionObject(e);
logger.LogException(e,"{this} exception due to {message} signature: signature}\nCallStack:", e.GetType().Name,e.Message,ex.Signature,ex.CallStack);
}
}