نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
- در نگارش‌های جدید ASP.NET Core، بسته‌ی بومی سازی آن، خطاهای یافت نشدن کلیدها را توسط ILogger آن لاگ می‌کند (و هیچ استثنایی را مشاهده نخواهید کرد). به همین جهت لاگ کردن را در برنامه‌ی خود فعال کنید و خروجی آن‌را جهت یافتن عباراتی مانند search for بررسی کنید (دقیقا عنوان می‌کند که کجاها را برای یافتن معادل‌ها بررسی کرده‌است و چیزی یافت نشده).
- برای مثال اگر از قالب پیش‌فرض ASP.NET Core 2.1 استفاده می‌کنید، لاگ کردن در کنسول و همچنین دیباگ آن فعال است (بدون نیاز به تنظیم اضافه‌تری). در اینجا برنامه را در حالت dotnet watch run اجرا کنید (این دستور را در ریشه‌ی پروژه اجرا کنید) و سپس در کنسول جاری، عبارات لاگ شده را بررسی کنید. یا پنجره‌ی debug در ویژوال استودیو نیز همینکار را انجام می‌دهد.
بررسی لاگ‌های برنامه جزو الزامات کار با برنامه‌های NET Core. است. در اینجا کمتر استثناءها را مشاهده می‌کنید و چون یک فریم ورک توکار Logging را به همراه دارد، همه‌جا از آن برای گزارش مشکلات، در پشت صحنه استفاده می‌شود.
مطالب
یکی کردن اسمبلی‌های یک پروژه‌ی WPF
فرض کنید پروژه‌ی WPF شما از چندین پروژه‌ی ‍Class library و اسمبلی‌های جانبی دیگر، تشکیل شده‌است. اکنون نیاز است جهت سهولت توزیع آن، تمام این فایل‌ها را با هم یکی کرده و تبدیل به یک فایل EXE نهایی کنیم. مایکروسافت ابزاری را به نام ILMerge، برای یک چنین کارهایی تدارک دیده‌است؛ اما این برنامه با WPF سازگار نیست. در ادامه قصد داریم اسمبلی‌های جانبی را تبدیل به منابع مدفون شده در فایل EXE برنامه کرده و سپس آن‌ها را در اولین بار اجرای برنامه، به صورت خودکار بارگذاری و در برنامه مورد استفاده قرار دهیم.

یک مثال جهت بازتولید کدهای این مطلب
الف) یک پروژه‌ی WPF جدید را به نام MergeAssembliesIntoWPF ایجاد کنید.
ب) یک پروژه‌ی Class library جدید را به نام MergeAssembliesIntoWPF.ViewModels به این Solution اضافه کنید. از آن برای تعریف ViewModelهای برنامه استفاده خواهیم کرد.
برای نمونه کلاس ذیل را به آن اضافه کنید:
namespace MergeAssembliesIntoWPF.ViewModels
{
    public class ViewModel1
    {
        public string Data { set; get; }

        public ViewModel1()
        {
            Data = "Test";
        }
    }
}
ج) یک پروژه‌ی WPF User control library را نیز به نام MergeAssembliesIntoWPF.Shell به این Solution اضافه کنید. از آن برای تعریف Viewهای برنامه کمک خواهیم گرفت.
به این پروژه ارجاعی را به اسمبلی قسمت (ب) اضافه نموده و برای نمونه User control ذیل را به نام View1.xaml به آن اضافه نمائید:
<UserControl x:Class="MergeAssembliesIntoWPF.Shell.View1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:VM="clr-namespace:MergeAssembliesIntoWPF.ViewModels;assembly=MergeAssembliesIntoWPF.ViewModels"
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <VM:ViewModel1 x:Key="ViewModel1" />
    </UserControl.Resources>
    <Grid DataContext="{Binding Source={StaticResource ViewModel1}}">
        <TextBlock Text="{Binding Data}" />
    </Grid>
</UserControl>
در پروژه اصلی Solution (قسمت الف)، ارجاعاتی را به دو اسمبلی قسمت‌های ب و ج اضافه کنید. سپس MainWindow.xaml آن‌را به نحو ذیل تغییر داده و برنامه را اجرا کنید:
<Window x:Class="MergeAssembliesIntoWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:V="clr-namespace:MergeAssembliesIntoWPF.Shell;assembly=MergeAssembliesIntoWPF.Shell"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <V:View1 x:Key="View1" />
    </Window.Resources>
    <Grid>
        <V:View1 />
    </Grid>
</Window>
تا اینجا باید متن Test در پنجره اصلی برنامه ظاهر شود.


ب) مدفون کردن خودکار اسمبلی‌های جانبی برنامه در فایل EXE آن
فایل csproj پروژه اصلی را خارج از VS.NET باز کنید. در انتهای آن سطر ذیل قابل مشاهده است:
 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
پس از این سطر، چند سطر ذیل را اضافه کنید:
  <Target Name="AfterResolveReferences">
    <ItemGroup>
      <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
        <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
      </EmbeddedResource>
    </ItemGroup>
  </Target>
این task جدید MSBuild سبب خواهد شد تا با هر بار Build برنامه، اسمبلی‌هایی که در ارجاعات برنامه دارای خاصیت Copy local مساوی true هستند، به صورت خودکار به صورت یک resource جدید در فایل exe برنامه مدفون شوند. عموما ارجاعاتی که دستی اضافه می‌شوند، مانند دو اسمبلی یاد شده در ابتدای بحث، دارای خاصیت Copy local=true نیز هستند.
پس از این تغییر نیاز است یکبار پروژه را بسته و مجددا باز کنید. اکنون پروژه را build کنید و جهت اطمینان بیشتر آن‌را برای مثال توسط ILSpy مورد بررسی قرار دهید:


همانطور که مشاهده می‌کنید، دو اسمبلی مورد استفاده در برنامه به صورت خودکار در قسمت منابع فایل EXE مدفون شده‌اند.
اگر به مسیر LogicalName تنظیمات فوق دقت کنید، DestinationSubDirectory نیز ذکر شده‌است. علت این است که بسیاری از اسمبلی‌های بومی سازی شده WPF با نام‌هایی یکسان اما در پوشه‌هایی مانند fa، fr و امثال آن ذخیره می‌شوند. به همین جهت نیاز است بین این‌ها تمایز قائل شد.


ج) بارگذاری خودکار اسمبلی‌ها در AppDomain برنامه

تا اینجا اسمبلی‌های جانبی را در فایل EXE مدفون کرده‌ایم. اکنون نوبت به بارگذاری آن‌ها در AppDomain برنامه است. برای اینکار نیاز است تا روال رخدادگردان AppDomain.CurrentDomain.AssemblyResolve را تحت نظر قرار داده و اسمبلی‌هایی را که برنامه درخواست می‌کند، در همینجا از منابع خوانده و به AppDomain اضافه کرد.
انجام اینکار در برنامه‌های WinForms ساده‌است. فقط کافی است به متد Program.Main برنامه مراجعه کرده و تعریف یاد شده را به ابتدای متد Main اضافه کرد. اما در WPF هرچند فایل App.xaml.cs به نظر نقطه‌ی آغازین برنامه است، اما در واقع اینطور نیست. برای نمونه، پوشه‌ی obj\Debug برنامه را گشوده و فایل App.g.i.cs آن‌را بررسی کنید. در اینجا می‌توانید همان رویه شبیه به برنامه‌های WinForm را در متد Program.Main آن، مشاهده کنید. بنابراین نیاز است کنترل این مساله را راسا در دست بگیریم:
using System;
using System.Globalization;
using System.Reflection;

namespace MergeAssembliesIntoWPF
{
    public class Program
    {
        [STAThreadAttribute]
        public static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
            App.Main();
        }

        private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
        {
            var executingAssembly = Assembly.GetExecutingAssembly();
            var assemblyName = new AssemblyName(args.Name);

            var path = assemblyName.Name + ".dll";
            if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
            {
                path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
            }

            using (var stream = executingAssembly.GetManifestResourceStream(path))
            {
                if (stream == null)
                    return null;

                var assemblyRawBytes = new byte[stream.Length];
                stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
                return Assembly.Load(assemblyRawBytes);
            }
        }
    }
}
کلاس Program را با تعاریف فوق به پروژه خود اضافه نمائید. در اینجا Program.Main مورد نیاز خود را تدارک دیده‌ایم. کار آن مدیریت روال رخدادگردان AppDomain.CurrentDomain.AssemblyResolve برنامه پیش از شروع به هر کاری است. در روال رخداد گردان OnResolveAssembly، برنامه اعلام می‌کند که به چه اسمبلی خاصی نیاز دارد. ما آن‌را از قسمت منابع خوانده و سپس توسط متد Assembly.Load آن‌را در AppDomain برنامه بارگذاری می‌کنیم.
پس از اینکه کلاس فوق را اضافه کردید، نیاز است کلاس Program اضافه شده را به عنوان Startup object برنامه نیز معرفی کنید:

انجام اینکار ضروری است؛ در غیراینصورت با متد Main موجود در فایل App.g.i.cs تداخل می‌کند.
اکنون برای آزمایش برنامه، یکبار آن‌را Build کرده و بجز فایل Exe، مابقی فایل‌های موجود در پوشه‌ی bin را حذف کنید. سپس برنامه را خارج از VS.NET اجرا کنید. کار می‌کند!
MergeAssembliesIntoWPF.zip
 
نظرات مطالب
سیلورلایت 5 و تاریخ شمسی
پایه و اساس طراحی سیلورلایت، WPF و WinRT در ویندوز 8 یکی است.
بنابراین از یادگیری سیلورلایت هیچ ضرری نخواهید کرد؛ حتی اگر مستقیما از آن استفاده نکنید. برای مثال کسی که با سیلورلایت آشنا هست راحت می‌تونه به WinRT ویندوز 8 کوچ کنه چون در ویندوز 8 برنامه نویسی برای WinRT «با» دات نت فقط به استفاده از XAML و یکی از زبان‌های دات نتی خلاصه میشه. البته امکان استفاده از HTML خاص WinRT هم هست (که انتقال پذیر نیست و مخصوص کار با زیرساخت‌های WinRT یک سری اضافاتی رو داره) ولی کار با دات نت در این ویندوز برای تولید برنامه‌های مترو، پایه و اساسش همین سیلورلایت و WPF است.
من یک دوره WinRT رو زیرنویس دار کردم (^ ). نکته مهمش این است که در سراسر دوره عمده بحث بررسی تفاوت‌های WinRT با سیلورلایت و WPF است. یعنی اساس یکی است و چیزی دور ریخته نشده. فقط یک سری موارد کم شده چند کنترل زیاد شده، یک سری مباحث برنامه نویسی غیرهمزمان به آن اضافه شده و از این مباحث.
مطالب
امکان یافتن پیش از موعد مشکلات قالب‌های Angular در نگارش 5 آن
مشکلات کامپوننت‌های Angular را چون با زبان TypeScript تهیه می‌شوند، می‌توان بلافاصله در ادیتور مورد استفاده و یا در حین کامپایل برنامه مشاهده کرد؛ اما یک چنین بررسی در مورد قالب‌های HTML ایی آن در زمان کامپایل انجام نمی‌شود و اگر مشکلی وجود داشته باشد، این مشکلات را صرفا در زمان اجرای برنامه در مرورگر می‌توان مشاهده کرد. برای رفع این مشکل و بهبود این وضعیت، در نگارش 5.2.0 فریم ورک Angular (و همچنین Angular CLI 1.7 به بعد)، پرچم جدیدی به تنظیمات کامپایلر آن اضافه شده‌است که با فعالسازی آن، مشکلات binding احتمالی در قالب‌های کامپوننت‌ها را می‌توان یافت. زمانیکه توسط Angular CLI یک برنامه‌ی Angular را در حالت AoT کامپایل می‌کنیم، کامپایلر مراحلی را طی می‌کند که توسط آن کدهای یک قالب کامپوننت، تبدیل به دستور العمل‌هایی قابل اجرای در مرورگر می‌شوند. در طی یکی از این مراحل، کامپایلر قالب‌های Angular، از کامپایلر TypeScript برای اعتبارسنجی عبارت‌های binding استفاده می‌کند. اکنون می‌توان خروجی این مرحله را نیز در حین کار با Angular CLI، مشاهده و مشکلات گزارش شده‌ی توسط آن‌را برطرف کرد.


فعالسازی بررسی مشکلات قالب‌های کامپوننت‌ها

برای فعالسازی بررسی مشکلات قالب‌های کامپوننت‌ها، نیاز است به فایل تنظیمات کامپایلر TypeScript و یا همان tsconfig.json مراجعه کرد و سپس قسمت جدیدی را به آن به نام angularCompilerOptions، افزود:
{
  "compilerOptions": {
    "experimentalDecorators": true,
    ...
   },
   "angularCompilerOptions": {
     "fullTemplateTypeCheck": true,
     "preserveWhiteSpace": false,
     ...
   }
 }
- در اینجا با معرفی خاصیت fullTemplateTypeCheck و تنظیم آن به true، مشکلات موجود در قالب‌ها را در زمان کامپایل برنامه می‌توانید مشاهده کنید.
- البته این خاصیت در حین استفاده‌ی از یکی از دستورات ng serve --aot  و یا  ng build --prod انتخاب می‌شود.
- مقدار این پرچم در نگارش‌های 5x به صورت پیش‌فرض به false تنظیم شده‌است؛ اما در نگارش 6 آن به true تنظیم خواهد شد. بنابراین بهتر است از هم اکنون کار با آن‌را شروع کنید.


یک مثال: بررسی خاصیت fullTemplateTypeCheck

فرض کنید اینترفیس یک مدل را به صورت زیر تعریف کرده‌اید که فقط دارای خاصیت name است:
export interface PonyModel {
   name: string;
}
سپس یک خاصیت عمومی را بر همین مبنا در کامپوننتی، تعریف و مقدار دهی اولیه کرده‌اید:
import { PonyModel } from "./pony";

@Component({
  selector: "app-detect-common-errors-test",
  templateUrl: "./detect-common-errors-test.component.html",
  styleUrls: ["./detect-common-errors-test.component.css"]
})
export class DetectCommonErrorsTestComponent implements OnInit {

  ponyModel: PonyModel = { name: "Pony1" };
اکنون در قالب این کامپوننت، به شکل زیر از این وهله استفاده شده‌است:
 <p>Hello {{ponyModel.age}}

در این حالت اگر fullTemplateTypeCheck فعال شده باشد و دستور ng build --prod را صادر کنیم، به خروجی ذیل خواهیم رسید:
 \detect-common-errors-test.component.html(5,4): : Property 'age' does not exist on type 'PonyModel'.
همانطور که ملاحظه می‌کنید اینبار خطاهای کامپایل فایل html نیز در خروجی کامپایلر ظاهر شده‌است و عنوان می‌کند خاصیت age در اینترفیس PonyModel وجود خارجی ندارد.

برای اینکه بتوانید به حداکثر کارآیی این قابلیت برسید، بهتر است گزینه‌ی strict را در تنظیمات کامپایلر TypeScript روشن کنید و خودتان را به کار با نوع‌های نال نپذیر عادت دهید. به این ترتیب می‌توانید تعداد خطاهای احتمالی بیشتری را پیش از موعد و پیش از وقوع آن‌ها در زمان اجرا، در زمان کامپایل، پیدا و رفع کنید.


یک نکته‌ی تکمیلی
افزونه‌ی Angular Language service نیز یک چنین قابلیتی را به همراه دارد (و حتی در نگارش‌های پیش از 5 نیز قابل استفاده است).
اشتراک‌ها
کتاب C# 11 and .NET 7 – Modern Cross-Platform Development, 7th Edition

کتاب C# 11 and .NET 7 – Modern Cross-Platform Development, 7th Edition (سی شارپ 11 و دات نت 7، مبانی توسعه چند سکویی مدرن، ویرایش هفتم)، راهنمایی قابل دسترس برای برنامه نویسان مبتدی تا متوسط برای مفاهیم، کاربرد‌های دنیای واقعی و جدید‌ترین ویژگی‌های C# 11 و NET 7. به همراه تمرینات عملی با استفاده از Visual Studio 2022 و Visual Studio Code است. جدید‌ترین نسخه این کتاب به طور گسترده ای بازنگری شده است تا تمامی ویژگی‌های جدید ارائه شده با سی شارپ 11 و دات نت 7 را در خود جای دهد. 

کتاب C# 11 and .NET 7 – Modern Cross-Platform Development, 7th Edition
نظرات مطالب
تغییر عملکرد و یا ردیابی توابع ویندوز با استفاده از Hookهای دات نتی
- نگارش 1.1 آن‌را از اینجا دریافت کنید.
- لطفا برای گزارش خطا از قسمت مخصوص بازخوردهای پروژه آن در سایت استفاده کنید.
همچنین در این حالت فایل ErrosLog.Log آن‌را هم فراموش نکنید (ریز خطاها در آن ثبت می‌شوند).

اگر پروژه را خودتان کامپایل کرده‌اید (که اینطور به نظر می‌رسد با توجه به پوشه debug)، برنامه اجرا نمی‌شود چون تمام فایل‌های exe و dll همراه easy hook را برای اجرا نیاز دارد و این‌ها باید کنار فایل اجرایی اصلی برنامه همانند بسته‌ای که برای دریافت در سایت قرار گرفته، کپی شوند.
این نکته در متن هم ذکر شده؛ قسمت «چند نکته تکمیلی مهم برای کار با کتابخانه Easy hook» انتهای بحث: «برای توزیع هوک‌های خود باید تمام فایل‌های همراه کتابخانه easy hook را نیز توزیع کنید و فقط به چند DLL ابتدایی آن بسنده نباید کرد»
اشتراک‌ها
آیا می‌توان از قابلیت‌های C#6 در نگارش‌های پایین‌تر دات نت فریم ورک استفاده کرد؟
 پاسخ: بله! فقط async در نگارش‌های قبل از دات نت 4.5 وجود ندارند و متدهای الحاقی از دات نت 3 به بعد اضافه شدند و نیاز به افزودن ارجاعی به System.Runtime.CompilerServices.ExtensionAttribute دارند (برای دات نت 2 البته).
بنابراین اگر از VS 2015 استفاده می‌کنید برای مثال به سادگی می‌توانید از قابلیت‌های C# 6 در برنامه‌های دات نت 4 استفاده کنید. برای نمونه یک چنین کدی در VS 2015 با دات نت 4 هم قابل کامپایل است و بدون مشکل کار می‌کند:
using static System.Console;

namespace VS2015_Net4_Tests
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = "Test";
            WriteLine($"{test}");
        }
    }
}
آیا می‌توان از قابلیت‌های C#6 در نگارش‌های پایین‌تر دات نت فریم ورک استفاده کرد؟
اشتراک‌ها
اندازه گیری دما، مختصات جغرافیایی، لرزه یا تکانه و تنظیم نمودن هشدار دهنده توسط NET Micro Framework
اگر شما یک دات نت developer هستید، می‌توانید بدون داشتن دانش زبانهای سطح پایین مانند Assembly روی دستگاههای سازگار با این فریم ورک کد بنویسید و شرایط محیطی پیرامونتان را مانیتور نمایید. این پلت فرم رایگان و با سورس باز است و فرصت طلایی را در اختیار توسعه دهندگان جهت برنامه نویسی روی میکرو پروسسورها "با حداقل تنظیمات پیچیده" قرار می‌دهد. 
شما می‌توانید با syntax (دستورات) دات نتی روی بوردهایی با قیمت زیر 100$ براحتی در محیط دات نت برنامه بنویسید و به همان راحتی باگ گیری نمایید. بعنوان مثال برنامه GPS را روی بورد بنویسید و آن را به شی نصب کرده و از مختصات جغرافیایی در هر لحظه آگاه شوید. و یا دمای پیرامونتان را هر از X ثانیه توسط Thread اندازهگیری نمایید و چنانچه بالاتر و یا پایین‌تر از مقدار مورد انتظارتان بود هشدار دهنده را فعال نمایید.

Measuring temperature, GPS, vibration and set alarms by NET Micro Framework

اندازه گیری دما، مختصات جغرافیایی، لرزه یا تکانه و تنظیم نمودن هشدار دهنده توسط NET Micro Framework
مطالب دوره‌ها
تولید پویای کد در زمان اجرا توسط Reflection.Emit
در ادامه قصد داریم توسط امکانات Reflection به همراه کدهای IL، اشیایی را در زمان اجرا ایجاد کنیم.


Reflection چیست؟

Reflection چیزهایی هستند که با نگاه در یک آینه قابل مشاهده‌اند. در این حالت شخص می‌تواند قسمت‌های مختلف ظاهر خود را برانداز کرده یا قسمتی را تغییر دهید. اما این مساله چه ربطی به دنیای دات نت دارد؟ در دات نت با استفاده از Reflection می‌توان به اطلاعات اشیاء یک برنامه‌ی در حال اجرا دسترسی یافت. برای مثال نام کلاس‌های مختلف آن چیست یا درون کلاسی خاص، چه متدهایی قرار دارند. همچنین با استفاده از Reflection می‌توان رفتارهای جدیدی را نیز به کلاس‌ها و اشیاء افزود یا آن‌ها را تغییر داد.
همواره عنوان می‌شود که از Reflection به دلیل سربار بالای آن پرهیز کنید و تنها از آن به عنوان آخرین راه حل موجود استفاده نمائید و این دقیقا موردی است که در مباحث جاری بیشتر از آن استفاده خواهد شد: ساخت اشیاء جدید در زمان اجرا به کمک کدهای IL و امکانات Reflection


نگاهی به امکانات متداول Reflection

در مثال بعد، نگاهی خواهیم داشت به امکانات متداول Reflection، مانند دسترسی به متدها و خواص یک کلاس و تعویض مقدار یا فراخوانی آن‌ها:
using System;

namespace FastReflectionTests
{
    class Person
    {
        public string Name { set; get; }

        public string Speak()
        {
            return string.Format("Hello, my name is {0}.", this.Name);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //روش متداول
            var vahid = new Person { Name = "Vahid" };
            Console.WriteLine(vahid.Speak());

            var type = vahid.GetType();

            //نمایش متدهای یک کلاس
            var methods = type.GetMethods();
            foreach (var method in methods)
            {
                Console.WriteLine(method.Name);
            }

            //تغییر مقدار یک خاصیت
            var setNameMethod = type.GetMethod("set_Name");
            setNameMethod.Invoke(obj: vahid, parameters: new[] { "Ali" });

            //فراخوانی یک متد
            var speakMethod = type.GetMethod("Speak");
            var result = speakMethod.Invoke(obj: vahid, parameters: null);
            Console.WriteLine(result);
        }
    }
}
با خروجی ذیل
 Hello, my name is Vahid.
set_Name
get_Name
Speak
ToString
Equals
GetHashCode
GetType
Hello, my name is Ali.
توضیحات:
در اینجا یک کلاس شخص با خاصیت نام او تعریف شده است؛ به همراه متدی که رشته‌ای را نمایش خواهد داد.
در متد Main برنامه، ابتدا یک وهله جدید از این شخص ایجاد شده و سپس به روش متداول، متد Speak آن فراخوانی گردیده است. در ادامه کار از امکانات Reflection برای انجام همین امور کمک گرفته شده است.
کار با دریافت نوع یک وهله شروع می‌شود. برای نمونه در اینجا توسط vahid.GetType به نوع وهله ساخته شده دسترسی یافته‌ایم. سپس با داشتن این type، می‌توان به کلیه امکانات Reflection دسترسی یافت. برای مثال توسط GetMethods، لیست کلیه متدهای موجود در کلاس شخص بازگشت داده می‌شود.
اگر به خروجی فوق دقت کنید، پس از سطر اول، 7 سطر بعدی نمایانگر متدهای موجود در کلاس شخص هستند. شاید عنوان کنید که این کلاس به نظر یک متد بیشتر ندارد. اما در دات نت اشیاء از شیء Object مشتق می‌شوند و چهار متد ToString، Equals، GetHashCode و GetType متعلق به آن هستند. همچنین خواص تعریف شده نیز در اصل به دو متد set و get به صورت خودکار در کدهای IL برنامه ترجمه خواهند شد. از همین متد set_Name در ادامه برای مقدار دهی خاصیت نام وهله ایجاد شده استفاده شده است.
همانطور که ملاحظه می‌کنید برای فراخوانی یک وهله از طریق Reflection، ابتدا توسط متد type.GetMethod می‌توان به آن دسترسی یافت و سپس با فراخوانی متد Invoke، می‌توان متد مدنظر را بر روی یک شیء مهیا با پارامترهایی که ذکر می‌کنیم، فراخوانی کرد. اگر این متد پارامتری ندارد، آن‌را نال قرار خواهیم داد.

تا اینجا مقدمه‌ای را ملاحظه نمودید که بیشتر جهت تکمیل بحث، حفظ روابط منطقی قسمت‌های مختلف آن و یادآوری مباحث مرتبط با Reflection ذکر شدند.


ایجاد اشیاء در زمان اجرای برنامه

یکی از کلاس‌های مهم Reflection که در منابع مختلف کمتر به آن پرداخته شده است، کلاس DynamicMethod آن است که از آن می‌توان برای ایجاد اشیاء و یا متدهایی پویا در زمان اجرا استفاده کرد. این کلاس قرار گرفته در فضای نام System.Reflection.Emit، دارای یک ILGenerator است که می‌توان به آن OpCodeهایی را اضافه کرد. زمانیکه کار ایجاد این متدپویا به پایان رسید، با استفاده از Delegates امکان دسترسی و اجرای این متد پویا وجود خواهد داشت.
یک مثال کامل را در این زمینه در ادامه ملاحظه می‌نمائید:
using System;
using System.Reflection.Emit;

namespace FastReflectionTests
{
    class Program
    {
        static double Divider(int a, int b)
        {
            return a / b;
        }

        delegate double DividerDelegate(int a, int b);
        static void Main(string[] args)
        {
            //روش متداول
            Console.WriteLine(Divider(10, 2));

            //تعریف امضای متد
            var myMethod = new DynamicMethod(
                                        name: "DividerMethod",
                                        returnType: typeof(double),
                                        parameterTypes: new[] { typeof(int), typeof(int) },
                                        m: typeof(Program).Module);
            //تعریف بدنه متد
            var il = myMethod.GetILGenerator();
            il.Emit(opcode: OpCodes.Ldarg_0); //بارگذاری پارامتر اول بر روی پشته ارزیابی
            il.Emit(opcode: OpCodes.Ldarg_1); //بارگذاری پارامتر دوم بر روی پشته ارزیابی
            il.Emit(opcode: OpCodes.Div); // دو پارامتر از پشته ارزیابی دریافت و تقسیم خواهند شد
            il.Emit(opcode: OpCodes.Ret); // دریافت نتیجه نهایی از پشته ارزیابی و بازگشت آن

            //فراخوانی متد پویا
            //روش اول
            var result = myMethod.Invoke(obj: null, parameters: new object[] { 10, 2 });
            Console.WriteLine(result);

            //روش دوم
            var method = (DividerDelegate)myMethod.CreateDelegate(delegateType: typeof(DividerDelegate));
            Console.WriteLine(method(10, 2));
        }
    }
}
توضیحات
در ابتدای این مثال جدید یک متد متداول تقسیم کننده دو عدد را ملاحظه می‌کنید. در ادامه قصد داریم overload دیگری از این متد را توسط کدهای MSIL در زمان اجرا ایجاد کنیم که دو پارامتر int را قبول می‌کند.
کار با وهله سازی کلاس DynamicMethod موجود در فضای نام System.Reflection.Emit شروع می‌شود. در اینجا کار تعریف امضای متد جدید باید صورت گیرد. برای مثال نام آن چیست، نوع خروجی آن کدام است. نوع پارامترهای آن چیست و نهایتا این متدی که قرار است به صورت پویا به برنامه اضافه شود، باید در کجا قرار گیرد. برای اینکار از Module خود کلاس Program برنامه استفاده شده است.
پس از تعریف امضای متد پویا، نوبت به تعریف بدنه‌ی آن می‌رسد. کار با دریافت یک ILGenerator که می‌توان در آن کدهای IL را وارد کرد شروع می‌شود. مابقی آن تعریف کدهای IL توسط متد Emit است و پیشتر با مقدمات اسمبلی دات نت در قسمت‌های قبلی مبحث جاری آشنا شده‌ایم. ابتدا دو Ldarg فراخوانی شده‌اند تا دو پارامتر ورودی متد را دریافت کنند. سپس Div بر روی آن‌ها صورت گرفته و نهایتا نتیجه بازگشت داده شده است.
خوب؛ تا اینجا موفق شدیم اولین متد پویای خود را ایجاد نمائیم. برای اجرا آن حداقل دو روش وجود دارد:
الف) فراخوانی متد Invoke بر روی آن. با توجه به اینکه قرار نیست این متد بر روی وهله‌ی خاصی اجرا شود، اولین پارامتر آن null وارد شده است و سپس پارامترهای این متد پویا توسط آرگومان دوم متد Invoke وارد شده‌اند.
ب) می‌توان این عملیات را اندکی شکیل‌تر کرد. برای اینکار پیش از متد Main برنامه یک delegate به نام DividerDelegate تعریف شده است. سپس با استفاده از متد CreateDelegate، خروجی این متد پویا را تبدیل به یک delegate کرده‌ایم. اینبار فراخوانی متد پویا بسیار شبیه به متدهای معمولی می‌شود.
مطالب
C# 6 - Null-conditional operators
برنامه نویس‌‌های سی‌شارپ پیشتر با null-coalescing operator یا ?? آشنا شده بودند. برای مثال
 string data = null;
var result = data ?? "value";
در این حالت اگر data یا سمت چپ عملگر، نال باشد، مقدار value (سمت راست عملگر) بازگشت داده خواهد شد؛ که در حقیقت خلاصه شده‌ی چند سطر ذیل است:
if (data == null)
{
    data = "value";
}
var result = data;
در سی شارپ 6، جهت تکمیل عملگرهای کار با مقادیر نال و بالا بردن productivity برنامه نویس‌ها، عملگر دیگری به نام Null-conditional operator و یا .? به این مجموعه اضافه شده‌است. در این حالت ابتدا مقدار سمت چپ عملگر بررسی خواهد شد. اگر مقدار آن مساوی نال بود، در همینجا کار خاتمه یافته و نال بازگشت داده می‌شود. در غیر اینصورت کار بررسی زنجیره‌ی جاری ادامه خواهد یافت.
برای مثال بسیاری از نتایج بازگشتی از متدها، چند سطحی هستند:
class Response
{
    public string Result { set; get; }
    public int Code { set; get; }
}

 
class WebRequest
{
    public Response GetDataFromWeb(string url)
    {
        // ...
        return new Response { Result = null };
    }
}
در اینجا روش مرسوم کار با کلاس درخواست اطلاعات از وب به صورت ذیل است:
 var webData = new WebRequest().GetDataFromWeb("https://www.dntips.ir/");
if (webData != null && webData.Result != null)
{
    Console.WriteLine(webData.Result);
}
چون می‌خواهیم به خاصیت Result دسترسی پیدا کنیم، نیاز است دو مرحله وضعیت خروجی متد و همچنین خاصیت Result آن‌را جهت مشخص سازی نال نبودن آن‌ها، بررسی کنیم و اگر برای مثال خاصیت Result نیز خود متشکل از یک کلاس دیگر بود که در آن برای مثال StatusCode نیز ذکر شده بود، این بررسی به سه سطح یا بیشتر نیز ادامه پیدا می‌کرد.
در این حالت اگر اشاره‌گر را به محل && انتقال دهیم، افزونه‌ی ReSharper پیشنهاد یکی کردن این بررسی‌ها را ارائه می‌دهد:


به این ترتیب تمام چند سطح بررسی نال، به یک عبارت بررسی .? دار، خلاصه خواهد شد:
 if (webData?.Result != null)
{
    Console.WriteLine(webData.Result);
}
در اینجا ابتدا بررسی می‌شود که آیا webData نال است یا خیر؟ اگر نال بود همینجا کار خاتمه پیدا می‌کند و به بررسی Result نمی‌رسد. اگر نال نبود، ادامه‌ی زنجیره تا به انتها بررسی می‌شود.
البته باید دقت داشت که برای تمام سطوح باید از .? استفاده کرد (برای مثال response?.Results?.Status)؛ در غیر اینصورت همانند سابق در صورت استفاده‌ی از دات معمولی، به یک null reference exception می‌رسیم.


کار با متدها و Delegates

این عملگر جدید مقایسه‌ی با نال را بر روی متدها (علاوه بر خواص و فیلدها) نیز می‌توان بکار برد. برای مثال خلاصه شده‌ی فراخوانی ذیل:
 if (x != null)
{
   x.Dispose();
}
با استفاده از Null Conditional Operator به این صورت است:
 x?.Dispose();

و یا بکار گیری آن بر روی delegates (روش قدیمی):
 var copy = OnMyEvent;
if (copy != null)
{
   copy(this, new EventArgs());
}
نیز با استفاده از متد Invoke به نحو ذیل قابل انجام است و نکته جالب یک سطر کد ذیل علاوه بر ساده شدن آن:
 OnMyEvent?.Invoke(this, new EventArgs());
Thread-safe بودن آن نیز می‌باشد. زیرا در این حالت کامپایلر delegate را به یک متغیر موقتی کپی کرده و سپس فراخوانی‌ها را انجام می‌دهد. اگر انجام این کپی موقت صورت نمی‌گرفت، در حین فراخوانی آن از طریق چندین ترد مختلف، ممکن بود یکی از مشترکین delegate از آن قطع اشتراک می‌کرد و در این حالت فراخوانی تردی دیگر در همان لحظه، سبب کرش برنامه می‌شد.


استفاده از Null Conditional Operator بر روی Value types

الف) مقایسه با نال
کد ذیل را درنظر بگیرید:
 var code = webData?.Code;
در اینجا Code یک value type از نوع int است. در این حالت با بکارگیری Null Conditional Operator، خروجی این حاصل، از نوع <Nullable<int و یا ?int درنظر گرفته خواهد شد و با توجه به اینکه عبارات null>0 و همچنین null<0 هر دو false هستند، مقایسه‌ی این خروجی با 0 بدون مشکل انجام می‌شود. برای مثال مقایسه‌ی ذیل از نظر کامپایلر یک عبارت معتبر است و بدون مشکل کامپایل می‌شود:
 if (webData?.Code > 0)
{

}

ب) بازگشت مقدار پیش فرض دیگری بجای نال
اگر نیاز بود بجای null مقدار پیش فرض دیگری را بازگشت دهیم، می‌توان از null-coalescing operator سابق استفاده کرد:
 int count = response?.Results?.Count ?? 0;
در این مثال خاصیت CountT در اصل از نوع int تعریف شده‌است؛ اما بکارگیری .? سبب Nullable شدن آن خواهد شد. بنابراین امکان بکارگیری عملگر ?? یا null-coalescing operator نیز بر روی این متغیر وجود دارد.

ج) دسترسی به مقدار Value یک متغیر nullable
نمونه‌ی دیگر آن قطعه کد ذیل است:
 int? x = 10;
//var value = x?.Value; // invalid
Console.WriteLine(x?.ToString());
در اینجا برخلاف متغیر Code که از ابتدا nullable تعریف نشده‌است، متغیر x نال پذیر است. اما باید دقت داشت که با تعریف .? دیگر نیازی به استفاده از خاصیت Value این متغیر nullable نیست؛ زیرا .? سبب محاسبه و بازگشت خروجی آن می‌شود. بنابراین در این حالت، سطر دوم غیرمعتبر است (کامپایل نمی‌شود) و سطر سوم معتبر.


کار با indexer property و بررسی نال

اگر به عنوان بحث دقت کرده باشید، یک s جمع در انتهای Null-conditional operators ذکر شده‌است. به این معنا که این عملگر مقایسه‌ی با نال، صرفا یک شکل و فرم .? را ندارد. مثال ذیل در حین کار با آرایه‌ها و لیست‌ها بسیار مشاهده می‌شود:
 if (response != null && response.Results != null && response.Results.Addresses != null
  && response.Results.Addresses[0] != null && response.Results.Addresses[0].Zip == "63368")
{

}
در اینجا به علت بکارگیری indexer بر روی Addresses، دیگر نمی‌توان از عملگر .? که صرفا برای فیلدها، خواص، متدها و delegates طراحی شده‌است، استفاده کرد. به همین منظور، عملگر بررسی نال دیگری به شکل […]? برای این بررسی طراحی شده‌است:
 if(response?.Results?.Addresses?[0]?.Zip == "63368")
{

}
به این ترتیب 5 سطح بررسی نال فوق، به یک عبارت کوتاه کاهش می‌یابد.

 
موارد استفاده‌ی ناصحیح از عملگرهای مقایسه‌ی با نال

خوب، عملگر .? کار مقایسه‌ی با نال را خصوصا در دسترسی‌های چند سطحی به خواص و متدها بسیار ساده می‌کند. اما آیا باید در همه جا از آن استفاده کرد؟ آیا باید از این پس کلا استفاده از دات را فراموش کرد و بجای آن از .? در همه جا استفاده کرد؟
مثال ذیل را درنظر بگیرید:
 public void DoSomething(Customer customer)
{
    string address = customer?.Employees
                  ?.SingleOrDefault(x => x.IsAdmin)?.Address?.ToString();
    SendPackage(address);
}
در این مثال در تمام سطوح آن از .? بجای دات استفاده شده‌است و بدون مشکل کامپایل می‌شود. اما این نوع فراخوانی سبب خواهد شد تا یک سری از مشکلات موجود کاملا مخفی شوند؛ خصوصا اعتبارسنجی‌ها. برای مثال در این فراخوانی اگر مشتری نال باشد یا اگر کارمندانی را نداشته باشد، آدرسی بازگشت داده نمی‌شود. بنابراین حداقل دو سطح بررسی و اعتبارسنجی عدم وجود مشتری یا عدم وجود کارمندان آن در اینجا مخفی شده‌اند و دیگر مشخص نیست که علت بازگشت نال چه بوده‌است.
روش بهتر انجام اینکار، بررسی وضعیت customer و انتقال مابقی زنجیره‌ی LINQ به یک متد مجزای دیگر است:
 public void DoSomething(Customer customer)
{
   Contract.Requires(customer != null); 
   string address = customer.GetAdminAddress();
   SendPackage(address);
}