C# 6 - The nameof Operator
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: دو دقیقه

یکی دیگر از قابلیت‌های جذاب نسخه‌ی جدید سی‌شارپ، عملگر nameof است. هدف اصلی آن ارائه کدهایی با قابلیت Refactoring بهتر است؛ زیرا به جای نوشتن نام فیلدها و یا متدها در صورت نیاز به صورت hard-coded، می‌توانیم از این عملگر استفاده کنیم. به عنوان مثال در زمان صدور استثناءیی از نوع ArgumentNullException باید نام آرگومان را به سازنده‌ی این کلاس پاس دهیم. متاسفانه یکی از مشکلاتی که با رشته‌ها در حالت کلی وجود دارد این است که امکان دیباگ در زمان کامپایل را از دست خواهیم داد و با تغییر هر المنت، تغییرات به صورت خودکار به رشته پاس داده شده، به سازنده‌ی کلاس ArgumentNullException اعمال نخواهد شد:
public static void DoWork(string name)
{
       if (name == null)
       {
           throw new ArgumentNullException("name");
       }
}
اما با استفاده از عملگر nameof، کد امن‌تری را خواهیم داشت؛ زیرا همیشه نام واقعی آرگومان به سازنده‌ی کلاس ArgumentNullException پاس داده می‌شود:
public static void DoWork(string name)
{
       if (name == null)
       {
           throw new ArgumentNullException(nameof(name));
       }
}
اگر ReSharper را نصب کرده باشید، به شما پیشنهاد می‌دهد که از nameof به جای یک رشته‌ی جادویی (magic string) استفاده نمائید:


یک مثال دیگر می‌تواند در زمان فراخوانی رخدادهای مربوط به OnPropertyChanged باشد. در اینجا باید نام خصوصیتی را که تغییر یافته است، به آن پاس دهیم: 
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
اما با کمک عملگر nameof می‌توانیم قسمت فراخوانی متد OnPropertyChanged را به اینصورت نیز بازنویسی کنیم:
OnPropertyChanged(nameof(Name));
ممکن است عنوان کنید قبلاً در سی‌شارپ 5 هم می‌توانستیم از ویژگی [CallerMemberName] استفاده کنیم، پس دیگر نیازی به استفاده از عملگر nameof نخواهد بود. اما تفاوت کلیدی این است که CallerMemberName در زمان اجرا نام فیلد فراخوان را دریافت میکند (run time)، در حالیکه با استفاده از عملگر nameof می‌توانید در زمان کامپایل به نام فیلد دسترسی داشته باشید (compile time).

محدودیت‌های عملگر nameof
این عملگر حالت‌هایی را که مشاهده می‌کنید، فعلاً پشتیبانی نخواهد کرد:
nameof(f()); // where f is a method - you could use nameof(f) instead
nameof(c._Age); // where c is a different class and _Age is private. Nameof can't break accessor rules.
nameof(List<>); // List<> isn't valid C# anyway, so this won't work
nameof(default(List<int>)); // default returns an instance, not a member
nameof(int); // int is a keyword, not a member- you could do nameof(Int32)
nameof(x[2]); // returns an instance using an indexer, so not a member
nameof("hello"); // a string isn't a member
nameof(1 + 2); // an int isn't a member
برای آزمایش عملگر nameof می‌توانیم یک تست را در حالت‌های زیر بنویسیم:


همانطور که مشاهده می‌کنید، همه‌ی حالت‌های فوق با موفقیت پاس شده‌اند.
  • #
    ‫۱ سال و ۹ ماه قبل، سه‌شنبه ۲۴ آبان ۱۴۰۱، ساعت ۱۳:۲۵
    یک نکته‌ی تکمیلی: بهبود عملگر nameof در C# 11

    عملگر nameof در C# 11، اندکی بهبود یافته‌است و اینبار می‌تواند در ویژگی‌ها (attributes) نیز به نام پارامترهای متدها، دسترسی پیدا کند. چند مثال:
    الف) امکان دسترسی به نام پارامتر متد، در حالت اعمال به متد
    [MyAttr(nameof(parameter))]
    void Method(string parameter)
    {
    }

    ب) امکان دسترسی به نام نوع پارامتر جنریک متد، در حالت اعمال به متد
    [MyAttr(nameof(T))]
    void Method<T>()
    {
    }

    ج) امکان دسترسی به نام پارامتر متد، در حالت اعمال به پارامتر
    void Method([MyAttr(nameof(parameter))] int parameter)
    {
    }

    یکی از کاربردهای آن، بهبود تعاریف متادیتای nullable reference types و عدم نیاز به کار با رشته‌ها به صورت مستقیم است:
    [return: NotNullIfNotNull(nameof(path))]
    public static string? GetUrl(string? path)
            => !string.IsNullOrEmpty(path) ? $"https://localhost/api/{path}" : null;
  • #
    ‫۹ ماه قبل، سه‌شنبه ۷ آذر ۱۴۰۲، ساعت ۱۵:۰۴
    یک نکته‌ی تکمیلی: تکامل اپراتور nameof در C# 12.0

    همانطور که در این مطلب مشاهده کردید، اپراتور nameof، روشی بسیار مفید جهت دسترسی به نام متغیرها، نوع‌ها و یا اعضای یک کلاس است. در C# 12، این ویژگی اندکی بهبود یافته‌است و امکان دسترسی به اطلاعات اعضای یک کلاس را هم دارد:
    public class NameofClass
    {
        public string SomeProperty { get; set; }
    
        // Now legal with C# 12
        // would show "Length" on the console
        public const string NameOfSomePropertyLength = nameof(SomeProperty.Length); 
        
        public static int StaticField;
        public const string NameOfStaticFieldMinValue  = nameof(StaticField.MinValue);
    
        [Description($"String {nameof(SomeProperty.Length)}")]
        public int StringLength(string s)
        {
            return s.Length;
        }
    }
    در این مثال، اگر سعی کنیم مقدار NameOfSomePropertyLength را در کنسول نمایش دهیم، عبارت Length ظاهر خواهد شد. تا پیش از C# 12 برای دسترسی به یک چنین قابلیتی نیاز به نمونه سازی و تولید شیءای از کلاس NameofClass فوق وجود داشت تا بتوان اپراتور nameof را به خواص آن اعمال کرد. این محدودیت در C# 12 برطرف شده‌است.

    همچنین همانطور که مشاهده می‌کنید، امکان دسترسی به اطلاعات فیلدهای استاتیک و یا بکارگیری این قابلیت در Attributes هم میسر شده‌است.