مقدمه

به فصل شانزدهم خوش آمدید! در این فصل به بررسی نحوه‌ی ساخت و پیکربندی کتابخانه‌های کلاس، یعنی همان واحدهای قابل استفاده مجدد (reusable) در .NET، خواهیم پرداخت. اولین و بنیادی‌ترین قدم برای ساخت کدی سازمان‌یافته و قابل مدیریت، استفاده از فضاهای نام (Namespaces) است. ما از ابتدای این دوره، با استفاده از عبارت using System; از انواع داده‌ی موجود در فضای نام System استفاده کرده‌ایم. فضای نام، مکانیزمی برای گروه‌بندی انواع داده‌ی مرتبط (مانند کلاس‌ها، ساختارها، اینترفیس‌ها و ...) و جلوگیری از تداخل نام (name collision) است. در این درس، یاد می‌گیریم که چگونه فضاهای نام سفارشی خود را برای سازماندهی کدهایمان تعریف کنیم.

مشکل: آلودگی فضای نام عمومی

تصور کنید در حال ساخت یک برنامه‌ی بزرگ هستید. شما ممکن است صدها کلاس مختلف داشته باشید. اگر تمام این کلاس‌ها را در یک سطح و بدون هیچ‌گونه دسته‌بندی تعریف کنید، با دو مشکل اساسی مواجه می‌شوید:

  1. پیدا کردن و مدیریت کد دشوار می‌شود: پیدا کردن یک کلاس خاص در میان صدها کلاس دیگر بسیار سخت خواهد بود.
  2. تداخل نام رخ می‌دهد: اگر شما یک کلاس به نام Logger بسازید و سپس از یک کتابخانه‌ی خارجی استفاده کنید که آن هم یک کلاس Logger دارد، کامپایلر نمی‌داند که شما به کدام یک ارجاع می‌دهید و با خطای تداخل نام مواجه می‌شوید.

فضاهای نام این مشکلات را با ایجاد یک "قلمرو" یا "دامنه" برای انواع داده حل می‌کنند.

تعریف یک فضای نام سفارشی

برای تعریف یک فضای نام، از کلمه‌ی کلیدی namespace و به دنبال آن، یک نام منحصر به فرد استفاده می‌کنیم. قرارداد رایج برای نام‌گذاری فضاهای نام، استفاده از نام شرکت یا محصول و به دنبال آن، نام تکنولوژی یا ویژگی است که با نقطه از هم جدا می‌شوند (مثلاً MyCompany.DataAccess.SqlServer).

Copy Icon Shapes.cs
namespace MyDrawingApp.Shapes
{
    public class Circle { /* ... */ }
    public class Rectangle { /* ... */ }
}
Copy Icon Utilities.cs
namespace MyDrawingApp.Utilities
{
    public class Logger { /* ... */ }
}

استفاده از انواع داده‌ی تعریف شده

پس از اینکه انواع خود را در یک فضای نام قرار دادید، برای استفاده از آن‌ها در یک فایل دیگر، دو راه دارید:

استفاده از نام کامل (Fully Qualified Name): شما می‌توانید همیشه با ذکر کامل نام فضای نام به آن نوع دسترسی پیدا کنید. این روش صریح است اما می‌تواند کد را طولانی کند.
MyDrawingApp.Shapes.Circle myCircle = new();
استفاده از عبارت using: روش بهتر و رایج‌تر، افزودن یک عبارت using در ابتدای فایل است. این کار به کامپایلر می‌گوید که تمام انواع موجود در آن فضای نام را به محدوده‌ی فعلی "وارد" کند تا بتوانید از آن‌ها بدون ذکر نام کامل استفاده نمایید.
Copy Icon Program.cs
using MyDrawingApp.Shapes;
using MyDrawingApp.Utilities;

Circle myCircle = new();
Logger myLogger = new();

با این کار، می‌توانید به راحتی از انواع موجود در فضای نام بدون نیاز به ذکر نام کامل آن‌ها استفاده کنید.

حل تداخل نام با نام مستعار (alias)

حال فرض کنید از یک کتابخانه‌ی خارجی نیز استفاده می‌کنیم که آن هم یک کلاس Logger در فضای نام ThirdParty.Logging دارد. اگر هر دو فضای نام را using کنیم، چه اتفاقی می‌افتد؟

Copy Icon Program.cs
using MyDrawingApp.Utilities;
using ThirdParty.Logging;

// The following line will cause a compile-time error.
// Error: 'Logger' is an ambiguous reference between 'MyDrawingApp.Utilities.Logger' and 'ThirdParty.Logging.Logger'.
Logger logger = new();

برای حل این مشکل، می‌توانیم یک نام مستعار (alias) برای یکی از انواع یا هر دو تعریف کنیم.

Copy Icon Program.cs
using MyLogger = MyDrawingApp.Utilities.Logger;
using ThirdPartyLogger = ThirdParty.Logging.Logger;

// Now the code is clear and unambiguous.
MyLogger logger1 = new();
ThirdPartyLogger logger2 = new();

فضاهای نام و اسمبلی‌ها

یک نکته‌ی مهم این است که بین فضای نام و اسمبلی (Assembly) تفاوت قائل شویم. اسمبلی، واحد فیزیکی استقرار کد در .NET است (یک فایل .dll یا .exe). فضای نام، یک مفهوم کاملاً منطقی و برای سازماندهی است.

  • یک اسمبلی واحد می‌تواند شامل چندین فضای نام باشد.
  • یک فضای نام واحد می‌تواند در چندین اسمبلی مختلف پخش شده باشد (برای مثال، فضای نام System در چندین اسمبلی از فریم‌ورک .NET وجود دارد).

در درس بعدی، به تفصیل به بررسی نقش اسمبلی‌ها خواهیم پرداخت.