Umumlashgan turlarga cheklovlar.
Umumiy parametrlar yordamida biz har qanday turdagi umumiy sinflarni yozishimiz mumkin. Biroq, ba'zida turni konkretlashtirish kerak bo'ladi. Misol uchun, bizda ba'zi xabarlarni ifodalovchi quyidagi Message sinfi mavjud:
class Message
{
public string Text { get; } // habar matni
public Message(string text)
{
Text = text;
}
}
Aytaylik, biz xabarlarni Message ob'ektlari sifatida yuborish usulini aniqlamoqchimiz. Birinchi qarashda biz quyidagi usulni aniqlashimiz va foydalanishimiz mumkin:
SendMessage(new Message("Hello World"));
void SendMessage(Message message)
{
Console.WriteLine($"Xabar yuborilmoqda: {message.Text}");
}
SendMessage usuli Message ob'ektini message parametri sifatida qabul qiladi va uni yuborishga taqlid qiladi. Hamma narsa yaxshi bo'lib tuyuladi va undan yaxshiroq narsani tasavvur qilish qiyin. Lekin Message sinfi meros qilib olingan sinflarga ega bo'lishi mumkin. Masalan, elektron pochta xabarlari uchun EmailMessage sinfi, sms xabarlari uchun SmsMessae va boshqalar.
class EmailMessage : Message
{
public EmailMessage(string text) : base(text) { }
}
class SmsMessage : Message
{
public SmsMessage(string text) : base(text) { }
}
Agar biz ushbu sinflarni ifodalovchi xabarlarni yubormoqchi bo'lsakchi? Hech qanday muammo yo'q ko'rinadi, chunki SendMessage usuli Message ob'ektini va shunga mos ravishda olingan sinflar ob'ektlarini ham qabul qiladi:
SendMessage(new EmailMessage("Hello World"));
void SendMessage(Message message)
{
Console.WriteLine($"Xabar yuborilmoqda: {message.Text}");
}
Ammo bu erda biz turdagi konvertatsiyaga duch keldik: EmailMessage dan Messagega. Bundan tashqari, agar biz xabar ob'ektini olingan sinf ob'ektiga aylantirmoqchi bo'lsak, yana bir turdagi xavfsizlik muammosi mavjud. Bu holda, konversiyalarni oldini olish uchun biz umumlashganlarni qo'llashimiz mumkin:
void SendMessage(T message)
{
Console.WriteLine($"Xabar yuborilmoqda: {message.Text}");
// ! Xato - Text xususiyati
}
Umumlashganlar konvertatsiya qilishdan qochish imkonini beradi, ammo endi biz boshqa muammoga duch keldik - umumiy parametr T har qanday turni nazarda tutadi. Lekin har bir tur Text xususiyatiga ega emas. Shunga ko'ra, T tipidagi ob'ekt uchun Text xususiyati aniqlanmagan va biz bu xususiyatdan foydalana olmaymiz. Bundan tashqari, T ob'ekti uchun sukut bo'yicha biz faqat object tipidagi usullardan foydalanishimiz mumkin.
Shunday qilib, muammo paydo bo'ladi: turlarni o'zgartirishdan qochish va shunga mos ravishda umumlashganlardan foydalanish kerak, boshqa tomondan, usul ichidagi Message sinfining funksionalligiga kirish kerak. Umumlashtirish cheklovlari bu muammoni hal qiladi.
Usullarning cheklovlari. Usul cheklovlari parametrlar ro'yxatidan keyin where operatoridan keyin ko'rsatiladi:
usul_nomi (parametrlar) where T: cheklash_turi
where operatoridan keyin cheklov qo'llaniladigan umumiy parametr keladi. Va cheklash turi yo'g'on nuqta bilan ko'rsatilgan - odatda cheklov sifatida ma'lum bir tur qo'llaniladi.
Masalan, Message obyektlarini yuboruvchi SendMessage usuliga cheklovlarni qo'llang.
SendMessage(new Message("Hello World"));
SendMessage(new EmailMessage("Bye World"));
void SendMessage(T message) where T: Message
{
Console.WriteLine($"Xabar yuborilmoqda: {message.Text}");
}
class Message
{
public string Text { get; } // habar matni
public Message(string text)
{
Text = text;
}
}
class EmailMessage : Message
{
public EmailMessage(string text) : base(text) { }
}
SendMessage usulining ta'rifidagi where T: Message bandi Message sinfi va olingan sinflar ob'ektlari T umumiy parametri orqali o'tkazilishini aytadi. Buning yordamida kompilyator T ning Message sinfining funksionalligiga ega bo'lishini bilib oladi va shunga mos ravishda metod ichidagi Message sinfining usullari va xususiyatlariga muammosiz kirishimiz mumkin.
Usulni chaqirganda, burchakli qavslar ichida turni ko'rsatishning hojati yo'q kompilyator o'tkazilgan qiymatdan kelib chiqqan holda usulning qaysi turiga yozilishini aniqlaydi:
SendMessage(new EmailMessage("Bye World"));
Biroq, bu aniq amalga oshirilishi mumkin
SendMessage(new EmailMessage("Bye World"));
Umumlashganlarning turlari bo'yicha cheklovlari. Xuddi shunday, siz umumiy turdagi cheklovlarni belgilashingiz mumkin. Masalan, umumiy sinflarning cheklovlari:
class sinf_nomi where T: tur_cheklovlari
Misol sifatida, Message ob'ektlari sifatida xabarlarni yuboradigan messenjer sinfini aniqlaymiz:
class Messenger where T : Message
{
public void SendMessage(T message)
{
Console.WriteLine($"Xabar yuborilmoqda: {message.Text}");
}
}
class Message
{
public string Text { get; } // habar matni
public Message(string text)
{
Text = text;
}
}
class EmailMessage : Message
{
public EmailMessage(string text) : base(text) { }
}
Bu erda yana cheklov where T: Message Messenger sinfi uchun o'rnatiladi. Ya'ni, Messenger sinfida T tipidagi barcha ob'ektlar Message ob'ektlari sifatida ishlatilishi mumkin. Va bu holda, xabarlarni yuborish SendMessage usulida Messenger sinfida yana emulyatsiya qilinadi.
Keling, xabarlarni yuborish uchun sinfdan foydalanamiz:
Messenger telegram = new Messenger();
telegram.SendMessage(new Message("Hello World"));
Messenger outlook = new Messenger();
outlook.SendMessage(new EmailMessage("Bye World"));
Cheklash turlari va standart cheklovlar. Cheklov sifatida quyidagi turlardan foydalanishimiz mumkin:
Sinflar
Interfeyslar
class - umumiy parametr sinfni ifodalashi kerak
struct - umumiy parametr strukturani ifodalashi kerak
new() - umumiy parametr umumiy (public) parametrsiz konstruktorga ega bo'lgan turni ifodalashi kerak
Biz foydalanishimiz mumkin bo'lgan bir qator standart cheklovlar mavjud. Xususan, siz faqat tuzilmalar yoki boshqa qiymat turlari ishlatilishi uchun cheklovni belgilashingiz mumkin:
class Messenger where T : struct
{}
Shu bilan birga, sinflardan farqli o'laroq, beton konstruktsiyalarni cheklash sifatida ishlatish mumkin emas.
Shuningdek, siz ma'lumot turlarini cheklovlar sifatida o'rnatishingiz mumkin:
class Messenger where T : class
{}
Bundan tashqari, umumiy parametrsiz konstruktorga ega bo'lgan sinf yoki tuzilmani belgilash uchun cheklov sifatida new so'zidan foydalanishingiz mumkin:
class Messenger where T : new()
{}
Agar universal parametr uchun bir nechta cheklovlar belgilangan bo'lsa, ular ma'lum bir tartibda borishlari kerak:
Sinf nomi, class, struct. Bundan tashqari, biz bir vaqtning o'zida ushbu cheklovlardan faqat bittasini aniqlay olamiz.
Interfeys nomi
new()
class Smartphone where T: Messenger, new()
{
….
}
Bir nechta umumiy parametrlardan foydalanish. Agar sinf bir nechta universal parametrlardan foydalansa, siz ularning har biriga ketma-ket cheklovlarni o'rnatishingiz mumkin:
class Messenger
where T : Message
where P: Person
{
public void SendMessage(P sender, P receiver, T message)
{
Console.WriteLine($"Jo`natuvchi: {sender.Name}");
Console.WriteLine($"Qabul qiluvchi: {receiver.Name}");
Console.WriteLine($"Habar: {message.Text}");
}
}
class Person
{
public string Name { get;}
public Person(string name) => Name = name;
}
class Message
{
public string Text { get; } // habar matni
public Message(string text) => Text = text;
}
Bunday holda, Person tipidagi ob'ektlar P parametri uchun, Message ob'ektlari T parametri uchun uzatiladi.
Keling, sinflarga tadbiq etamiz:
Messenger telegram = new Messenger();
Person tom = new Person("Tom");
Person bob = new Person("Bob");
Message hello = new Message("Hello, Bob!");
telegram.SendMessage(tom, bob, hello);
Konsol chiqishi:
Jo`natuvchi: Tom
Qabul qiluvchi: Bob
habar: Salom, Bob!
Dostları ilə paylaş: |