לכל טיפוס ב-.NET יש שם מלא שמורכב מ-namespace ומשם הטיפוס: System.Console, System.Collections.Generic.List<T>, System.Text.StringBuilder. namespaces מקבצים טיפוסים קשורים ומונעים התנגשויות שמות, כך שהמחלקה Order או Timer שלכם יכולה להתקיים לצד מחלקות באותו שם מספריות. הנחיית using בראש קובץ מייבאת namespace כדי שתוכלו להשתמש בשמות הקצרים.
פלט:
Cart: tea, honey
1
הנחיית using משפיעה רק על שמות בזמן קומפילציה. היא לא טוענת ספרייה ולא מוסיפה קוד לתוכנית; אילו ספריות זמינות נקבע לפי ההפניות של הפרויקט וחבילות ה-NuGet שלו. אם הקומפיילר מדווח שטיפוס או namespace "could not be found (are you missing a using directive or an assembly reference?)", שגיאה CS0246, אז או ששורת ה-using חסרה או שאין הפניה לחבילה.
הצהרה על namespaces משלכם
שימו טיפוסים בתוך בלוק namespace. אפשר לקנן namespaces, לכתוב אותם עם נקודות, ואותו namespace יכול להתפרס על פני קבצים רבים.
פלט:
120.00 via DHL
Shop.Billing.Invoice
namespace Shop.Billing { } הוא קיצור של namespace Shop { namespace Billing { } }. קוד בתוך Shop יכול להתייחס ל-Billing.Invoice בלי הקידומת Shop., כי שמות מחופשים קודם ב-namespaces העוטפים.
מוסכמות: התחילו בחברה או במוצר (Contoso.Payments), אחר כך התכונה או השכבה, והתאימו למבנה התיקיות (Contoso.Payments/Refunds/RefundService.cs מכיל את Contoso.Payments.Refunds). לעולם אל תקראו למחלקה באותו שם כמו ה-namespace שלה (Shop.Shop), כי זה מכריח ציון שם מלא ומסורבל בכל מקום.
כינויים (aliases)
using Name = Namespace.Type; נותן לטיפוס (או ל-namespace) שם מקומי. יש שני מצבים שבהם זה שימושי: קיצור של טיפוס גנרי ארוך, ופתרון התנגשות.
פלט:
latte: 3.60
order 17
כינויים חלים רק על הקובץ שבו הם מוצהרים (אלא אם הם global, בהמשך). ב-C# 12 ואילך כינוי יכול לתת שם לכל טיפוס, כולל tuples ומערכים: using Point = (int X, int Y);.
הפניות עמומות (CS0104)
כששני namespaces מיובאים מכילים טיפוס באותו שם, שימוש בשם הקצר הוא שגיאה. המקרה הקלאסי הוא Timer, שקיים ב-System.Threading, ב-System.Timers וגם (באפליקציות דסקטופ) ב-System.Windows.Forms:
using System.Threading;
using System.Timers;
var t = new Timer(1000);
// error CS0104: 'Timer' is an ambiguous reference between
// 'System.Timers.Timer' and 'System.Threading.Timer'
שלושה תיקונים, מהמקומי ביותר לפחות מקומי: לכתוב את השם המלא (new System.Timers.Timer(1000)), להוסיף כינוי שאומר לאיזה מהם הכוונה, או להסיר את ההנחיה שלא צריך.
פלט:
250
System.Timers.Timer
System.Threading.Timer
אם ה-namespace שלכם מגדיר טיפוס בשם System או Console, הקידומת global:: מכריחה חיפוש מהשורש: global::System.Console.WriteLine("hi");. קוד שנוצר אוטומטית משתמש בה בכל מקום מהסיבה הזו.
using static
using static מייבא את האיברים הסטטיים של טיפוס אחד, כך שאפשר לקרוא להם בלי שם הטיפוס. זה מתאים לקוד שקורא שוב ושוב לאותה מחלקה, כמו Math:
פלט:
Area: 28.27
Hypotenuse: 5
42
using static עובד גם עם enums (using static System.DayOfWeek; מאפשר לכתוב Monday) ועם מחלקות סטטיות משלכם. השתמשו בו כשהשמות הקצרים נשארים חד-משמעיים; WriteLine לבדו בקובץ גדול עלול להשאיר את הקוראים תוהים על איזה כותב מדובר.
global using ו-namespaces ברמת קובץ (C# 10)
שתי תכונות של C# 10 מקצרות את הקוד החוזר בראש הקבצים. global using חל על כל קובץ בפרויקט, ובדרך כלל שומרים אותו בקובץ אחד כמו GlobalUsings.cs:
// GlobalUsings.cs, C# 10 and later
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using Money = System.Decimal;
פרויקטי SDK על .NET 6 ואילך יכולים ליצור אותם בשבילכם. עם <ImplicitUsings>enable</ImplicitUsings> בקובץ ה-.csproj (ברירת המחדל בתבניות חדשות), פרויקט console מקבל את System, System.Collections.Generic, System.IO, System.Linq, System.Net.Http, System.Threading ו-System.Threading.Tasks; פרויקטי web מקבלים גם namespaces של ASP.NET Core. לכן קבצי Program.cs מודרניים מתחילים ישר בקוד.
namespace ברמת קובץ (file-scoped) מצהיר על ה-namespace לכל הקובץ עם נקודה-פסיק במקום בלוק, וחוסך רמת הזחה אחת:
// C# 10 and later
namespace Shop.Billing;
public class Invoice
{
public decimal Amount { get; set; }
}
בקובץ יכול להיות רק namespace אחד ברמת קובץ, ואי אפשר לערבב אותו עם namespaces בצורת בלוק באותו קובץ. פרויקטים ישנים, סקריפטים של Unity וקוד שמכוון ל-.NET Framework משתמשים בצורת הבלוק שהוצגה קודם.
איפה שמים הנחיות using, והוראת using
הנחיות using חייבות להופיע לפני כל הצהרה על namespace או על טיפוס בקובץ (ו-global usings לפני כל השאר). אפשר גם לשים אותן בתוך בלוק namespace, ואז הן חלות רק שם; מדריכי הסגנון חלוקים בעניין, ורוב הקוד ב-.NET שומר אותן בראש הקובץ.
למילת המפתח using יש תפקיד שני, שאין לו קשר, בתוך מתודות: הוראת using using (var file = File.OpenRead(path)) { ... } קוראת ל-Dispose על האובייקט כשהבלוק מסתיים, וכך סוגרת קבצים וחיבורים. היא חולקת את מילת המפתח ותו לא. ראו את הדף על הוראת using.
שאלות נפוצות
מה עושה using ב-C#?
בראש קובץ, using System.Text; היא הנחיה שמייבאת namespace, כך שאפשר לכתוב StringBuilder במקום System.Text.StringBuilder. היא לא מוסיפה קוד ולא טוענת כלום; היא רק מקצרת שמות. בתוך מתודה, using (var f = ...) היא תכונה אחרת, הוראת using, שמשחררת (dispose) אובייקט בסוף בלוק.
מה זה namespace ב-C#?
namespace הוא קבוצה של טיפוסים עם שם, שמונעת התנגשויות שמות ומארגנת קוד, למשל System.Collections.Generic או MyShop.Billing. שתי מחלקות יכולות לחלוק שם אם הן ב-namespaces שונים. לפי המוסכמה namespaces עוקבים אחרי מבנה התיקיות ומתחילים בשם החברה או המוצר.
מה זה using static ב-C#?
using static System.Math; מייבא את האיברים הסטטיים של טיפוס אחד, כך שאפשר לקרוא ל-Sqrt(16) ולקרוא את PI בלי הקידומת Math.. זה עובד עם כל מחלקה, כולל מחלקות שלכם ו-enums. השתמשו בו כשהשמות הקצרים נשארים ברורים, למשל בקוד עתיר חישובים מתמטיים.
איך מתקנים את השגיאה "is an ambiguous reference" ב-C#?
שגיאה CS0104 אומרת ששני namespaces מיובאים מכילים טיפוס באותו שם, למשל Timer ב-System.Threading וב-System.Timers. כתבו את השם המלא (System.Timers.Timer), הסירו אחת מהנחיות ה-using, או הוסיפו כינוי: using Timer = System.Timers.Timer;.
מה זה global using ב-C#?
global using System.Text; (C# 10) מייבא namespace לכל הקבצים בפרויקט, לא רק לקובץ שבו הוא מופיע. פרויקטי SDK של .NET 6 ואילך יוצרים אוטומטית סט של global usings כשמוגדר <ImplicitUsings>enable</ImplicitUsings>, ולכן בקבצי Program.cs חדשים אין שורות using.