使用 C# 編譯器解譯而來的屬性判斷呼叫端資訊
使用資訊屬性可取得方法呼叫者的資訊。 您可以取得原始程式碼的檔案路徑、原始程式碼中的行號,以及呼叫端的成員名稱。 若要取得成員呼叫端資訊,請使用套用至選擇性參數的屬性。 每個選擇性參數都會指定預設值。 下表列出 System.Runtime.CompilerServices 命名空間中定義的 Caller Info 屬性:
屬性 | 描述 | 類型 |
---|---|---|
CallerFilePathAttribute | 包含呼叫端的原始程式檔完整路徑。 完整路徑是編譯時的路徑。 | String |
CallerLineNumberAttribute | 原始程式檔中呼叫方法的行號。 | Integer |
CallerMemberNameAttribute | 呼叫端的方法名稱或屬性名稱。 | String |
CallerArgumentExpressionAttribute | 引數運算式的字串表示。 | String |
此資訊可協助您進行追蹤和偵錯,以及協助您建立診斷工具。 下列範例示範如何使用呼叫端資訊屬性。 每次呼叫 TraceMessage
方法時,會將引數的呼叫端資訊插入選擇性參數。
public void DoProcessing()
{
TraceMessage("Something happened.");
}
public void TraceMessage(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Trace.WriteLine("message: " + message);
Trace.WriteLine("member name: " + memberName);
Trace.WriteLine("source file path: " + sourceFilePath);
Trace.WriteLine("source line number: " + sourceLineNumber);
}
// Sample Output:
// message: Something happened.
// member name: DoProcessing
// source file path: c:\Visual Studio Projects\CallerInfoCS\CallerInfoCS\Form1.cs
// source line number: 31
您必須為每個選擇性參數指定明確的預設值。 您無法對未指定成選擇性參數的參數套用呼叫端資訊屬性。 呼叫端資訊屬性不會將參數指定為選擇性。 而是會影響省略引數時傳入的預設值。 呼叫端資訊的值會在編譯時,以常值的形態發出給中繼語言 (IL)。 結果不受模糊化所影響,與 StackTrace 屬性中例外狀況的結果不同。 您可以明確提供選擇性引數來控制呼叫端資訊,或是隱藏呼叫端資訊。
成員名稱
您可以使用 CallerMemberName
屬性避免指定成員名稱做為所呼叫方法的 String
引數。 利用這個技巧就可以避免發生 [重新命名重構] 未變更 String
值這個問題。 這項優點對於下列工作特別有用:
- 使用追蹤和診斷常式。
- 當繫結資料時,實作 INotifyPropertyChanged 介面。 此介面允許物件的屬性通知繫結控制項屬性已變更。 控制項可以顯示更新的資訊。 沒有
CallerMemberName
屬性 (Attribute),您就必須指定屬性 (Property) 名稱做為常值。
下圖顯示當您使用 CallerMemberName
屬性時,傳回的成員名稱。
呼叫會發生在 | 成員名稱結果 |
---|---|
方法、屬性或事件 | 產生呼叫的方法、屬性或事件的名稱。 |
建構函式 | 字串 ".ctor" |
靜態建構函式 | 字串 ".cctor" |
完成項 | 字串 "Finalize" |
使用者定義的運算子或轉換 | 產生的成員名稱,例如 "op_Addition"。 |
屬性建構函式 | 套用屬性 (attribute) 的方法或屬性 (property) 名稱。 如果屬性為成員內的任何項目 (例如參數、傳回值或泛型類型參數),這個結果會是與該項目相關聯的成員名稱。 |
無包含的成員 (例如,組件層級或套用至類型的屬性)。 | 選擇性參數的預設值。 |
引數運算式內
當您想要以引數形式傳遞運算式時,可使用 System.Runtime.CompilerServices.CallerArgumentExpressionAttribute。 診斷程式庫可能會想要為傳遞給引數的 運算式提供更多詳細資料。 除了參數名稱及提供觸發診斷的運算式之外,開發人員還有觸發診斷之條件的詳細資料。 這些額外資訊有利於修正運算式。
下列範例示範如何在引數無效時,提供引數的詳細資訊:
public static void ValidateArgument(string parameterName, bool condition, [CallerArgumentExpression("condition")] string? message=null)
{
if (!condition)
{
throw new ArgumentException($"Argument failed validation: <{message}>", parameterName);
}
}
叫用方法如下列範例所示:
public void Operation(Action func)
{
Utilities.ValidateArgument(nameof(func), func is not null);
func();
}
condition
使用的運算式,由編譯器插入 message
引數中。 當開發人員使用 null
引數呼叫 Operation
時,下列訊息會儲存在 ArgumentException
中:
Argument failed validation: <func is not null>
此屬性可讓您撰寫診斷公用程式來提供更多詳細資料。 開發人員可以更快了解需要變更的事項。 您也可以使用 CallerArgumentExpressionAttribute,決定要使用哪個運算式做為擴充方法的接收端。 下列方法會定期取樣序列。 若序列的元素數低於頻率,會回報錯誤:
public static IEnumerable<T> Sample<T>(this IEnumerable<T> sequence, int frequency,
[CallerArgumentExpression(nameof(sequence))] string? message = null)
{
if (sequence.Count() < frequency)
throw new ArgumentException($"Expression doesn't have enough elements: {message}", nameof(sequence));
int i = 0;
foreach (T item in sequence)
{
if (i++ % frequency == 0)
yield return item;
}
}
上一個範例在 sequence
參數中使用 nameof
運算子。 這是 C# 11 的功能。 在 C# 11 之前,必須以字串格式鍵入參數的名稱。 您可以依照下列方式呼叫此方法:
sample = Enumerable.Range(0, 10).Sample(100);
上述範例會擲回 ArgumentException,其訊息文字如下所示:
Expression doesn't have enough elements: Enumerable.Range(0, 10) (Parameter 'sequence')