8-Bob: Metodlar
Instance konstruktorlar, tur konstruktorlari, operator overloading, konversiya operatorlari, kengaytma metodlari va partial metodlar
Ushbu bobda turlarning turli xil metodlari bilan tanishasiz. Konstruktorlar obyektlar va turlarni to'g'ri initsializatsiya qilish uchun ishlatiladi. Operator overload metodlari sizga o'z turlaringiz uchun operatorlar xatti-harakatini aniqlash imkonini beradi. Konversiya operatorlari bir turni boshqasiga o'girish usullarini belgilaydi. Kengaytma metodlari mavjud turlarga yangi funksionallik qo'shish imkonini beradi. Va nihoyat, partial metodlar kodni generatsiya qilish stsenariylarida qanday foydalanishni ko'rasiz.
Instance Konstruktorlar va Klasslar (Reference Turlar)
Konstruktorlar — turning instansiya holatini to'g'ri boshlang'ich qiymatga o'rnatuvchi maxsus metodlardir. Konstruktor metodlari metadata jadvallarida .ctor (konstruktor) deb nomlanadi. Yangi tur instansiyasi yaratilganda, uning uchun xotira ajratiladi, obyektning overhead maydonlari (tur obyekt ko'rsatkichi va sinxronizatsiya blok indeksi) initsializatsiya qilinadi, turning instance konstruktori instansiyaning boshlang'ich holatini o'rnatish uchun chaqiriladi.
Reference tur (klass) obyekti yaratilganda, uning instance ma'lumot maydonlari uchun ajratilgan xotira doimo 0 ga o'rnatiladi (nollanadi), keyin tur konstruktori chaqiriladi. Shuning uchun, agar siz konstruktor yozmagan bo'lsangiz, har qanday maydon o'zining standart qiymatiga ega bo'ladi (0, null, false va h.k.).
Klass hech qanday konstruktor aniqlamasligi mumkin. Bunday holatda C# kompilyatori avtomatik ravishda standart parametrsiz konstruktor aniqlaydi. Standart konstruktor tanasi faqat bazaviy klassning (System.Object) parametrsiz konstruktorini chaqiradi.
public class SomeType {
// C# kompilyatori bu standart konstruktorni avtomatik yaratadi:
// public SomeType() : base() { }
}
Agar klass kamida bitta konstruktor aniq aniqlansa, C# kompilyatori avtomatik standart konstruktor yaratmaydi:
public class SomeType {
// Faqat Int32 parametrli konstruktor mavjud
public SomeType(Int32 x) { ... }
}
// Quyidagi kod kompilyatsiya XATOSI beradi:
// SomeType o = new SomeType(); // Parametrsiz konstruktor yo'q!
Agar siz kamida bitta konstruktor aniqlagan bo'lsangiz va standart parametrsiz konstruktorni ham saqlamoqchi bo'lsangiz, uni aniq ravishda aniqlashingiz kerak. Abstrakt klasslar uchun kompilyator yaratadigan standart konstruktor protected bo'ladi, boshqa klasslar uchun esa public.
Bitta klass bir nechta konstruktorga ega bo'lishi mumkin. Har bir konstruktor turli parametrlar to'plamiga (imzoga) ega bo'lishi kerak:
public sealed class SomeType {
private Int32 m_x;
private String m_s;
private Double m_d;
private Byte m_b;
// Parametrsiz konstruktor — barcha maydonlar standart qiymatlarga ega
public SomeType() {
m_x = 5;
m_s = "Hi there";
m_d = 3.14159;
m_b = 0xff;
}
// Bitta parametrli konstruktor
public SomeType(Int32 x) : this() {
m_x = x;
}
// Ikki parametrli konstruktor
public SomeType(Int32 x, String s) : this() {
m_x = x;
m_s = s;
}
}
Yuqoridagi misolda bitta va ikki parametrli konstruktorlar this() orqali parametrsiz konstruktorni chaqirmoqda. Bu texnika juda foydali, chunki barcha umumiy initsializatsiya kodi bitta joyda (parametrsiz konstruktorda) saqlanadi va takrorlanmaydi.
Ishlash Samaradorligi va Maydon Initsializatsiyasi
C# kompilyatori sizga instansiya maydonlarini e'lon qilingan joyda inline tarzda initsializatsiya qilish imkonini beradi:
public sealed class SomeType {
private Int32 m_x = 5;
private String m_s = "Hi there";
private Double m_d = 3.14159;
private Byte m_b = 0xff;
// Kompiliyator ichki tarzda quyidagi kodni yaratadi:
// 1. Barcha inline maydon qiymatlarini konstruktorga qo'shadi
// 2. Bazaviy klass konstruktorini chaqiradi
}
Bu qulay sintaksis. Lekin shuni bilishingiz kerakki, C# kompilyatori inline initsializatsiya kodini har bir konstruktor tanasiga qo'shadi. Agar sizda 3 ta konstruktor va 5 ta inline initsializatsiya qilingan maydon bo'lsa, kompilyator bu 5 ta initsializatsiyani har uchta konstruktorga ham qo'shadi.
Inline maydon initsializatsiyasi ko'p konstruktorli klasslarda IL kod hajmini oshirishi mumkin. Buning o'rniga, barcha umumiy initsializatsiya kodini bitta konstruktorda joylashtiring va boshqa konstruktorlardan this() orqali shu konstruktorni chaqiring. Bu yondashuv kod hajmini kamaytiradi.
// TAVSIYA ETILMAYDIGAN usul (inline initsializatsiya):
public sealed class SomeType {
private Int32 m_x = 5; // Har bir konstruktorda takrorlanadi
private String m_s = "Hi"; // Har bir konstruktorda takrorlanadi
public SomeType() { }
public SomeType(Int32 x) { m_x = x; }
public SomeType(Int32 x, String s) { m_x = x; m_s = s; }
}
// TAVSIYA ETILADIGAN usul:
public sealed class SomeType {
private Int32 m_x;
private String m_s;
// Bitta asosiy konstruktor
public SomeType() {
m_x = 5;
m_s = "Hi";
}
// Boshqa konstruktorlar asosiyni chaqiradi
public SomeType(Int32 x) : this() {
m_x = x;
}
public SomeType(Int32 x, String s) : this() {
m_x = x;
m_s = s;
}
}
C# kompilyatori bazaviy klass konstruktor chaqiruvini (base()) faqat o'sha konstruktor this() yordamida boshqa konstruktorni chaqirmagan taqdirda yaratadi. Bu mantiqiy, chunki oxir-oqibat zanjir bo'ylab bitta konstruktor bazaviy klassni chaqiradi.
Instance konstruktorlar hech qachon virtual, abstract yoki sealed bo'lmasligi kerakligini unutmang. Agar klass sealed (muhrlangan) bo'lmasa, konstruktor protected sifatida aniqlanishi mumkin, shunda faqat hosila klasslar uning konstruktorini chaqirishi mumkin.
Instance Konstruktorlar va Strukturalar (Value Turlar)
Value turlar (strukturalar) uchun konstruktorlar reference turlardan farq qiladi. CLR har qanday value tur instansiyasini yaratishga ruxsat beradi va buni oldini olishning iloji yo'q. Shu sababli value turlar aslida konstruktor aniqlamasligi ham mumkin — CLR shunchaki barcha baytlarni 0 ga o'rnatadi.
C# kompilyatori value turlar (structurelar) uchun standart parametrsiz konstruktor aniqlashga ruxsat bermaydi. Bu cheklov C# ga xos — CLR o'zi bu cheklovni qo'ymaydi. Sababi shundaki, qiymat turi massivga joylashtirilganda yoki klassning maydoni bo'lganda, CLR barcha baytlarni 0 ga o'rnatadi va hech qanday konstruktorni chaqirmaydi. Agar parametrsiz konstruktor bo'lganida, CLR uni har bir element uchun chaqirishi kerak bo'lar edi, bu esa ishlashni jiddiy pasaytiradi.
internal struct Point {
public Int32 m_x, m_y;
// C# da bu XATO beradi:
// public Point() { m_x = m_y = 0; } // Ruxsat berilmaydi!
// Lekin parametrli konstruktor yaratish mumkin:
public Point(Int32 x, Int32 y) {
m_x = x;
m_y = y;
}
}
Value tur konstruktori yozganingizda, C# kompilyatori barcha maydonlarni konstruktor ichida initsializatsiya qilishingizni talab qiladi. Agar siz faqat m_x ni initsializatsiya qilsangiz, m_y ni qoldirganingiz uchun kompilyatsiya xatosi olasiz:
internal struct Point {
public Int32 m_x, m_y;
public Point(Int32 x) {
m_x = x;
// XATO: 'm_y' maydoni tayinlanmagan!
// Tuzatish: m_y = 0; qo'shing
}
}
Value turlar uchun kompilyator avtomatik bazaviy klass konstruktor chaqiruvini yaratmaydi. Aslida, value turlar boshqa turlardan voris bo'la olmaydi (faqat System.ValueType dan avtomatik voris bo'ladi).
C# value turlar uchun inline maydon initsializatsiya sintaksisiga ruxsat bermaydi (instance maydonlar uchun). Lekin statik maydonlar uchun inline initsializatsiya qo'llanilishi mumkin:
internal struct SomeValType {
// Bu XATO — instance maydonni inline initsializatsiya mumkin emas:
// private Int32 m_x = 5;
// Bu TO'G'RI — statik maydonni inline initsializatsiya mumkin:
private static Int32 s_y = 10;
}
Value turning yangi instansiyasini ikki usulda yaratishingiz mumkin:
// 1-usul: new operatori bilan (konstruktor chaqiriladi)
Point p1 = new Point(10, 20);
// 2-usul: Standart qiymatlar bilan (barcha maydonlar 0)
Point p2;
p2.m_x = 10;
p2.m_y = 20;
Tur Konstruktorlari (Statik Konstruktorlar)
Instance konstruktorlardan tashqari, CLR tur konstruktorlarini ham qo'llab-quvvatlaydi. Tur konstruktorlari statik konstruktorlar, klass konstruktorlar yoki tur initsializatorlar deb ham ataladi. Tur konstruktorlari interfeyslar uchun ham qo'llanilishi mumkin (C# buni qo'llab-quvvatlamasa ham, CLR o'zi quvvatlaydi), reference turlar va value turlar uchun ishlatilishi mumkin.
Instance konstruktorlarning maqsadi turning instance holatini o'rnatishdir. Xuddi shunday, tur konstruktorining maqsadi turning statik holatini boshlang'ich qiymatlarga o'rnatishdir.
Odatiy holda, turlar tur konstruktor aniqlamaydi. Agar tur tur konstruktor aniqlasa, unda faqat bitta bo'lishi mumkin va u hech qachon parametr qabul qilmaydi:
internal sealed class SomeType {
// Statik maydon
private static Int32 s_x;
// Tur konstruktori — parametrsiz va statik
static SomeType() {
s_x = 10;
Console.WriteLine("Tur konstruktori chaqirildi!");
}
}
Tur konstruktorlari uchun hech qanday kirish modifikatori (public, private, va h.k.) ko'rsatilishi mumkin emas. Aslida, tur konstruktorlari doimo private bo'ladi — C# kompilyatori uni avtomatik private qiladi. Bu shuni anglatadiki, dasturchi kodi hech qachon tur konstruktorini to'g'ridan-to'g'ri chaqira olmaydi — faqat CLR buni amalga oshiradi.
Tur konstruktori aniqlash instance konstruktorni aniqlashga o'xshash. Asosiy farqlar:
statickalit so'zi ishlatiladi- Hech qanday kirish modifikatori bo'lmaydi (doimo private)
- Hech qanday parametr qabul qilmaydi
- Tana ichida faqat statik maydonlarga murojaat qilish mumkin
Tur Konstruktorining Chaqirilish Vaqti
CLR tur konstruktorlarini chaqirish vaqti bo'yicha quyidagi qoidalarga amal qiladi. Birinchi usul — "precise" (aniq) semantika — tur konstruktori turning birinchi instansiyasi yaratilganda yoki turning birinchi statik maydoni/metodiga murojaat qilinganda chaqiriladi. Ikkinchi usul — "before-field-init" — CLR istagan vaqtda tur konstruktorini chaqirishi mumkin, lekin turning statik maydoniga birinchi murojaat qilinishidan oldin.
internal sealed class SomeType {
// beforefieldinit semantikasi — inline initsializatsiya
private static Int32 s_x = 5;
}
internal sealed class AnotherType {
// "precise" semantika — aniq tur konstruktori
private static Int32 s_x;
static AnotherType() {
s_x = 5;
}
}
C# kompilyatori tur konstruktori aniq aniqlangan turni beforefieldinit metadata bayrog'i bilan belgilamaydi. Agar faqat inline initsializatsiya bo'lsa va aniq tur konstruktori bo'lmasa, kompilyator beforefieldinit ni qo'yadi. Bu CLR ga tur konstruktorini istalgan qulay vaqtda chaqirish erkinligini beradi, bu esa ishlashni yaxshilaydi.
Inline initsializatsiya qilingan statik maydon uchun kompilyator avtomatik tur konstruktor yaratadi. Masalan:
// Siz yozgan kod:
internal sealed class SomeType {
private static Int32 s_x = 5;
}
// Kompilyator aslida quyidagini yaratadi:
internal sealed class SomeType {
private static Int32 s_x;
static SomeType() { s_x = 5; }
}
ILDasm.exe yordamida kompilyator yaratgan IL kodni tekshirish mumkin. Tur konstruktori metodlari doimo .cctor (class constructor) deb nomlanadi:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldc.i4.5
IL_0001: stsfld int32 SomeType::s_x
IL_0006: ret
} // end of method SomeType::.cctor
Agar siz ham inline initsializatsiya, ham aniq tur konstruktori aniqlasangiz, kompilyator bitta tur konstruktorni yaratadi va inline initsializatsiyani birinchi, keyin esa sizning aniq kodingizni joylashtiradi:
internal sealed class SomeType {
private static Int32 s_x = 5; // Avval bu bajariladi
static SomeType() {
s_x = 10; // Keyin bu bajariladi
}
// Natija: s_x = 10
}
Tur Konstruktorlari va Thread Xavfsizligi
Metod JIT-kompilyatsiya qilingandan so'ng, oqim (thread) ishga tushadi va oxir-oqibat tur konstruktorini chaqiradigan kodga yetib keladi. Bir nechta oqimlar bir vaqtning o'zida aynan shu metodni bajarayotgan bo'lishi mumkin. CLR turning konstruktori har bir AppDomain da faqat bir marta bajarilishini kafolatlaydi.
Buni ta'minlash uchun tur konstruktori chaqirilganda, chaqiruvchi oqim o'zaro istisno thread sinxronizatsiya qulfini (mutually exclusive thread synchronization lock) oladi. Shunday qilib, agar bir nechta oqim bir vaqtning o'zida turning statik konstruktorini chaqirishga urinsa, faqat bitta oqim qulfni oladi, qolganlari esa bloklanadi. Birinchi oqim konstruktordan chiqqanidan so'ng, kutayotgan oqimlar uyg'onadi va konstruktor kodi allaqachon bajarilganini ko'radi. Ular kodni qayta bajarmaydi — shunchaki konstruktor metodidan qaytadi.
CLR tur konstruktorini har bir AppDomain da faqat bir marta bajarishi va thread-safe ekanini kafolatlaydi. Shu sababli, tur konstruktori turga zarur bo'lgan singleton obyektlarni initsializatsiya qilish uchun ajoyib joy hisoblanadi.
Bitta oqim ichida, agar ikki tur konstruktori bir-biriga havola qilsa, muammo yuzaga kelishi mumkin. Masalan, ClassA tur konstruktori ClassB ga havola qilsa va ClassB tur konstruktori ClassA ga havola qilsa — CLR hali ham har birining kodi faqat bir marta bajarilishini kafolatlaydi. Lekin ClassA tur konstruktori tugaganini ClassB tur konstruktori bajarilishidan oldin kafolatlab bo'lmaydi. Shuning uchun tur konstruktorlari muayyan tartibda chaqirilishini talab qiladigan kod yozishdan qochishingiz kerak.
Agar tur konstruktori ishlov berilmagan istisno (exception) chiqarsa, CLR turni foydalanib bo'lmaydigan deb hisoblaydi. Turning istalgan maydoni yoki metodiga murojaat qilishga urinish System.TypeInitializationException tashlashga olib keladi.
Tur konstruktorlari asosiy maqsad — turning statik maydonlarini initsializatsiya qilish. Bazaviy tur konstruktorini chaqirishning hojati yo'q, chunki bazaviy turning statik maydonlari meros qilib olinmaydi yoki taqsimlanmaydi.
Dasturchilar ba'zan tur tushirilganda (unload) biror kodni bajarishning yo'lini so'rashadi. Turlar faqat AppDomain tushirilganda tushiriladi. AppDomain tushirilganda turni belgilaydigan obyekt yetib bo'lmaydi va garbage collector uning xotirasini qaytarib oladi. Ko'plab dasturchilar statik Finalize metodi qo'shsa bo'ladi deb o'ylashadi, lekin CLR statik Finalize metodlarni qo'llab-quvvatlamaydi. AppDomain tushirilganda biror kod bajarishni xohlasangiz, System.AppDomain turining DomainUnload hodisasiga callback metod ro'yxatdan o'tkazing.
Ba'zi tillar (Java kabi) turga murojaat qilish turning tur konstruktori va uning barcha bazaviy turlarining tur konstruktorlarini ham chaqirilishini kutadi. Bundan tashqari, turlar amalga oshirgan interfeyslar ham o'z tur konstruktorlari chaqirilishini kutishi mumkin. CLR bu xatti-harakatni taklif qilmaydi. Lekin CLR kompilyatorlarga bu imkoniyatni System.Runtime.CompilerServices.RuntimeHelpers turining RunClassConstructor metodi orqali beradi.
Operator Overload Metodlari
Ba'zi dasturlash tillari turga operatorlar turning instansiyalarini qanday boshqarishini aniqlash imkonini beradi. Masalan, ko'plab turlar (System.String, System.Decimal va System.DateTime kabi) tenglik (==) va tengsizlik (!=) operatorlarini qayta yuklaydi.
CLR aslida operatorlar haqida hech narsa bilmaydi, chunki u operatorning nima ekanligini ham bilmaydi. Sizning dasturlash tilingiz operatorlarning har bir belgisi nimani anglatishini va bu maxsus belgilar paydo bo'lganda qanday kod yaratilishini aniqlaydi.
Masalan, C# da + belgisini primitiv sonlarga qo'llaganda, kompilyator ikki sonni qo'shadigan kod yaratadi. + belgisini String obyektlariga qo'llaganda esa kompilyator ikki satrni birlashtiruvchi kod yaratadi. Tengsizlik uchun C# != belgisini, Microsoft Visual Basic esa <> belgisini ishlatadi. ^ belgisi C# da XOR, Visual Basic da esa darajaga ko'tarish (exponent) operatori sifatida ishlatiladi.
CLR operatorlar haqida hech narsa bilmasa ham, u tillar operator overloadlarni qanday ko'rsatishi kerakligini belgilaydi, shunda ular boshqa tilda yozilgan kod tomonidan osonlik bilan ishlatilishi mumkin. CLR nuqtai nazaridan, operator overloadlar shunchaki metodlardir.
Manba kodingizni kompilyatsiya qilganingizda, kompilyator operatorning xatti-harakatini aniqlaydigan metod yaratadi. CLR spetsifikatsiyasi operator overload metodlarning public va static bo'lishini talab qiladi. Bundan tashqari, C# (va ko'plab boshqa tillar) operator metodining kamida bitta parametri metod aniqlangan tur bilan bir xil bo'lishini talab qiladi.
C# klass ta'rifida aniqlangan operator overload metodiga misol:
public sealed class Complex {
public static Complex operator+(Complex c1, Complex c2) { ... }
}
Kompilyator op_Addition deb nomlangan metod uchun metadata usul ta'rifi yaratadi; usul ta'rif yozuvi specialname bayrog'iga ham ega. Til kompilyatorlari (C# kompilyatori jumladan) manba kodida + operatorini ko'rganda, operand turlaridan birida specialname bayrog'iga ega va parametrlari operand turlari bilan mos keladigan op_Addition metodi aniqlanganmi-yo'qmi tekshiradi. Agar bunday metod mavjud bo'lsa, kompilyator bu metodni chaqiruvchi kod yaratadi.
C# Operatorlar va CLS Metod Nomlari
Quyidagi jadvallar C# tomonidan qayta yuklanishi mumkin bo'lgan unar va binar operatorlarni, ularning belgilarini va kompilyator yaratadigan CLS metod nomlarini ko'rsatadi.
8-1 Jadval: C# Unar Operatorlar
| C# Operator Belgisi | Maxsus Metod Nomi | CLS-mos Metod Nomi |
|---|---|---|
+ | op_UnaryPlus | Plus |
- | op_UnaryNegation | Negate |
! | op_LogicalNot | Not |
~ | op_OnesComplement | OnesComplement |
++ | op_Increment | Increment |
-- | op_Decrement | Decrement |
| (yo'q) | op_True | IsTrue { get; } |
| (yo'q) | op_False | IsFalse { get; } |
8-2 Jadval: C# Binar Operatorlar
| C# Operator Belgisi | Maxsus Metod Nomi | CLS-mos Metod Nomi |
|---|---|---|
+ | op_Addition | Add |
- | op_Subtraction | Subtract |
* | op_Multiply | Multiply |
/ | op_Division | Divide |
% | op_Modulus | Mod |
& | op_BitwiseAnd | BitwiseAnd |
| | op_BitwiseOr | BitwiseOr |
^ | op_ExclusiveOr | Xor |
<< | op_LeftShift | LeftShift |
>> | op_RightShift | RightShift |
== | op_Equality | Equals |
!= | op_Inequality | Equals |
< | op_LessThan | Compare |
> | op_GreaterThan | Compare |
<= | op_LessThanOrEqual | Compare |
>= | op_GreaterThanOrEqual | Compare |
Agar siz asosiy raqamli turlarni (Int32, Int64, UInt32 va h.k.) tekshirsangiz, ularning hech qanday operator overload metodi aniqlamaganini ko'rasiz. Buning sababi shundaki, kompilyatorlar bu primitiv turlar ustidagi amallar uchun maxsus qidiradi va instansiyalarni bevosita manipulyatsiya qiluvchi IL ko'rsatmalarini yaratadi. Agar turlar metodlar taklif qilganida va kompilyatorlar bu metodlarni chaqirish uchun kod yaratganida, har bir metod chaqiruvi bilan ish vaqtida ishlash xarajati bog'liq bo'lar edi.
Operatorlar va Dasturlash Tillari O'zaro Mosligi
Operator overloading juda foydali vosita bo'lib, dasturchilarga o'z fikrlarini ixcham kod bilan ifodalash imkonini beradi. Lekin barcha dasturlash tillari operator overloadingni qo'llab-quvvatlamaydi.
Operator overloadingni qo'llab-quvvatlamaydigan tildan foydalanganingizda, til + operatorini qanday talqin qilishni bilmaydi (agar tur primitiv bo'lmasa) va kompilyator xatolik beradi. Bunday tillarda kerakli op_* metodini (masalan, op_Addition) to'g'ridan-to'g'ri chaqirishga ruxsat berilishi kerak.
Agar siz operator overloadingni qo'llab-quvvatlamaydigan tildan foydalansangiz va turda op_Addition metodi mavjud bo'lsa, uni + operatori orqali chaqirib bo'lmaydi. Sababi C# kompilyatori + operatorini ko'rganda, specialname metadata bayrog'iga ega op_Addition metodini qidiradi. Agar op_Addition metodi operator overloadingni qo'llab-quvvatlamaydigan til tomonidan yaratilgan bo'lsa, metod specialname bayrog'iga ega bo'lmaydi va C# kompilyatori xatolik beradi.
Microsoft'ning Operator Metod Nomlari Haqida Jeff'ning Fikri
Operator overload metodlarini aniqlaydigan tur uchun, Microsoft turda operator overload metodlarini ichki chaqiruvchi do'stona (friendly) public static metodlarni ham aniqlashni tavsiya qiladi. Masalan, op_Addition metodini overload qiladigan turda Add deb nomlangan public friendly metod ham aniqlash kerak.
public sealed class Complex {
public static Complex operator+(Complex c1, Complex c2) { ... }
public static Complex Add(Complex c1, Complex c2) { return(c1 + c2); }
}
8-1 va 8-2 jadvallarning uchinchi ustunlarida har bir operator uchun tavsiya etilgan do'stona metod nomlari keltirilgan. Shunday qilib, istalgan dasturlash tilida yozilgan kod do'stona operator metodlarini, masalan Add, chaqirishi mumkin.
Operatorlarni overload qiluvchi va do'stona metod nomlarini Microsoft dizayn ko'rsatmalariga mos ishlatadigan tur misolini ko'rish uchun FCL dagi System.Decimal klassini ko'ring.
Konversiya Operator Metodlari
Ba'zan turning obyektini boshqa turga aylantirish zaruriyati paydo bo'ladi. Masalan, Byte ni Int32 ga aylantirish. Agar manba turi va maqsad turi ikkalasi ham kompilyatorning primitiv turlari bo'lsa, kompilyator obyektni aylantirish uchun zarur kodni yaratishni biladi.
Agar manba turi yoki maqsad turi primitiv bo'lmasa, kompilyator CLR ga konversiyani (cast) bajarishga buyuradigan kodni yaratadi. Bu holatda CLR shunchaki manba obyektning turi maqsad tur bilan bir xil yoki maqsad turdan hosila ekanligini tekshiradi.
Lekin ba'zan turni butunlay boshqa turga aylantirish tabiiy bo'ladi. Tasavvur qiling, FCL da Rational (kasr) ma'lumot turi bo'lsa. Int32 yoki Single obyektni Rational ga aylantirish va Rational ni Int32 yoki Single ga aylantirish qulaydir.
Bu konversiyalarni amalga oshirish uchun Rational turi quyidagilarni aniqlashi kerak:
- Public konstruktorlar — konversiya qilinadigan turni yagona parametr sifatida qabul qiladi
- Public instansiya
ToXxxmetodlari — parametrsiz bo'lib, aniqlanayotgan turning instansiyasiniXxxturiga aylantiradi
public sealed class Rational {
// Int32 dan Rational ga konstruktor
public Rational(Int32 num) { ... }
// Single dan Rational ga konstruktor
public Rational(Single num) { ... }
// Rational dan Int32 ga konversiya metodi
public Int32 ToInt32() { ... }
// Rational dan Single ga konversiya metodi
public Single ToSingle() { ... }
}
Bu konstruktorlar va metodlarni chaqirish orqali, istalgan dasturlash tilini ishlatadigan dasturchi Int32 yoki Single dan Rational ga, va Rational dan Int32 yoki Single ga aylantirishi mumkin.
implicit va explicit Konversiya Operatorlari
Ba'zi dasturlash tillari (C# jumladan) konversiya operator overloadingni ham qo'llab-quvvatlaydi. Konversiya operatorlari — bir turdan boshqasiga obyektni aylantiradigan metodlar. Konversiya operator metodini maxsus sintaksis yordamida aniqlaysiz. CLR spetsifikatsiyasi konversiya overload metodlarining public va static bo'lishini talab qiladi. Bundan tashqari, C# parametr yoki qaytarish turlaridan biri konversiya metodi aniqlangan tur bilan bir xil bo'lishini talab qiladi.
public sealed class Rational {
// Int32 dan Rational yaratish (konstruktor)
public Rational(Int32 num) { ... }
// Single dan Rational yaratish (konstruktor)
public Rational(Single num) { ... }
// Rational dan Int32 ga konversiya
public Int32 ToInt32() { ... }
// Rational dan Single ga konversiya
public Single ToSingle() { ... }
// Int32 dan Rational ga YASHIRIN konversiya operatori
public static implicit operator Rational(Int32 num) {
return new Rational(num);
}
// Single dan Rational ga YASHIRIN konversiya operatori
public static implicit operator Rational(Single num) {
return new Rational(num);
}
// Rational dan Int32 ga ANIQ konversiya operatori
public static explicit operator Int32(Rational r) {
return r.ToInt32();
}
// Rational dan Single ga ANIQ konversiya operatori
public static explicit operator Single(Rational r) {
return r.ToSingle();
}
}
Konversiya operatorlarini aniqlaganimizdan so'ng, C# da quyidagi kodni yozish mumkin:
public sealed class Program {
public static void Main() {
Rational r1 = 5; // Int32 dan Rational ga yashirin konversiya
Rational r2 = 2.5F; // Single dan Rational ga yashirin konversiya
Int32 x = (Int32) r1; // Rational dan Int32 ga aniq konversiya
Single s = (Single) r2; // Rational dan Single ga aniq konversiya
}
}
C# kompilyatori koddagi castlarni (tur konversiyalarini) aniqlaydi va ichki tarzda Rational turi tomonidan aniqlangan konversiya operator metodlarini chaqiradigan IL kodni yaratadi. Metadatani tekshirsangiz, to'rtta konversiya operator metodi quyidagicha ko'rinadi:
public static Rational op_Implicit(Int32 num)
public static Rational op_Implicit(Single num)
public static Int32 op_Explicit(Rational r)
public static Single op_Explicit(Rational r)
Ko'rib turganingizdek, bir turdan boshqasiga aylantiruvchi metodlar doimo op_Implicit yoki op_Explicit deb nomlanadi:
implicit— konversiya paytida aniqlik (precision) yoki kattalik (magnitude) yo'qolmasa, yashirin konversiya operatorini aniqlang. Masalan,Int32danRationalga aylantirishda ma'lumot yo'qolmaydi.explicit— konversiya paytida aniqlik yoki kattalik yo'qolishi mumkin bo'lsa, aniq konversiya operatorini aniqlang. Masalan,RationaldanInt32ga aylantirishda kasr qism yo'qoladi. Agar aniq konversiya muvaffaqiyatsiz bo'lsa, metodOverflowExceptionyokiInvalidOperationExceptiontashlashi kerak.
Ikki op_Explicit metodi bir xil parametrga (Rational) ega, lekin qaytarish turlari boshqacha (Int32 va Single). Bu faqat qaytarish turi bo'yicha farqlanadigan ikki metod namunasidir. CLR turda faqat qaytarish turi bo'yicha farqlanadigan bir nechta metodlarni aniqlash imkoniyatini to'liq qo'llab-quvvatlaydi. Lekin ko'pchilik tillar (C++, C#, Visual Basic, Java jumladan) bu imkoniyatni ochib bermaydi. C# bu imkoniyatni to'g'ridan-to'g'ri dasturchiga ko'rsatmasa ham, konversiya operator metodlarini aniqlaganda kompilyator ichki tarzda bundan foydalanadi.
C# konversiya operatorlarini to'liq qo'llab-quvvatlaydi. Kompilyator siz obyekt bilan ishlayotganingizda boshqa tur kutilayotganini aniqlaganda, yashirin konversiya operator metodini qidiradi. Agar topilsa, kodni yaratadi. Agar manba kodida aniq cast ko'rsa, yashirin yoki aniq konversiya operator metodini qidiradi. Mos metod topilmasa, kompilyator xatolik beradi.
C# as yoki is operatorlarini ishlatganda aniq konversiya operatorlarini chaqirish uchun kod yaratmaydi — faqat cast ifodasi ((Type)value) konversiya operatorlarini chaqiradi.
Operator overload metodlari va konversiya operator metodlarini haqiqiy tushunish uchun, System.Decimal turini namuna sifatida o'rganishingizni qat'iy tavsiya qilaman. Decimal turli turlardan obyektlarni Decimal ga aylantirish uchun bir nechta konstruktorlarni, Decimal obyektni boshqa turga aylantirish uchun bir nechta ToXxx metodlarini, va bir nechta konversiya operatorlari va operator overload metodlarini aniqlaydi.
Kengaytma Metodlari (Extension Methods)
C# ning kengaytma metodlari xususiyatini eng yaxshi tushunish misollar orqali bo'ladi. 14-bobdagi "StringBuilder a'zolari" bo'limida StringBuilder klassi String klassiga qaraganda kamroq metodlar taklif qilishi aytib o'tiladi. Bu g'alati, chunki StringBuilder klassi satrni manipulyatsiya qilishning afzal usuli (chunki u mutable). Shuning uchun siz o'zingiz StringBuilder ustida ishlaydigan metodlarni yaratmoqchi bo'lishingiz mumkin. Masalan, o'zingizning IndexOf metodingiz:
public static class StringBuilderExtensions {
public static Int32 IndexOf(StringBuilder sb, Char value) {
for (Int32 index = 0; index < sb.Length; index++)
if (sb[index] == value) return index;
return -1;
}
}
Endi bu metodni quyidagicha ishlatishingiz mumkin:
StringBuilder sb = new StringBuilder("Hello. My name is Jeff.");
// Nuqtani undov belgisiga almashtirish va 1-gapda belgini topish
Int32 index = StringBuilderExtensions.IndexOf(sb.Replace('.', '!'), '!');
Bu kod ishlaydi, lekin dasturchi nuqtai nazaridan ideal emas. Uchta muammo bor:
StringBuilderExtensionsklassining mavjudligini bilish kerak- Kod
StringBuilderustidagi amallar tartibini aks ettirmaydi — dasturchigaReplaceni birinchi, keyinIndexOfni chaqirish kerakligi aniq emas StringBuilderExtensionsnomlari dasturchining asosiy operatsiyadan diqqatini chalg'itadi
Agar StringBuilder klassida o'zining IndexOf metodi bo'lganida, kodni quyidagicha yozish mumkin edi:
// Nuqtani undov belgisiga almashtirish va 1-gapda belgini topish
Int32 index = sb.Replace('.', '!').IndexOf('!');
C# ning kengaytma metodlari aynan shu muammoni hal qiladi. Statik metodni instansiya metodi sintaksisi yordamida chaqirishga imkon beradi. IndexOf ni kengaytma metodga aylantirish uchun birinchi argumentdan oldin this kalit so'zini qo'shamiz:
public static class StringBuilderExtensions {
public static Int32 IndexOf(this StringBuilder sb, Char value) {
for (Int32 index = 0; index < sb.Length; index++)
if (sb[index] == value) return index;
return -1;
}
}
Endi kompilyator quyidagi kodni ko'rganda:
Int32 index = sb.IndexOf('X');
Kompilyator avval StringBuilder klassi yoki uning bazaviy klasslarida yagona Char parametr qabul qiluvchi IndexOf instansiya metodi bormi-yo'qligini tekshiradi. Agar mavjud bo'lsa, kompilyator uni chaqirish uchun IL kodi yaratadi. Agar mos instansiya metodi topilmasa, kompilyator this kalit so'zi bilan belgilangan va birinchi parametr turi ifoda turi bilan mos keladigan IndexOf statik metodni barcha statik klasslarda qidiradi. Bu misolda, ifoda sb bo'lib, u StringBuilder turiga ega. Kompilyator StringBuilder va Char parametrli bizning statik IndexOf metodimizni topadi va uni chaqiruvchi IL kodi yaratadi.
Bu endi oldingi ikkala muammoni hal qiladi — IntelliSense oynasida StringBuilder obyektiga nuqta qo'yganda kengaytma metodlari ham ko'rsatiladi, va boshqa dasturchilar obyektlarning turli turlarida ishlaydigan o'z metodlaringizni tabiiy ravishda kashf etishlari mumkin.
Kengaytma Metodlari: Qoidalar va Ko'rsatmalar
Kengaytma metodlari haqida bilishingiz kerak bo'lgan qo'shimcha qoidalar va ko'rsatmalar:
- Faqat metodlar: C# faqat kengaytma metodlarni qo'llab-quvvatlaydi; kengaytma xossalar (property), kengaytma hodisalar (event) va boshqalarni qo'llab-quvvatlamaydi.
- Statik klassda: Kengaytma metodlari (
thisbilan belgilangan birinchi parametrli metodlar) generik bo'lmagan, statik klasslarda e'lon qilinishi kerak. Lekin klass nomiga hech qanday cheklov yo'q — ixtiyoriy nom berishingiz mumkin. Albatta, kamida bitta parametr bo'lishi kerak va faqat birinchi parametrthisbilan belgilanishi mumkin. - Fayl darajasida: C# kompilyatori faqat fayl darajasida aniqlangan statik klasslardagi kengaytma metodlarni qidiradi. Agar siz statik klassni boshqa klassning ichiga joylashtirsangiz, C# kompilyatori quyidagi xabarni beradi:
error CS1109: Extension method must be defined in a top-level static class; StringBuilderExtensions is a nested class. - using direktivasi kerak: Statik klasslarning ixtiyoriy nomga ega bo'lishi sababli, C# kompilyatoriga kengaytma metodlarni topish uchun barcha fayl darajasidagi statik klasslarni va ularning statik metodlarini skanerlash kerak bo'ladi. Ishlashni yaxshilash uchun C# kompilyatori kengaytma metodlarni "import" qilishni talab qiladi. Masalan,
Wintellectnomlar fazosidaStringBuilderExtensionsklassi aniqlangan bo'lsa, dasturchiusing Wintellect;direktivasini qo'shishi kerak. - Noaniqlik (ambiguity): Bir nechta statik klasslar bir xil kengaytma metodini aniqlashi mumkin. Agar kompilyator ikki yoki undan ko'p kengaytma metod mavjudligini aniqlasa, quyidagi xabarni beradi:
error CS0121: The call is ambiguous between the following methods or properties...Buni tuzatish uchun instansiya metod sintaksisi o'rniga statik metod sintaksisidan foydalanib, klassning to'liq nomini aniq ko'rsatish kerak. - Ehtiyotkorlik bilan ishlating: Bu xususiyatni tejamkorlik bilan ishlating, chunki barcha dasturchilar u bilan tanish emas. Masalan, kengaytma metod bilan turni kengaytirganingizda, siz aslida hosila turlarni ham kengaytirgan bo'lasiz. Shuning uchun birinchi parametri
System.Objectbo'lgan kengaytma metod aniqlamang, chunki bu metod barcha ifoda turlari uchun chaqirilishi mumkin bo'ladi va Visual Studio IntelliSense oynasini to'ldiradi. - Versiyalash muammosi: Kengaytma metodlarda versiyalash muammosi mavjud. Agar kelajakda Microsoft
StringBuilderklassiga sizning metod bilan bir xil imzoga egaIndexOfinstansiya metodi qo'shsa, kodingizni qayta kompilyatsiya qilganingizda kompilyator Microsoft ning instansiya metodini chaqiradi. Bu dasturingiz xatti-harakatini o'zgartirishi mumkin.
Turli Turlarni Kengaytma Metodlari bilan Kengaytirish
Kengaytma metodi aslida statik metodning chaqirilishi bo'lganligi sababli, CLR metodni chaqirish uchun ishlatiladigan ifoda qiymatining null emasligini tekshiruvchi kodni yaratmaydi:
// sb null
StringBuilder sb = null;
// Kengaytma metod chaqirish: NullReferenceException tashlanMAYDI
// IndexOf ning for tsikli ichida NullReferenceException tashlanaadi
sb.IndexOf('X');
// Instansiya metod chaqirish: NullReferenceException TASHLANADI
sb.Replace('.', '!');
Siz interfeys turlari uchun ham kengaytma metodlarini aniqlashingiz mumkin:
public static void ShowItems<T>(this IEnumerable<T> collection) {
foreach (var item in collection)
Console.WriteLine(item);
}
Bu kengaytma metod endi IEnumerable<T> interfeysini amalga oshiradigan har qanday ifoda bilan chaqirilishi mumkin:
public static void Main() {
// Har bir Char ni konsolda alohida satrda ko'rsatish
"Grant".ShowItems();
// Har bir String ni konsolda alohida satrda ko'rsatish
new[] { "Jeff", "Kristin" }.ShowItems();
// Har bir Int32 qiymatini konsolda alohida satrda ko'rsatish
new List<Int32>() { 1, 2, 3 }.ShowItems();
}
Kengaytma metodlari Microsoft ning Language Integrated Query (LINQ) texnologiyasining asosiy toshi hisoblanadi. Ko'plab kengaytma metodlarni taklif qiladigan klass misolini ko'rish uchun System.Linq.Enumerable statik klassiga va uning barcha statik kengaytma metodlariga qarang. Ushbu klassdagi har bir kengaytma metod IEnumerable yoki IEnumerable<T> interfeysini kengaytiradi.
Delegat turlari uchun ham kengaytma metodlar aniqlash mumkin:
public static void InvokeAndCatch<TException>(this Action<Object> d, Object o)
where TException : Exception {
try { d(o); }
catch (TException) { }
}
Va uni quyidagicha chaqirish mumkin:
Action<Object> action = o => Console.WriteLine(o.GetType());
// NullReferenceException tashlaydi:
action.InvokeAndCatch<NullReferenceException>(null);
// NullReferenceException ni yutib yuboradi
Nihoyat, C# kompilyatori sizga kengaytma metodiga havola qiluvchi delegat yaratishga ruxsat beradi:
public static void Main() {
// Statik ShowItems kengaytma metodiga ishora qiluvchi Action delegati yaratish
// va birinchi argument "Jeff" satriga ishora qilish
Action a = "Jeff".ShowItems;
.
.
.
// ShowItems ni chaqirish va unga "Jeff" satriga havola uzatish
a();
}
Bu kodda C# kompilyatori Action delegatini yaratish uchun IL kodi yaratadi. Delegat yaratishda konstruktorga chaqirilishi kerak bo'lgan metod (ShowItems) va maqsad obyekti — "Jeff" satriga havola uzatiladi. Bu biroz noodatiy, lekin tabiiy ishlaydi.
Extension Atributi
Kengaytma metodlari kontseptsiyasi faqat C# ga xos bo'lmasligi eng yaxshi bo'lar edi. Biz boshqa dasturlash tillarida ham kengaytma metodlarni aniqlash va ulardan foydalanish imkoniyatini xohlaymiz. Buning uchun ishlaydigan kompilyator statik turlar va metodlarni skanerlashni, potentsial mos kengaytma metodlarni qidirishni qo'llab-quvvatlashi kerak. Kompilyatorlar buni tezda bajarishi muhim.
C# da birinchi parametri this kalit so'zi bilan belgilangan statik metod ichki tarzda metod va uni o'z ichiga olgan assembly metadatasiga System.Runtime.CompilerServices.ExtensionAttribute maxsus atributini qo'llaydi. Bu atribut quyidagicha ko'rinadi:
// System.Runtime.CompilerServices nomlar fazosida aniqlangan
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class
| AttributeTargets.Assembly)]
public sealed class ExtensionAttribute : Attribute {
}
Bundan tashqari, bu atribut kamida bitta kengaytma metodini o'z ichiga olgan har bir statik klass metadatasiga va kamida bitta kengaytma metodini o'z ichiga olgan assembly metadatasiga ham qo'llaniladi.
Endi instansiya metodi mavjud bo'lmagan kodni kompilyatsiya qilganda, kompilyator tez ravishda barcha havola qilingan assemblylarni skanerlashi va qaysilari kengaytma metodlarni o'z ichiga olganini aniqlashi mumkin. Keyin faqat kengaytma metodlarni o'z ichiga olgan statik klasslarni va faqat kengaytma metodlarning o'zini skaner qilib, potentsial mosliklarni imkon qadar tez topishi mumkin.
ExtensionAttribute klassi System.Core.dll assemblysida aniqlangan. Bu shuni anglatadiki, kompilyator yaratgan assembly System.Core.dll ga havola o'z ichiga oladi — hatto siz System.Core.dll dan hech qanday turni ishlatmasangiz ham. Lekin bu jiddiy muammo emas, chunki ExtensionAttribute faqat kompilyatsiya vaqtida ishlatiladi; ish vaqtida (runtime) System.Core.dll dasturingiz bu assemblydagi boshqa narsalarni ishlatmasa, yuklanishi shart emas.
Partial Metodlar (Qisman Metodlar)
Tasavvur qiling, siz tur ta'rifini o'z ichiga olgan C# manba kod faylini yaratadigan vosita ishlatmoqdasiz. Vosita ishlab chiqargan kod ichida xatti-harakatni moslashtirish mumkin bo'lgan potentsial joylar borligini biladi. Odatda, moslashtirish vosita yaratgan kodda virtual metodlarni chaqirish orqali amalga oshiriladi. Vosita yaratgan kodda virtual metodlar uchun tanalar ham bo'lishi kerak — ular hech narsa qilmaydi va shunchaki qaytadi (no-op).
Xatti-harakatni moslashtirish uchun o'z klassingizni bazaviy klassdan hosila qilib, virtual metodlarni qayta yozasiz (override). Mana bir misol:
// Vosita yaratgan kod:
internal class Base {
private String m_name;
// m_name maydoni o'zgartirilishidan oldin chaqiriladi
protected virtual void OnNameChanging(String value) {
}
public String Name {
get { return m_name; }
set {
OnNameChanging(value.ToUpper()); // Klassga o'zgarish haqida xabar
m_name = value; // Maydonni o'zgartirish
}
}
}
// Dasturchi yozgan kod:
internal class Derived : Base {
protected override void OnNameChanging(string value) {
if (String.IsNullOrEmpty(value))
throw new ArgumentNullException("value");
}
}
Afsuski, bu yondashuvda ikkita muammo bor:
- Tur sealed bo'lmagan klass bo'lishi kerak. Bu texnikani sealed klasslar va value turlar uchun ishlatib bo'lmaydi. Bundan tashqari, statik metodlar uchun ham bu texnikani ishlatib bo'lmaydi, chunki ular qayta yozib bo'lmaydi.
- Samaradorlik muammolari mavjud. Metodni qayta yozish uchun tur aniqlash — ozgina tizim resurslarini sarflaydi. Va hatto
OnNameChangingxatti-harakatini qayta yozishni xohlamasangiz ham, bazaviy klass kodi hali ham hech narsa qilmaydigan virtual metodni chaqiradi.ToUpperham argumentgaOnNameChangingmurojaat qilsa ham qilmasa ham chaqiriladi.
C# ning partial metodlari xususiyati turning xatti-harakatini yuqoridagi muammolarni hal qilgan holda qayta yozish imkonini beradi:
// Vosita yaratgan kod (birinchi fayl):
internal sealed partial class Base {
private String m_name;
// Partial metod E'LONI — tanasi yo'q
partial void OnNameChanging(String value);
public String Name {
get { return m_name; }
set {
OnNameChanging(value.ToUpper()); // Klassga o'zgarish haqida xabar
m_name = value; // Maydonni o'zgartirish
}
}
}
// Dasturchi yozgan kod (ikkinchi fayl):
internal sealed partial class Base {
// Partial metod AMALGA OSHIRILISHI
partial void OnNameChanging(String value) {
if (String.IsNullOrEmpty(value))
throw new ArgumentNullException("value");
}
}
Bu kodni kompilyatsiya qilganingizda, asl kod bilan bir xil natija olasiz. Bu yangi kodning bir nechta muhim jihatlari bor:
- Klass endi sealed (muhrlangan) — u statik klass yoki hatto value tur ham bo'lishi mumkin.
- Vosita yaratgan kod va dasturchi yozgan kod aslida bitta tur ta'rifini tashkil etuvchi ikkita partial ta'rifdir.
- Vosita yaratgan kod
partialtokeni bilan belgilangan metod e'lonini (tanasiz) aniqlagan. - Dasturchi yozgan kod
partialtokeni bilan belgilangan metod amalga oshirilishini (tanali) aniqlagan.
Visual Studio muharririda partial deb yozib bo'sh joy (space) bossangiz, IntelliSense oynasi atrofidagi turning aniqlangan lekin hali amalga oshirilmagan barcha partial metod e'lonlarini ko'rsatadi. IntelliSense oynasidan partial metodni tanlashingiz mumkin va Visual Studio siz uchun metod prototipini avtomatik yaratadi. Bu samaradorlikni oshiruvchi juda yaxshi xususiyat.
Amalga Oshirilmagan Partial Metodlarning Afzalligi
Partial metodlarning yana bir katta yaxshilanishi bor. Aytaylik, siz vosita yaratgan turning xatti-harakatini o'zgartirmoqchi emassiz. Bunday holatda manba kod faylini umuman bermaysiz. Agar faqat vosita yaratgan kodni mustaqil kompilyatsiya qilsangiz, kompilyator IL kodi va metadatani xuddi vosita yaratgan kod quyidagicha yozilganday ishlab chiqaradi:
// Amalga oshiruvchi partial metod e'loni bo'lmasa,
// mantiqiy ekvivalent:
internal sealed class Base {
private String m_name;
public String Name {
get { return m_name; }
set {
m_name = value; // Maydonni o'zgartirish
}
}
}
Ya'ni, agar amalga oshiruvchi partial metod e'loni mavjud bo'lmasa, kompilyator partial metodni ifodalovchi hech qanday metadata yaratmaydi. Bundan tashqari, kompilyator partial metodni chaqiruvchi hech qanday IL ko'rsatmalarini yaratmaydi. Va kompilyator partial metodga uzatiladigan argumentlarni baholaydigan hech qanday kodni ham yaratmaydi. Bu misolda, kompilyator ToUpper metodini chaqirish uchun kod yaratmaydi. Natijada kamroq metadata/IL hosil bo'ladi va ish vaqtidagi ishlash ajoyib!
Partial metodlar System.Diagnostics.ConditionalAttribute atributiga o'xshash ishlaydi. Lekin partial metodlar bitta tur ichida ishlaydi, ConditionalAttribute esa boshqa turda aniqlangan metodlarni shartli chaqirish uchun ishlatilishi mumkin.
Partial Metodlar: Qoidalar va Ko'rsatmalar
Partial metodlar haqida bilishingiz kerak bo'lgan qo'shimcha qoidalar va ko'rsatmalar:
- Faqat partial klass yoki struct ichida: Partial metodlar faqat
partial classyokipartial structichida e'lon qilinishi mumkin. - Doimo
voidqaytarish turi: Partial metodlarning qaytarish turi doimovoidbo'lishi kerak va ularoutmodifikatori bilan belgilangan parametrlarga ega bo'la olmaydi. Bu cheklovlar mavjud, chunki ish vaqtida metod mavjud bo'lmasligi mumkin va shuning uchun siz metod qaytaradigan qiymatga o'zgaruvchi initsializatsiya qila olmaysiz (chunki metod qaytarmasligi mumkin). Xuddi shunday,outparametr bo'la olmaydi, chunki metod uni initsializatsiya qilishi kerak va metod mavjud bo'lmasligi mumkin. Lekin partial metoddarefparametrlar, generik parametrlar bo'lishi mumkin, instansiya yoki statik bo'lishi mumkin vaunsafedeb belgilanishi mumkin. - Identik imzolar: Albatta, aniqlash va amalga oshirish partial metod e'lonlari identik imzolarga ega bo'lishi kerak. Agar ikkalasida maxsus atributlar mavjud bo'lsa, kompilyator ikkala metod atributlarini birlashtiradi. Parametrga qo'llangan har qanday atributlar ham birlashtiriladi.
- Delegat yaratib bo'lmaydi: Agar amalga oshiruvchi partial metod e'loni bo'lmasa, partial metodga havola qiluvchi delegat yaratishga uringan har qanday kod kompilyatsiya xatosi oladi. Buning sababi metod ish vaqtida mavjud emas. Kompilyator quyidagi xabarni beradi:
error CS0762: Cannot create delegate from method 'Base.OnNameChanging(string)' because it is a partial method without an implementing declaration. - Doimo private: Partial metodlar doimo
privatemetod hisoblanadi. Lekin C# kompilyatori partial metod e'lonidan oldinprivatekalit so'zini yozishni taqiqlaydi.
Ushbu bobda siz turlarning turli xil metodlari bilan tanishdingiz: instance konstruktorlar, tur konstruktorlari, operator overload metodlari, konversiya operatorlari, kengaytma metodlari va partial metodlar. Har bir metod turining o'z maqsadi va qo'llanilish sohasi bor. Konstruktorlar obyektlarni to'g'ri initsializatsiya qilishni ta'minlaydi. Operator overloadlar sizning turingiz uchun intuitiv sintaksisni taklif qiladi. Kengaytma metodlari mavjud turlarga yangi funksionallik qo'shish imkonini beradi. Va partial metodlar kod generatsiyasi stsenariylarida kuchli moslashtirish mexanizmini taqdim etadi.