اشتراک‌ها
بررسی تغییرات Blazor در دات نت 8

What's New in Blazor for .NET 8
Come find out about the future of Blazor in .NET 8! We'll explore all the upcoming features and improvements, including our effort to create a unified full stack web UI programming model that combines the strengths of client and server. We hope to see you there!

You will learn:
How Blazor is becoming the best option for full stack web development
How Blazor in .NET 8 will provide full flexibility to build web apps however works best for you
How to try out the latest Blazor features in .NET 8
 

بررسی تغییرات Blazor در دات نت 8
مطالب
C# 12.0 - Interceptors
به C# 12 و دات‌نت 8، ویژگی «آزمایشی» جدیدی به نام Interceptors اضافه شده‌است که به آن «monkey patching» هم می‌گویند. هدف از آن، جایگزین کردن یک پیاده سازی، با پیاده سازی دیگری است. به این ترتیب توسعه دهندگان دات‌نتی می‌توانند فراخوانی متدهایی خاص را ره‌گیری کرده (interception) و سپس آن‌را به فراخوانی یک پیاده سازی جدید، هدایت کنند.


Interceptor چیست؟

از زمان ارائه‌ی NET 8 preview 6 SDK. به بعد، امکان ره‌گیری هر متدی از کدهای برنامه، به دات‌نت اضافه شده‌است؛ به همین جهت از واژه‌ی Interceptor/ره‌گیر در اینجا استفاده می‌شود. خود تیم دات‌نت از این قابلیت در جهت بازنویسی پویای قسمت‌هایی از کدهای زیرساخت دات‌نت که از Reflection استفاده می‌کنند، با نگارش‌های کامپایل شده‌ی مختص به برنامه‌ی شما، کمک می‌گیرند. به این ترتیب سرعت و کارآیی برنامه‌های دات‌نت 8، بهبود قابل ملاحظه‌ای را پیدا کرده‌اند. برای مثال ahead-of-time compilation (AOT) در دات‌نت 8 و ASP.NET Core 8x بر اساس این ویژگی پیاده سازی شده‌است. این ویژگی جدید، مکمل source generators است که در نگارش‌های پیشین دات‌نت ارائه شده بود.


بررسی  Interceptors با تهیه‌ی یک مثال ساده

فرض کنید می‌خواهیم فراخوانی متد GetText زیر را ره‌گیری کرده و سپس آن‌را با نمونه‌ی دیگری جایگزین کنیم:
namespace CS8Tests;

public class InterceptorsSample
{
    public string GetText(string text)
    {
        return $"{text}, World!";
    }
}
برای اینکار ابتدا نیاز است یک فایل جدید را به نام InterceptsLocationAttribute.cs با محتوای زیر به پروژه اضافه کرد:
namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class InterceptsLocationAttribute : Attribute
{
    public InterceptsLocationAttribute(string filePath, int line, int character)
    {
    }
}
همانطور که در مقدمه‌ی بحث هم عنوان شد، این ویژگی هنوز آزمایشی است و نهایی نشده و ویژگی فوق نیز هنوز به دات‌نت اضافه نشده‌است. به همین جهت فعلا باید آن‌را به صورت دستی به پروژه اضافه کرد و احتمالا در نگارش‌های بعدی دات‌نت، امضای آن تغییر خواهد کرد ... یا حتی ممکن است بطور کامل حذف شود!

سپس فرض کنید فراخوانی متد GetText در فایل Program.cs برنامه به صورت زیر انجام شده‌است:
using CS8Tests;

var example = new InterceptorsSample();
var text = example.GetText("Hello");
Console.WriteLine(text); //Hello, World!
یعنی متد GetText، در سطر چهارم و کاراکتر 20 ام آن فراخوانی شده‌است. این اعداد مهم هستند!

در ادامه از این اطلاعات در ره‌گیر سفارشی زیر استفاده خواهیم کرد:
using System.Runtime.CompilerServices;

namespace CS8Tests;

public static class MyInterceptor
{
    [InterceptsLocation("C:\\Path\\To\\CS8Tests\\Program.cs", 4, 20)] 
    public static string InterceptorMethod(this InterceptorsSample example, string text)
    {
        return $"{text}, DNT!";
    }
}
این ره‌گیر که به صورت متدی الحاقی برای کلاس InterceptorsSample دربرگیرنده‌ی متد GetText تهیه می‌شود، کار جایگزینی فراخوانی آن‌را در سطر چهارم و کاراکتر 20 ام فایل Program.cs انجام می‌دهد. امضای پارامترهای این متد، باید با امضای پارامترهای متد ره‌گیری شده، یکی باشد.

اکنون اگر برنامه را اجرا کنیم ... با خطای زیر مواجه می‌شویم:
 error CS9137: The 'interceptors' experimental feature is not enabled in this namespace. Add
'<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);CS8Tests</InterceptorsPreviewNamespaces>'
to your project.
عنوان می‌کند که این ویژگی آزمایشی است و باید فایل csproj. را به صورت زیر تغییر داد تا بتوان از آن استفاده نمود:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <!--<NoWarn>Test001</NoWarn>-->
    <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);CS8Tests</InterceptorsPreviewNamespaces>
  </PropertyGroup>
</Project>
اینبار برنامه کامپایل شده و اجرا می‌شود. در این حالت خروجی جدید برنامه، خروجی تامین شده‌ی توسط ره‌گیر سفارشی ما است:
Hello, DNT!


سؤال: آیا ره‌گیری انجام شده، در زمان کامپایل انجام می‌شود یا در زمان اجرا؟

برای این مورد می‌توان به Low-Level C# code تولیدی مراجعه کرد. برای مشاهده‌ی یک چنین کدهایی می‌توانید از منوی Tools->IL Viewer برنامه‌ی Rider استفاده کرده و در برگه‌ی ظاهر شده، گزینه‌ی Low-Level C# آن‌را انتخاب نمائید:
using CS8Tests;
using System;
using System.Runtime.CompilerServices;

[CompilerGenerated]
internal class Program
{
  private static void <Main>$(string[] args)
  {
    Console.WriteLine(new InterceptorsSample().InterceptorMethod("Hello"));
  }

  public Program()
  {
    base..ctor();
  }
}
همانطور که مشاهده می‌کنید، این ره‌گیری و جایگزینی، در زمان کامپایل انجام شده و کامپایلر، به‌طور کامل نحوه‌ی فراخوانی متد GetText اصلی را به متد ره‌گیر ما تغییر داده و بازنویسی کرده‌است.


سؤال: آیا این قابلیت واقعا کاربردی است؟!

اکنون شاید این سؤال مطرح شود که ... واقعا چه کسی قرار است مسیر کامل یک فایل، شماره سطر و شماره ستون فراخوانی متدی را به اینگونه در اختیار سیستم ره‌گیری قرار دهد؟! آیا واقعا این قابلیت، یک قابلیت کاربردی و مناسب است؟!
اینجا است که اهمیت source generators مشخص می‌شود. توسط source generators دسترسی کاملی به syntax trees وجود دارد و همچنین یکسری اطلاعات تکمیلی مانند FilePath و سپس CSharpSyntaxNodeها که دسترسی به داده‌های متد ()GetLocation را دارند که مکان دقیق سطر و ستون‌های فراخوانی‌ها را مشخص می‌کند.


کاربردهای فعلی ره‌گیرها در دات نت 8

در دات نت 8، این موارد با استفاده از ره‌گیرها بهینه سازی شده و سرعت آن‌ها افزایش یافته‌اند:
- فراخوانی‌هایی که تمام اطلاعات آن‌ها در زمان کامپایل فراهم است، مانند Regex.IsMatch(@"a+b+") که از یک الگوی ثابت و مشخص استفاده می‌کند، ره‌گیری شده و پیاده سازی آن با کدی استاتیک، جایگزین می‌شود.
- در ASP.NET Minimal API، استفاده از lambda expressions جهت ارائه‌ی تعاریفی مانند:
app.MapGet("/products", handler: (int? page, int? pageLength, MyDb db) => { ... })
مرسوم است. این نوع فراخوانی‌ها نیز توسط ره‌گیرها برای جایگزینی handler آن‌ها با کدهای استاتیک، جهت بالابردن کارآیی و کاهش تخصیص‌های حافظه انجام می‌شود.
- بهبود کارآیی foreach loops جهت استفاده از ریاضیات برداری و SIMD در صورت امکان.
- بهبود کارآیی تزریق وابستگی‌ها، زمانیکه به تعاریف مشخصی مانند ()<provider.Register<MyService ختم می‌شود.
- بجای استفاده از expression trees در زمان اجرای برنامه، اکنون می‌توان کدهای SQL معادل را در زمان کامپایل برنامه تولید کرد.
- بهبود کارآیی Serializers، زمانیکه از یک نوع مشخص مانند ()<Serialize<MyType استفاده می‌شود و کامپایلر می‌تواند آن‌را با کدهای زمان کامپایل، جایگزین کند.


محدودیت‌های ره‌گیرها در دات‌نت 8

- ره‌گیرهای دات‌نت 8 فقط با متدها کار می‌کنند.
- مسیر ارائه شده حتما باید یک مسیر کامل و مشخص باشد. یعنی اگر این قطعه کد، به سیستم دیگری منتقل شود، کامپایل نخواهد شد و امکان ارائه‌ی مسیرهای نسبی وجود ندارد.
- امضای متدها، حتما باید یکی باشد. یعنی نمی‌توان یک ره‌گیر جنریک را تعریف کرد.
اشتراک‌ها
NET 8 Preview 3. منتشر شد

.NET 8 Preview 3 is now available. It includes changes to build paths, workloads, Microsoft.Extensions, and containers. It also includes performance improvements in the JIT, for Arm64, and dynamic PGO. If you missed the March preview, you may want to read the Preview 2 post. 

NET 8 Preview 3. منتشر شد
اشتراک‌ها
با سی شارپ 8 از دست NullReferenceExceptions ها خلاص شوید

A .NET guideline specifies that an application should never throw a NullReferenceException. However, many applications and libraries do. The NullReferenceException is the most common exception happening. That’s why C# 8 tries to get rid of it. With C# 8, reference types are not null be default. This is a big change, and a great feature. However, what about all the legacy code? Can old libraries be used with C# 8 applications, and can C# 7 applications make use of C# 8 libraries?

This article demonstrates how C# 8 allows mixing old and new assemblies. 

با سی شارپ 8 از دست NullReferenceExceptions ها خلاص شوید
اشتراک‌ها
بازه‌ها و الگوهای بازگشتی در C# 8
  • C# 8 Adds Ranges and Recursive Patterns
  • Ranges easily define a sequence of data, replacing the Enumberable.Range()
  • Recursive Patterns brings an F#-like construct to C#
  • Recursive Patterns is an awesome feature, it giving you the flexibility to testing the data against a sequence of conditions and performing further computations based on the condition met.
  • Ranges is very useful to generate sequences of numbers in the form of a collection or a list. 
بازه‌ها و الگوهای بازگشتی در C# 8
مطالب
C# 12.0 - Experimental Attribute
گاهی از اوقات ممکن است یک ویژگی تکمیل نشده، سر از نگارش‌های release درآورد؛ چون نیاز به دریافت بازخوردی در این مورد وجود دارد و یا اینکه قرار است در طی چند مرحله تکمیل شود. برای اینکه یک چنین مساله‌ای خصوصا از طرف نویسندگان کتابخانه‌ها و فریم‌ورک‌ها مشخص شود، ویژگی جدید System.Diagnostics.CodeAnalysis.ExperimentalAttribute به دات‌نت 8 اضافه شده‌است.
در این حالت اگر کدی، شروع به استفاده‌ی از یک چنین عضو‌های آزمایشی کند، یک خطای زمان کامپایل رخ می‌دهد؛ مگر اینکه آن قطعه کد نیز دقیقا با همین ویژگی مزین شود. در اینجا می‌توان نوع‌ها، اسمبلی‌ها و حتی اعضای آن‌ها را آزمایشی تعریف کرد. اگر کل یک نوع را به صورت آزمایشی معرفی کنیم، تمام اعضای آن هم آزمایشی خواهند بود.


بررسی ویژگی Experimental با یک مثال

در ادامه نحوه‌ی اعمال ویژگی Experimental را به همراه یک diagnosticId که به کل یک کلاس اعمال شده‌است، مشاهده می‌کنید. از این diagnosticId در حین تولید متن خطاها و یا برای شناسایی آن‌ها، استفاده می‌شود:
using System.Diagnostics.CodeAnalysis;

namespace CS8Tests;

[Experimental(diagnosticId: "Test001")]
public class ExperimentalAttributeDemo
{
    public void Print()
    {
        Console.WriteLine("Hello Experimental Attribute");
    }
}
پس از این تعریف، اگر در قسمت دیگری از برنامه بخواهیم از این کلاس استفاده کنیم:
var experimentalAttributeDemo = new ExperimentalAttributeDemo();
با خطای زیر مواجه خواهیم شد:
error Test001: 'CS8Tests.ExperimentalAttributeDemo' is for evaluation purposes only
and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
برای مواجه شدن با یک چنین خطایی، می‌توان دو روش زیر را در پیش گرفت:
الف) غیرفعال کردن سراسری گزارش این نوع خطاها در فایل csproj. برنامه:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <NoWarn>Test001</NoWarn>
  </PropertyGroup>
</Project>
در اینجا اضافه شدن NoWarn را بر اساس diagnosticId ویژگی آزمایشی تعریف شده، مشاهده می‌کنید. این تنظیم سراسری است و به تمام قسمت‌های پروژه‌ی جاری اعمال می‌شود. اضافه کردن آن هم فقط یکبار صورت می‌گیرد.

ب) غیرفعال کردن موضعی آن، صرفا در محل استفاده
برای غیرفعال کردن محلی این بررسی، تنها کافی است با استفاده از pragma warning# یکبار آن‌را غیرفعال کرده و پس از پایان کار، مجددا آن‌را فعال کنیم:
#pragma warning disable Test001
var demo = new ExperimentalAttributeDemo();
#pragma warning restore Test001
همانطور که مشاهده می‌کنید، این فعال و غیرفعال کردن هم بر اساس diagnosticId صورت می‌گیرد. بدیهی است این تنظیم سراسری نبوده و درصورت بکارگیری این قطعه کد در قسمت‌های دیگر برنامه، باید مجددا تکرار شود.

و اگر این مثال را کمی پیچیده‌تر کنیم، به حالت زیر می‌رسیم:
using System.Diagnostics.CodeAnalysis;

namespace CS8Tests;

[Experimental(diagnosticId: "Test001")]
public class ExperimentalAttributeDemo
{
    [Experimental(diagnosticId: "Test002")]
    public void Print()
    {
        Console.WriteLine("Hello Experimental Attribute");
    }
}
در اینجا دو ویژگی آزمایشی، با دو diagnosticId متفاوت تعریف شده‌اند. در این حالت اگر سعی کنیم قطعه کد زیر را کامپایل کنیم:
var demo = new ExperimentalAttributeDemo();
demo.Print();
به ازای هر ویژگی آزمایشی تعریف شده، یک خطای کامپایلر جداگانه را دریافت می‌کنیم. به همین جهت برای رفع این خطاها، یا باید از روش غیرفعال سازی سراسری آن‌ها پیش‌رفت:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <NoWarn>Test001,Test002</NoWarn>
  </PropertyGroup>
</Project>
 و یا می‌توان به صورت محلی زیر عمل کرد:
#pragma warning disable Test001,Test002
var demo = new ExperimentalAttributeDemo();
demo.Print();
#pragma warning restore Test001,Test002
در اینجا ذکر هر دو diagnosticId، ضروری است.