ฉันจะรับหมายเลขบรรทัดปัจจุบันได้อย่างไร


118

นี่คือตัวอย่างของสิ่งที่ฉันต้องการทำ:

MessageBox.Show("Error line number " + CurrentLineNumber);

ในโค้ดด้านบนCurrentLineNumberควรเป็นหมายเลขบรรทัดในซอร์สโค้ดของโค้ดส่วนนี้

ฉันจะทำเช่นนั้นได้อย่างไร?


คุณไม่สามารถทำได้อย่างน่าเชื่อถือเนื่องจากคอมไพเลอร์ JIT สามารถทำการปรับให้เหมาะสมได้ (เช่นโค้ดอินไลน์) ซึ่งหมายความว่าหมายเลขบรรทัดของคุณจะผิด
adrianbanks

1
เนื่องจากคุณสามารถปิดการเพิ่มประสิทธิภาพได้หากต้องการคุณจึงทำได้อย่างน่าเชื่อถือ
jwg

คำตอบ:


175

ใน. NET 4.5 / C # 5 คุณสามารถให้คอมไพเลอร์ทำงานนี้ให้คุณได้โดยเขียนวิธียูทิลิตี้ที่ใช้แอตทริบิวต์ผู้โทรใหม่:

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 + ")");
}

สิ่งนี้จะแสดงตัวอย่างเช่น:

Boo ที่บรรทัด 39 (SomeMethodSomewhere)

นอกจากนี้ยัง[CallerFilePath]บอกเส้นทางของไฟล์โค้ดต้นฉบับด้วย


ขอบคุณมากสำหรับคำตอบ เป็นไปได้ไหมที่จะเรียนรู้ชื่อวัตถุ? โอ้ฉันสับสนกับอย่างอื่น สิ่งที่ฉันสงสัยคือเว็บไซต์ asp.net 4.5 ตัวจับข้อผิดพลาดทั่วโลก จับข้อผิดพลาดที่เกิดจากชื่อวัตถุ?
MonsterMMORPG

@MonsterMMORPG ไม่; เพียง 3 ข้อที่ฉันกล่าวถึงข้างต้น
Marc Gravell

1
@MarcGravell ต้องการให้สภาพแวดล้อม Run time ควรเป็น 4.5 ด้วยหรือไม่หรือเป็นคุณลักษณะของคอมไพเลอร์?
kuldeep

5
C # ได้รับการออกแบบมาอย่างดี มันไม่เคยหยุดที่จะทำให้ฉันประหลาดใจ ขอบคุณ Marc!
nmit026

74

ใช้เมธอดStackFrame.GetFileLineNumberตัวอย่างเช่น:

private static void ReportError(string message)
{
     StackFrame callStack = new StackFrame(1, true);
     MessageBox.Show("Error: " + message + ", File: " + callStack.GetFileName() 
          + ", Line: " + callStack.GetFileLineNumber());
}

ดูรายการ Blog ของ Scott Hanselmanสำหรับข้อมูลเพิ่มเติม

[แก้ไข: เพิ่มสิ่งต่อไปนี้]

สำหรับผู้ที่ใช้. Net 4.5 หรือใหม่กว่าให้พิจารณาแอ็ตทริบิวต์CallerFilePath , CallerMethodNameและCallerLineNumberในเนมสเปซ System.Runtime.CompilerServices ตัวอย่างเช่น:

public void TraceMessage(string message,
        [CallerMemberName] string callingMethod = "",
        [CallerFilePath] string callingFilePath = "",
        [CallerLineNumber] int callingFileLineNumber = 0)
{
    // Write out message
}

อาร์กิวเมนต์ต้องเป็นstringสำหรับCallerMemberNameและCallerFilePathและintสำหรับCallerLineNumberและต้องมีค่าเริ่มต้น การระบุแอ็ตทริบิวต์เหล่านี้บนพารามิเตอร์เมธอดจะสั่งให้คอมไพลเลอร์แทรกค่าที่เหมาะสมในโค้ดการเรียกในเวลาคอมไพล์ซึ่งหมายความว่ามันทำงานผ่านการทำให้สับสน ดูข้อมูลผู้โทรสำหรับข้อมูลเพิ่มเติม


@MonsterMMORPG สิ่งนี้ใช้งานได้โดยไม่คำนึงว่าจะมีข้อผิดพลาดหรือไม่ คลาส StackFrame กำลังดูวิธีการเรียกใช้ปัจจุบันที่กำลังดำเนินการ อาร์กิวเมนต์แรกของตัวสร้าง StackFrame คือความลึกของการเรียก (1) และอาร์กิวเมนต์ที่สองระบุว่าข้อมูลไฟล์เป็นสิ่งที่จำเป็น
akton

3
หากคุณกำลังรวบรวมStackFrameตัวอย่างในMonoอย่าลืมใช้--debugในเวลาคอมไพล์และรันไทม์
bernard paulus

StackFrameไม่มีใน. NET Core ใช้คำตอบของ Marc Gravell
Jesse Chisholm

การใช้ค่าเริ่มต้นทำให้เกิด= string.Emptyข้อผิดพลาด"ค่าพารามิเตอร์เริ่มต้นสำหรับ 'CallingFilePath' ต้องเป็นค่าคงที่เวลาคอมไพล์" !
stomy

1
@stomy ฉันเปลี่ยน examp [le เพื่อใช้เครื่องหมายคำพูดคู่ ( "") แทนstring.Empty.
akton

22

ฉันชอบหนึ่งสมุทรดังนั้น:

int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();

8
มันต้องการไฟล์. pdb .. ซึ่งโดยทั่วไปเราจะไม่สร้าง / คัดลอกไปยังเซิร์ฟเวอร์ที่ใช้งานจริง
Deepak Sharma

4

สำหรับผู้ที่ต้องการวิธีแก้ปัญหา. 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 ตามที่คุณต้องการ!


3
จะใช้ไม่ได้ในทุกกรณี .. มันต้องการไฟล์. pdb เสมอซึ่งโดยทั่วไปเราจะไม่สร้าง / คัดลอกไปยังเซิร์ฟเวอร์ที่ใช้งานจริง ลองใช้แอตทริบิวต์ C # 5.0 Caller *
Deepak Sharma

2
หากคุณใช้สิ่งนี้แทน: System.Diagnostics.Debug.WriteLine(String.Format("{0}({1}): {2}: {3}", fileName, lineNumber, methodName, message));คุณสามารถคลิกบรรทัดในหน้าต่างเอาต์พุตและไปที่บรรทัดนั้นในแหล่งที่มา
Jesse Chisholm

3

หากอยู่ในบล็อก 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());
}

1

ใน. 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


สิ่งนี้ไม่ส่งกลับหมายเลขบรรทัดที่ถูกต้อง ฉันต้องลบ 191 เพื่อให้ถูกต้องด้วยเหตุผลบางประการ
Daniel

น่าสนใจ ทำงานได้ดีสำหรับฉันที่นี่ คุณได้เปิดใช้หมายเลขลิงก์ใน IDE หรือไม่? หากคุณเรียกใช้ฟังก์ชันนี้จากที่ต่างๆในไฟล์คุณยังต้องลบ 191 หรือไม่? สิ่งนี้อาจเป็นข้อบกพร่องของคอมไพเลอร์ (ไม่น่าจะเป็นไปได้ แต่เป็นไปได้) หรือบล็อกที่ยุบในหน้าของคุณ (ในขณะที่ไม่ควรป้องกันไม่ให้หมายเลขบรรทัดถูกต้องอาจอธิบายความแตกต่างได้หากคุณกำลังนับแทนที่จะค้นหาหมายเลขบรรทัด) หากคุณสามารถติดต่อฉันแบบออฟไลน์ฉันอยากจะไปที่ด้านล่างของมัน
Brian Cryer

ไม่มีบล็อกที่ยุบหมายเลขบรรทัดเปิดอยู่ แต่ยังคงต้องลบ 191 ไม่ว่าจะเรียกมาจากที่ใด ฉันรู้ว่า ...
Daniel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.