در C# 11 ارائهی شدهی به همراه NET 7.0.، واژهی کلیدی جدید file، جهت تعریف نوعهای محدود به یک فایل «File Scoped Types» ارائه شدهاست. این واژهی کلیدی را میتوان به تعریف هر نوع دلخواهی مانند class, interface, record, struct, enum, delegate اضافه کرد (منهای خواص، فیلدها و رخدادها؛ البته تا C# 11)، تا آن نوع، دیگر در سایر کلاسهای فایلهای برنامه، قابل دسترسی نباشد و سطح دید استفادهی از آن، تنها محدود به فایل جاری محل قرار گیری آن شود. به این ترتیب میتوان در یک فضای نام مشخص، چندین کلاس همنام را تعریف کرد؛ کاری که در نگارشهای پیشین #C، میسر نبود. بدیهی دیگر نمیتوان یک چنین نوعهایی را با سطوح دسترسی متداول internal و یا public، تعریف و ترکیب کرد.
یک مثال: نمونهای از نحوهی تعریف و استفادهی از File Scoped Types
فرض کنید دو فایل جدید را به نامهای File1.cs و File2.cs به پروژهی جاری اضافه کردهایم.
محتوای فایل File1.cs به صورت زیر است:
namespace CS11Tests;
file static class Post
{
public static string GetTitle() => "Title from File1.cs";
}
internal static class InternalClassFromFile1
{
public static string GetTitle() => Post.GetTitle();
}
و محتوای فایل File2.cs به نحو زیر تعریف شدهاست:
namespace CS11Tests;
file static class Post
{
public static string GetTitle() => "Title from File2.cs";
}
internal static class InternalClassFromFile2
{
public static string GetTitle() => Post.GetTitle();
}
اگر دقت کنید، ذیل فضای نام مشخص و ثابت CS11Tests، دو کلاس هم نام Post را داریم که اینبار با واژهی کلیدی file، شروع شدهاند و میدان دید دسترسی به آنها، محدود به همان فایل دربرگیرندهی آنها است و در سایر قسمتهای برنامه قابل دسترسی نیستند. اگر خواستیم بهنحوی از آنها در سایر قسمتهای برنامه نیز استفاده کنیم، مانند فایل Program.cs، میتوان یک تعریف متداول internal/public را مانند کلاسهای internal تعریف شده، ایجاد کرد و سپس به صورت
«غیرمستقیمی» به آنها دسترسی یافت:
using System.Security.AccessControl;
using CS11Tests;
using static System.Console;
WriteLine(InternalClassFromFile1.GetTitle());
WriteLine(InternalClassFromFile2.GetTitle());
امکان partial تعریف کردن نوعهای محدود به یک فایل در C# 11
در اینجا میتوان نوعهای محدود به یک فایل را partial نیز تعریف کرد؛ به شرطی که تمام تعاریف آنها داخل همان فایل قرار گیرند:
namespace CS11Tests;
file static partial class Post
{
internal static string GetFileScopeTitle() => "Title from File3.cs";
}
file static partial class Post
{
internal static string AnotherGetFileScopeTitle() => "Another Title from File3.cs";
}
یک سؤال: اگر در یک فایل، file class Post و در فایلی دیگر، کلاس هم نام داخلی internal class Post را تعریف کردیم، آیا میتوان از نمونهی همنام internal، در کلاس file دار استفاده کرد؟
پاسخ: خیر!
فرض کنید در File4.cs چنین تعریفی را داریم:
namespace CS11Tests;
internal static class Post
{
public static string GetTitle() => "Title from File4.cs";
}
در اینجا در فضای نام مشخصی، کلاس Post، به صورت internal تعریف شدهاست. اکنون در File3.cs، مجدد تعریف کلاس همنام Post را اینبار به صورت file داریم:
namespace CS11Tests;
file static class Post
{
internal static string GetFileScopeTitle() => CS11Tests.Post.GetTitle() + "Title from File3.cs";
}
این قطعه کد کامپایل نمیشود. چون Post ای که در اینجا قابل استفادهاست، دقیقا همان کلاس Post جاری این فایل است و نه نمونهی همنام internal در فایلی دیگر.
خروجی کامپایلر C# 11 در مورد سطح دسترسی file
کامپایلر C# 11 جهت جلوگیری از تداخل نامهای حاصل از تعریف کلاسهای با سطح دسترسی file، از قالب زیر:
<SourceFileNameWithoutExtension>F$index$_TypeName
برای نامگذاری نهایی اینگونه نوعها استفاده میکند؛ مانند مثال زیر که مرتبط با کلاس Post تعریف شدهی در فایل File1.cs است:
internal static class <File1>F3A5590C89B71B2DB20A548228781187A11D076C0CC91E851A4EE796FFE808F8F__Post
{
public static string GetTitle()
{
return "Title from File1.cs";
}
}
Index منحصربفرد استفاده شده، مشکل تداخل نامها را برطرف میکند و به علت وجود <> در تعریف این نامهای ویژه، امکان استفادهی از آنها در سایر قسمتها و فایلهای برنامه وجود ندارد.
تاکنون از این روش نامگذاری ویژه، در موارد دیگری مانند async/await , lambda, anonymous method, anonymous types نیز استفاده شدهاست.
چرا قابلیت «File Scoped Types» به زبان C# 11 اضافه شدهاست؟
- جهت کدهای تولیدی توسط ابزارها: گاهی از اوقات، تولید کنندههای کد،
از یک نام مشخص مانند DataSet، بارها و بارها استفاده میکنند. برای جلوگیری از
تداخل اینها، عموما از تعریف تو در توی کلاسها استفاده میشود و یا نام
آنها را با ایندکسهایی مانند DateSet1، DateSet2 و امثال آنها مشخص
میکنند. وجود واژهی کلیدی file، کار ابزارهای تولید کنندهی کد را
سادهتر میکند.
- برای ساده سازی تعریف متدهای الحاقی: با استفاده از سطح دسترسی
فایل میتوان از تداخل متدهای الحاقی هم نام و همچنین شلوغ شدن
intellisense جلوگیری کرد. به این ترتیب میتوان کلاسهای حاوی Extension
method مختص به یک فایل را ایجاد کرد که در سایر قسمتهای برنامه قابل
دسترسی نباشند.
- کاهش تعریف کلاسهای تو در تو: همانطور که عنوان شد، یکی از
روشهای مقابلهی با مشکل تعریف کلاسهای هم نام در یک فضای نام مشخص،
تعریف nested classes است. با ارائهی واژهی کلیدی file، میتوان یک سطح
فرو رفتگی تعریف کلاسها را کاهش داد و به کدهای تمیزتری رسید.
- امکان کپسوله سازیهای بهتر: عموما کامپوننتها و ماژولها، از چند
کلاس تشکیل میشوند. با وجود واژهی کلیدی file، میتوان به سطح بالاتری
از خصوصی سازی نوعها، بدون نیاز به تعریف نوعهای private و یا nested
private رسید.
- سهولت نوشتن کلاسهای آزمونهای واحد: عموما هر کلاس آزمون، از
نوعها و دادههای خاص خودش استفاده میکنند و در اینجا میتوان سطح دسترسی
این تعاریف را بسیار محدود و مختص به همان فایل Test کرد.