به دست آوردن اطلاعات کد اجراکننده یک متد
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: یک دقیقه

در C# 5 به بعد می‌توان به پارامترهای یک متد، پارامترهای دلخواهی را افزود تا به واسطه آن‌ها مشخصات کدی که این متد را فراخوانده، به دست آورد. روش انجام این کار، افزودن صفات زیر به پارامترهای متد مورد نظر است:
  1. [CallerFilePath]:مسیر کد فراخواننده را نگه می‌دارد.
  2. [CallerLineNumber]: شماره خط کد فراخواننده را  نگه می‌دارد.
  3. [CallerMemberName] : نام کد فراخوان را نگه می‌دارد .
این صفات کامپایلر را قادر می‌سازد که اطلاعاتی درباره فراخواننده متد مورد نظر، جمع‌آوری کند 
مثال زیر را در نظر بگیرید:
using System;
using System.Runtime.CompilerServices; 
namespace ConsoleApplication8
{ 
  class Program
    {
        static void Main(string[] args)
        {
            Test();
            Console.Read();
        }
        static void Test(
                [CallerMemberName] string memberName = null,
                [CallerFilePath] string filePath = null,
                [CallerLineNumber] int lineNumber = 0)
        {
            Console.WriteLine(memberName);
            Console.WriteLine(filePath);
            Console.WriteLine(lineNumber);
        }
    }
}
که نتیجه اجرای کد فوق به صورت زیر است:
Main          
c:\Pojects\ConsoleApplication8\Program.cs       
9                
 که عبارت Main عنوان متدی است که محل فراخوانی متد مورد نظر ماست و خط دوم حاوی مسیری است که کد فراخواننده متد مورد نظر ما درآن‌جا ذخیره شده است و عدد 9 نشانگر شماره خط محل فراخوانی متد Test است.
  • #
    ‫۲ سال و ۹ ماه قبل، دوشنبه ۲۴ آبان ۱۴۰۰، ساعت ۱۶:۲۴
    اضافه شدن ویژگی CallerArgumentExpression به C#10.0

    ویژگی [CallerArgumentExpression] امکان دریافت آرگومان ارسالی به یک متد را به صورت رشته‌ای میسر می‌کند. برای مثال:
    public static void LogExpression<T>(
        T value, 
        [CallerArgumentExpression("value")] string expression = null)
    {
        Console.WriteLine($"{expression}: {value}");
    }
    با ورودی زیر:
    var person = new Person("Vahid", "N.");
    LogExpression(person.FirstName);
    چنین خروجی را تولید می‌کند:
    person.FirstName: Vahid

    پارامتری که توسط ویژگی [CallerArgumentExpression] معرفی می‌شود، اختیاری بوده و به صورت خودکار توسط کامپایلر مقدار دهی می‌شود. یعنی کامپایلر فراخوانی فوق را به صورت زیر انجام می‌دهد:
    LogExpression(person.FirstName, "person.FirstName");
    وجود یک چنین قابلیتی، نویسندگان کتابخانه‌ها را از بکارگیری <<Expression<Func<T‌ها یا همان استاتیک ریفلکشن، رهایی می‌بخشد.

    یک نمونه کاربرد دیگر آن در بررسی نال بودن مقدار پارامترهای ارسالی است که می‌توان آن‌ها را به صورت زیر خلاصه کرد:
    public static void EnsureArgumentIsNotNull<T>(
       T value, 
       [CallerArgumentExpression("value")] string expression = null)
    {
        if (value is null)
            throw new ArgumentNullException(expression);
    }
    
    public static void Foo(string name)
    {
        EnsureArgumentIsNotNull(name); // if name is null, throws ArgumentNullException: "Value cannot be null. (Parameter 'name')"
        ...
    }
    پیشتر می‌بایستی با استفاده از nameof، نام پارامتر را مشخص کرد. در اینجا کامپایلر قادر است این مقدار را مشخص کند و دیگر نیازی به استفاده از روش زیر نیست:
        if (name is null)
        {
            throw new ArgumentNullException(nameof(name));
        }
    البته NET 6.0. به همراه متد جدید زیر که از قابلیت فوق استفاده می‌کند، هست:
    ArgumentNullException.ThrowIfNull(name);
    و متد ThrowIfNull آن به صورت زیر تعریف شده‌است:
    public static void ThrowIfNull(
        [NotNull] object? argument,
        [CallerArgumentExpression("argument")] string? paramName = null)

    سؤال: چرا آرگومان اول این متد، هم nullable تعریف شده‌است و هم با ویژگی NotNull مزین گشته‌است؟
    nullable بودن آن از این جهت است که ممکن است مقدار ارسالی به آن نال باشد. ویژگی NotNull آن به کامپایلر اعلام می‌کند که اگر این متد با موفقیت به پایان رسید، در سطرهای پس از آن، مقدار این شیء دیگر نال نیست و نیازی نیست تا به استفاده کنند اعلام کند که باید مراقب نال بودن آن باشد.