اشتراکها
اشتراکها
Syntax highlighting شبیه به GitHub
نظرات مطالب
پشتیبانی از Generic Attributes در C# 11
روش تبدیل ServiceFilterAttribute در ASP.NET Core به یک نمونهی Type-Safe
این ویژگی در اصل به صورت زیر تعریف شدهاست:
/// <summary> /// Instantiates a new <see cref="ServiceFilterAttribute"/> instance. /// </summary> /// <param name="type">The <see cref="Type"/> of filter to find.</param> public ServiceFilterAttribute(Type type) { ServiceType = type ?? throw new ArgumentNullException(nameof(type)); }
public class TypeSafeServiceFilterAttribute<T> : ServiceFilterAttribute where T: IActionFilter { public TypeSafeServiceFilterAttribute():base(typeof(T)) { } }
[Route("api/[controller]")] [ApiController] public class CoursesController : ControllerBase { [HttpGet] [TypeSafeServiceFilterAttribute<ExampleFilter>()] public IActionResult Get() { return Ok(); } }
C# 7 به همراه تغییرات قابل توجهی در مورد نحوهی دریافت خروجی از متدها است که نمونههایی از آنها را مانند tuples و out variable، پیشتر بررسی کردیم. در ادامه تغییرات جدید دیگری را به نام ref locals و ref returns نیز بررسی خواهیم کرد و هدف از آن، کاهش تعداد بار کپی کردن مقادیر و یا اعمال dereferencing جهت بالا بردن کارآیی برنامه هستند.
انتقال توسط مقدار
اگر پارامتری به صورت value type تعریف شود، این مقدار درون متد، ایجاد شده و حافظهای به آن اختصاص داده میشود و سپس در انتهای متد تخریب خواهد شد. بنابراین تغییری در مقدار آن، سبب انعکاس آن به فراخوان متد، نخواهد شد.
در این مثال متد PassByValue تنها یک کپی از مقدار متغیر a را دریافت میکند. بنابراین هر تغییری که درون متد PassByValue بر روی این مقدار دریافتی رخ دهد، به فراخوان آن منتقل نخواهد شد.
انتقال توسط ارجاع
برای بازگشت مقدار تغییر داده شدهی توسط یک متد، میتوان یک نوع خروجی را برای آن تعریف کرد. راه دیگر آن تعریف یک پارامتر توسط واژهی کلیدی ref است. پارامتری که به این روش تعریف شود، هم ارسال کنندهی مقدار و هم دریافت کنندهی تغییرات خواهد بود.
در این مثال متغیر x به مقدار متغیر a اشاره میکند. بنابراین هر تغییری که بر روی آن صورت گیرد، به متغیر a هم اعمال و منعکس خواهد شد.
متغیرهای out
با استفاده از واژهی کلیدی ref، میتوان یک مقدار را هم به تابع ارسال کرد و هم از آن دریافت نمود. اما اگر تنها قرار است مقداری از تابع بازگشت داده شود، میتوان از متغیرهای out استفاده کرد.
در حالت استفادهی از واژهی کلیدی out، نیازی به مقدار دهی اولیهی متغیر ارسالی به متد نیست و در اینجا روش جدید تعریف متغیرهای out را در C# 7 نیز مشاهده میکنید که نیازی نیست تا ابتدا int a را تعریف کرد و سپس در متد Out آنرا فراخوانی نمود. میتوان کل عملیات را به صورت خلاصه در یک سطر ذکر کرد.
البته اگر تنها قرار است یک مقدار را از یک متد دریافت کنیم، بهتر است نوع خروجی متد را مشخص کرد و از آن استفاده نمود؛ بجای استفادهی از متغیرهای out. یک نمونه کاربرد مفید متغیرهای out در خود فریم ورک و توسط متد TryParse وجود دارد:
Ref Locals در C# 7
در C# 7، امکان تعریف متغیرهای محلی از نوع ref نیز وجود دارد:
در اینجا متغیر x1 دارای ارجاعی است به متغیر x. بنابراین تغییر مقدار x1، به متغیر x نیز منعکس خواهد شد.
باید دقت داشت که نمیتوان یک مقدار را به یک ref variable نسبت داد:
به این ترتیب امکان اشتباه گرفتن بین value variables و reference variables توسط کامپایلر گوشزد خواهد شد:
Ref Returns در C# 7
در C# 7، واژهی کلیدی ref را به همراه نوع خروجی نیز میتوان بکار برد:
در این مثال ارجاعی به اولین عضو آرایهی تعریف شده، به عنوان خروجی متد بازگشت داده میشود. همچنین میتوان این کد را به صورت سادهتر ذیل نیز نوشت:
باید دقت داشت که یک متغیر int محلی را نمیتوان به صورت ref بازگشت داد. علت اینجا است که متغیر int یک value type است و در انتهای متد تخریب خواهد شد. بنابراین امکان بازگشت آن به صورت ref میسر نیست. اما آرایهها reference type بوده و بر روی heap تشکیل میشوند. در این حالت متغیر int داخل آرایه را میتوان به صورت ref بازگشت داد.
هرچند امکان بازگشت یک متغیر محلی int به صورت ref وجود ندارد، اما اگر این متغیر به صورت ref به تابع ارسال شده باشد، این امکان میسر است:
چند نکته: امکان تعریف فیلد ref و یا خاصیت get;set دار از نوع ref وجود ندارد. اما تعریف خواصی که یک ref را بازگشت میدهند، میسر هستند:
مزیت کار با ref returns
ref returnها شاید آنچنان در کدهای روزمرهی #C بکارگرفته نشوند، اما نیاز به کدهای unsafe و کار مستقیم با pointers را به حداقل میرسانند و به آن میتوان لقب safe pointer را اطلاق کرد؛ از این جهت که این کد هنوز هم یک managed code است و نه یک unsafe code.
مهمترین مزیت این قابلیت جدید را در قطعه کد فوق ملاحظه میکنید. در طراحی بازیها عموما استفادهی از آرایههای بزرگ از پیش تخصیص داده شدهی structها بسیار مرسوم است (چون میزان مصرف حافظهی کمتری را نسبت به نوعهای ارجاعی دارند و فشار کمتری را به GC وارد میکنند). اکنون با معرفی این قابلیت، دیگر نیازی نیست تا مدام آرایههای بزرگی از structها را از قسمتی به قسمت دیگر کپی کرد و سپس بر روی عناصری از آنها عملیاتی را انجام داد و مجددا این حاصل را به مکان مدنظر کپی کرد. در اینجا بدون کپی کردن value types میتوان با ایجاد ارجاعی به آنها، تغییرات مدنظر را به آنها اعمال کرد.
انتقال توسط مقدار
اگر پارامتری به صورت value type تعریف شود، این مقدار درون متد، ایجاد شده و حافظهای به آن اختصاص داده میشود و سپس در انتهای متد تخریب خواهد شد. بنابراین تغییری در مقدار آن، سبب انعکاس آن به فراخوان متد، نخواهد شد.
static void PassByValueSample() { int a = 1; PassByValue(a); Console.WriteLine($"after the invocation of {nameof(PassByValue)}, {nameof(a)} = {a}"); } static void PassByValue(int x) { x = 2; }
انتقال توسط ارجاع
برای بازگشت مقدار تغییر داده شدهی توسط یک متد، میتوان یک نوع خروجی را برای آن تعریف کرد. راه دیگر آن تعریف یک پارامتر توسط واژهی کلیدی ref است. پارامتری که به این روش تعریف شود، هم ارسال کنندهی مقدار و هم دریافت کنندهی تغییرات خواهد بود.
static void PassByReferenceSample() { int a = 1; PassByReference(ref a); Console.WriteLine($"after the invocation of {nameof(PassByReference)}, {nameof(a)} = {a}"); } static void PassByReference(ref int x) { x = 2; }
متغیرهای out
با استفاده از واژهی کلیدی ref، میتوان یک مقدار را هم به تابع ارسال کرد و هم از آن دریافت نمود. اما اگر تنها قرار است مقداری از تابع بازگشت داده شود، میتوان از متغیرهای out استفاده کرد.
static void OutSample() { Out(out int a); Console.WriteLine($"after the invocation of {nameof(Out)}, {nameof(a)} = {a}"); } static void Out(out int x) { x = 2; }
البته اگر تنها قرار است یک مقدار را از یک متد دریافت کنیم، بهتر است نوع خروجی متد را مشخص کرد و از آن استفاده نمود؛ بجای استفادهی از متغیرهای out. یک نمونه کاربرد مفید متغیرهای out در خود فریم ورک و توسط متد TryParse وجود دارد:
if (int.TryParse("42", out var result)) { Console.WriteLine($"the result is {result}"); }
Ref Locals در C# 7
در C# 7، امکان تعریف متغیرهای محلی از نوع ref نیز وجود دارد:
int x = 3; ref int x1 = ref x; x1 = 2; Console.WriteLine($"local variable {nameof(x)} after the change: {x}");
باید دقت داشت که نمیتوان یک مقدار را به یک ref variable نسبت داد:
ref int i = sequence.Count();
ref int number1 = null; // ERROR ref int number2 = 42; // ERROR
Ref Returns در C# 7
در C# 7، واژهی کلیدی ref را به همراه نوع خروجی نیز میتوان بکار برد:
static ref int ReturnByReference() { int[] arr = { 1 }; ref int x = ref arr[0]; return ref x; }
static ref int ReturnByReference2() { int[] arr = { 1 }; return ref arr[0]; }
هرچند امکان بازگشت یک متغیر محلی int به صورت ref وجود ندارد، اما اگر این متغیر به صورت ref به تابع ارسال شده باشد، این امکان میسر است:
static ref int ReturnByReference3(ref int x) { x = 2; return ref x; }
چند نکته: امکان تعریف فیلد ref و یا خاصیت get;set دار از نوع ref وجود ندارد. اما تعریف خواصی که یک ref را بازگشت میدهند، میسر هستند:
class Thing1 { ref string _Text1; /* Error */ ref string Text2 { get; set; } /* Error */ string _text = "Text"; ref string Text3 { get { return ref _text; } } // Properties that return a reference are allowed }
مزیت کار با ref returns
ref returnها شاید آنچنان در کدهای روزمرهی #C بکارگرفته نشوند، اما نیاز به کدهای unsafe و کار مستقیم با pointers را به حداقل میرسانند و به آن میتوان لقب safe pointer را اطلاق کرد؛ از این جهت که این کد هنوز هم یک managed code است و نه یک unsafe code.
private MyBigStruct[] array = new MyBigStruct[10]; private int current; public ref MyBigStruct GetCurrentItem() { return ref array[current]; }
اشتراکها
IQueryable و طراحیهای نشتیدار
اشتراکها
زبان برنامه نویسی Spider
اشتراکها
زبان C چگونه پدید آمد؟
Finalizers are interesting and dangerous because they are an environment in which everything you know is wrong.
I’ve written a lot about the perils of C# finalizers / destructors
(either name is fine) over the years, but it’s scattered in little bits
over the internet. In this series I’m going to try to get everything in
one place; here are a bunch of things that many people believe about
finalizers, all of which are wrong.
Finalizers are interesting and dangerous because they are an environment in which everything you know is wrong.
I’ve written a lot about the perils of C# finalizers / destructors
(either name is fine) over the years, but it’s scattered in little bits
over the internet. In this series I’m going to try to get everything in
one place; here are a bunch of things that many people believe about
finalizers, all of which are wrong.