16-Bob: Massivlar

Massiv elementlarini initsializatsiya qilish, casting, System.Array, interfeyslar, massivlarni uzatish va qaytarish, noldan boshqa pastki chegarali massivlar, ichki tuzilish va xavfsiz bo'lmagan massiv murojaat

Massivlar bir nechta elementlarni yagona to'plam sifatida ko'rib chiqish imkonini beradigan mexanizmlardir. Microsoft .NET umumiy til runtime (CLR) bir o'lchamli massivlarni, ko'p o'lchamli massivlarni va tishli (jagged) massivlarni (ya'ni massivlar massivini) qo'llab-quvvatlaydi. Barcha massiv turlari System.Array abstrakt klassidan bilvosita hosila bo'ladi, o'zi esa System.Object dan hosila bo'ladi. Bu shuni anglatadiki, massivlar doimo managed heap da joylashtirilgan reference turlardir va ilovangizning o'zgaruvchisi yoki maydoni massivning elementlarini emas, balki massivning o'ziga havolani o'z ichiga oladi. Quyidagi kod buni aniqroq ko'rsatadi.

Int32[] myIntegers;              // Massivga havolani e'lon qilish
myIntegers = new Int32[100];     // 100 ta Int32 dan iborat massiv yaratish

Birinchi satrda myIntegers bir o'lchamli Int32 massiviga ishora qilishga qodir bo'lgan o'zgaruvchidir. Dastlab myIntegers null ga o'rnatiladi, chunki men hali massiv ajratmaganman. Ikkinchi satr 100 ta Int32 qiymatdan iborat massivni ajratadi; barcha Int32 lar 0 ga initsializatsiya qilinadi. Massivlar reference turlar bo'lgani uchun, 100 ta unboxed Int32 ni saqlash uchun kerakli xotira bloki managed heap da ajratiladi. Aslida, massiv elementlaridan tashqari, massiv obyekti egallagan xotira blokida tur obyekt ko'rsatkichi, sinxronizatsiya blok indeksi va bir nechta qo'shimcha overhead a'zolari ham mavjud. Bu massivning xotira blokining manzili qaytariladi va myIntegers o'zgaruvchisida saqlanadi.

Siz reference turlarning massivlarini ham yaratishingiz mumkin.

Control[] myControls;              // Massivga havolani e'lon qilish
myControls = new Control[50];      // 50 ta Control havolasidan iborat massiv yaratish

Birinchi satrda myControls bir o'lchamli Control havolalar massiviga ishora qilishga qodir bo'lgan o'zgaruvchidir. Dastlab myControls null ga o'rnatiladi, chunki men hali massiv ajratmaganman. Ikkinchi satr 50 ta Control havolasidan iborat massivni ajratadi; bu havolalarning barchasi null ga initsializatsiya qilinadi. Control reference tur bo'lganligi sababli, massiv yaratish faqat havolalar to'plamini yaratadi; haqiqiy obyektlar hali yaratilmagan.

16-1 Rasm value turlar massivlari va reference turlar massivlarining managed heap da qanday ko'rinishini ko'rsatadi.

16-1 Rasm

Rasmdagi Controls massivi quyidagi satrlar bajarilganidan keyin holatni ko'rsatadi:

myControls[1]  = new Button();
myControls[2]  = new TextBox();
myControls[3]  = myControls[2];    // Ikki element bir xil obyektga ishora qiladi
myControls[46] = new DataGrid();
myControls[48] = new ComboBox();
myControls[49] = new Button();

Umumiy Til Spetsifikatsiyasiga (CLS) muvofiqlik barcha massivlar noldan boshlanadigan bo'lishini talab qiladi. Bu C# da yozilgan metod massiv yaratishi va massivga havolani boshqa tilda (masalan, Microsoft Visual Basic .NET) yozilgan kodga uzatishiga imkon beradi. Bundan tashqari, noldan boshlanadigan massivlar eng keng tarqalgan bo'lganligi sababli, Microsoft ularning ishlashini optimallashtirish uchun ko'p vaqt sarflagan. Biroq, CLR noldan boshlanmaydigan massivlarni ham qo'llab-quvvatlaydi, garchi ulardan foydalanish tavsiya etilmasa ham. Ishlash jazosi yoki tillar arasi portativlik haqida qayg'urmaydigan dasturchilar uchun noldan boshlanmaydigan massivlarni qanday yaratish va ishlatishni ushbu bobda keyinroq ko'rsataman.

16-1 Rasmda ko'rib turganingizdek, har bir massiv o'zi bilan bog'langan qo'shimcha overhead ma'lumotlariga ega. Bu ma'lumot massivning darajasini (o'lchamlar sonini), har bir o'lcham uchun pastki chegaralarni (deyarli doim 0) va har bir o'lcham uzunligini o'z ichiga oladi. Overhead massivning element turiga havola ham o'z ichiga oladi. Bu overhead haqida so'rov yuborish imkonini beruvchi metodlarni keyinroq bobda aytib o'taman.

Hozirgacha men bir o'lchamli massivlar yaratishni ko'rsatib o'tdim. Imkon qadar bir o'lchamli, noldan boshlanadigan massivlarga yopishing, ular ba'zan SZ massivlar yoki vektorlar deb ham ataladi. Vektorlar eng yaxshi ishlashni ta'minlaydi, chunki siz ularni manipulyatsiya qilish uchun maxsus Intermediate Language (IL) ko'rsatmalaridan — masalan, newarr, ldelem, ldelema, ldlen va stelem — foydalanishingiz mumkin. Biroq, ko'p o'lchamli massivlar bilan ishlashni afzal ko'rsangiz, buni amalga oshirishingiz mumkin. Mana ko'p o'lchamli massivlarning ba'zi misollari.

// Ikki o'lchamli Doubles massivi yaratish.
Double[,] myDoubles = new Double[10, 20];

// Uch o'lchamli String havolalar massivi yaratish.
String[,,] myStrings = new String[5, 3, 10];

CLR tishli (jagged) massivlarni ham qo'llab-quvvatlaydi, ular massivlar massivlaridir. Noldan boshlanadigan, bir o'lchamli tishli massivlar oddiy vektorlar bilan bir xil ishlashga ega. Biroq, tishli massiv elementlariga kirish ikki yoki undan ko'p massiv murojatini talab qiladi. Mana har biri Point instansiyalari massividan iborat ko'pburchaklar massivini yaratish misollari.

// Point massivlarining bir o'lchamli massivini yaratish.
Point[][] myPolygons = new Point[3][];

// myPolygons[0] 10 ta Point instansiyasi massiviga ishora qiladi.
myPolygons[0] = new Point[10];

// myPolygons[1] 20 ta Point instansiyasi massiviga ishora qiladi.
myPolygons[1] = new Point[20];

// myPolygons[2] 30 ta Point instansiyasi massiviga ishora qiladi.
myPolygons[2] = new Point[30];

// Birinchi ko'pburchakdagi Pointlarni ko'rsatish.
for (Int32 x = 0; x < myPolygons[0].Length; x++)
   Console.WriteLine(myPolygons[0][x]);
Eslatma

CLR massivga kirish indeksining haqiqiy ekanligini tekshiradi. Boshqacha aytganda, siz 100 ta elementli massiv yaratib (0 dan 99 gacha raqamlangan), keyin -5 yoki 100 indeksdagi elementga kirishga urinib bo'lmaysiz. Bunday qilish System.IndexOutOfRangeException ni tashlaydi. Massiv chegarasidan tashqaridagi xotiraga kirishga ruxsat berish tur xavfsizligi va potensial xavfsizlik zaifligining buzilishi bo'lar edi, va CLR tekshiriladigan kodga buni qilishga ruxsat bermaydi. Odatda, indeks tekshirish bilan bog'liq ishlash pasayishi ahamiyatsiz, chunki just-in-time (JIT) kompilyator odatda massiv chegaralarini loop ning har bir iteratsiyasida emas, balki loop boshida bir marta tekshiradi. Agar siz CLR ning indeks tekshiruvlarining ishlash ta'siridan xavotirda bo'lsangiz, massivga kirish uchun C# da unsafe kodni ishlatishingiz mumkin. Ushbu bobning keyingi "Massivlarning Ichki Tuzilishi" bo'limi buni qanday amalga oshirishni ko'rsatadi.

Massiv Elementlarini Initsializatsiya Qilish

Avvalgi bo'limda men massiv obyektini yaratish va keyin massiv elementlarini initsializatsiya qilish usulini ko'rsatdim. C# bu ikki operatsiyani bitta bayonotda bajarish imkonini beradigan sintaksisni taklif qiladi. Quyidagi misolni ko'ring.

String[] names = new String[] { "Aidan", "Grant" };

Figurali qavslar ichidagi vergul bilan ajratilgan tokenlar to'plami massiv initsializatori (array initializer) deb ataladi. Har bir token ixtiyoriy murakkab ifoda yoki ko'p o'lchamli massiv bo'lsa, ichki o'rnatilgan massiv initsializatori bo'lishi mumkin. Yuqoridagi misolda men faqat ikkita oddiy String ifodani ishlatdim.

Agar siz lokal o'zgaruvchini initsializatsiya qilingan massivga havola qiladigan qilib e'lon qilayotgan bo'lsangiz, kodni biroz soddalashtirish uchun C# ning yashirin turlangan lokal o'zgaruvchi (var) xususiyatidan foydalanishingiz mumkin.

// C# ning yashirin turlangan lokal o'zgaruvchi xususiyatidan foydalanish:
var names = new String[] { "Aidan", "Grant" };

Bu yerda kompilyator names lokal o'zgaruvchisi String[] turiga ega bo'lishi kerakligini anglaydi, chunki bu tayinlash operatorining (=) o'ng tomonidagi ifodaning turidir.

Siz C# ning yashirin turlangan massiv xususiyatidan foydalanib, kompilyatorga massiv elementlarining turini aniqlashni topshirishingiz mumkin. E'tibor bering, quyidagi satrda new va [] orasida tur ko'rsatilmagan.

// C# ning yashirin turlangan lokal o'zgaruvchi va yashirin turlangan massiv xususiyatlari:
var names = new[] { "Aidan", "Grant", null };

Yuqoridagi satrda kompilyator massivning ichidagi ifodalar turlarini tekshiradi va barcha elementlarga umumiy bo'lgan eng yaqin bazaviy klassni tanlaydi. Bu misolda kompilyator ikkita String va null ni ko'radi. null har qanday reference turga (jumladan String ga) yashirin casting qilinadigan bo'lgani uchun, kompilyator String havolalari massivini yaratish va initsializatsiya qilish kerakligini xulosa qiladi.

Agar sizda quyidagi kod bo'lsa:

// C# ning yashirin turlangan lokal o'zgaruvchi va yashirin turlangan massiv xususiyatlari: (xato)
var names = new[] { "Aidan", "Grant", 123 };

kompilyator quyidagi xabarni chiqaradi: error CS0826: No best type found for implicitly-typed array. Buning sababi ikkita String va Int32 orasidagi umumiy bazaviy tur Object bo'lib, bu esa kompilyator Object havolalari massivini yaratishi va keyin 123 ni box qilishi kerakligini anglatadi. C# kompilyator jamoasi massiv elementlarini boxing qilishni kompilyator siz uchun qilishi uchun juda og'ir deb hisoblaydi va shuning uchun kompilyator xato chiqaradi.

Massivni initsializatsiya qilishda qo'shimcha sintaktik bonus sifatida siz quyidagini yozishingiz mumkin.

String[] names = { "Aidan", "Grant" };

E'tibor bering, tayinlash operatorining (=) o'ng tomonida faqat massiv initsializatori ifodasi berilgan — new, tur va [] yo'q. Bu sintaksis yoqimli, lekin afsuski, C# kompilyator yashirin turlangan lokal o'zgaruvchilar bilan bu sintaksisdan foydalanishga ruxsat bermaydi.

// Bu endi lokal o'zgaruvchi (xato)
var names = { "Aidan", "Grant" };

Agar siz yuqoridagi satrni kompilyatsiya qilishga harakat qilsangiz, kompilyator ikkita xabar chiqaradi: error CS0820: Cannot initialize an implicitly-typed local variable with an array initializer va error CS0622: Can only use array initializer expressions to assign to array types. Try using a new expression instead. Garchi kompilyator buni ishlatishi mumkin bo'lsa-da, C# jamoasi bu sizning o'rningizga kompilyator tomonidan juda ko'p narsa qilish bo'ladi deb hisobladi. Bu massivning turini anglash, massivni new qilish, massivni initsializatsiya qilish va lokal o'zgaruvchining turini anglash bo'lar edi.

Oxirgi narsa — men sizga yashirin turlangan massivlarni anonim turlar va yashirin turlangan lokal o'zgaruvchilar bilan qanday ishlatishni ko'rsatmoqchiman. Anonim turlar va tur identifikatsiyasi ularga qanday tegishli ekanligi 10-Bob "Xossalar" da muhokama qilingan. Quyidagi kodni ko'rib chiqing.

// C# ning yashirin turlangan lokal, yashirin turlangan massiv va anonim tur xususiyatlari:
var kids = new[] {new { Name="Aidan" }, new { Name="Grant" }};

// Namuna foydalanish (boshqa yashirin turlangan lokal o'zgaruvchi bilan):
foreach (var kid in kids)
   Console.WriteLine(kid.Name);

Bu misolda men ikki element uchun ikkita ifodaga ega massiv initsializatoridan foydalanayapman. Har bir ifoda anonim turni ifodalaydi (chunki new operatoridan keyin tur nomi ko'rsatilmagan). Ikkala anonim tur bir xil strukturaga ega bo'lganligi sababli (bitta String turli Name maydoni), kompilyator bu ikki obyektning aynan bir xil turdagi ekanligini biladi. Endi men new va [] orasida tur ko'rsatilmagan C# ning yashirin turlangan massiv xususiyatidan foydalanaman, shunda kompilyator massiv turini o'zi aniqlaydi, massiv obyektini yaratadi va uni ikkita instansiyaga havolalar bilan initsializatsiya qiladi. Nihoyat, bu massiv obyektiga havola C# ning yashirin turlangan lokal o'zgaruvchi xususiyati tufayli kids lokal o'zgaruvchisiga tayinlanadi.

Men foreach loop ni ko'rsataman, bu massivning qanday yaratilgani va initsializatsiya qilinganini ko'rsatish uchun misoldir. Men loop uchun yashirin turlangan lokal o'zgaruvchi (kid) dan foydalanishim kerak. Bu kodni ishga tushirganimda quyidagi natijani olaman.

Aidan
Grant

Massivlarni Casting Qilish

Reference turlar elementli massivlar uchun CLR sizga manba massivning element turini maqsad turga yashirin casting qilish imkonini beradi. Cast muvaffaqiyatli bo'lishi uchun ikkala massiv turi bir xil o'lchamlar soniga ega bo'lishi kerak va manba element turidan maqsad element turiga yashirin yoki aniq konvertatsiya mavjud bo'lishi kerak. CLR value tur elementli massivlarni boshqa turga casting qilishga ruxsat bermaydi. (Biroq, Array.Copy metodidan foydalanib, yangi massiv yaratishingiz va uni maqsad effektini olish uchun to'ldirishingiz mumkin.) Quyidagi kod massiv castingi qanday ishlashini ko'rsatadi.

// Ikki o'lchamli FileStream massivi yaratish.
FileStream[,] fs2dim = new FileStream[5, 10];

// Ikki o'lchamli Object massiviga yashirin cast.
Object[,] o2dim = fs2dim;

// Ikki o'lchamli massivdan bir o'lchamli massivga cast qilish mumkin emas
// Kompilyator xatosi CS0030: Cannot convert type 'object[*,*]' to
// 'System.IO.Stream[]'
Stream[] s1dim = (Stream[]) o2dim;

// Stream ning ikki o'lchamli massiviga aniq cast.
Stream[,] s2dim = (Stream[,]) o2dim;

// String ning ikki o'lchamli massiviga aniq cast.
// Kompilyatsiya qilinadi, lekin ish vaqtida InvalidCastException tashlaydi
String[,] st2dim = (String[,]) o2dim;

// Bir o'lchamli Int32 massivi yaratish (value turlar).
Int32[] i1dim = new Int32[5];

// Value turlar massividan boshqa narsaga cast qilish mumkin emas
// Kompilyator xatosi CS0030: Cannot convert type 'int[]' to 'object[]'
Object[] o1dim = (Object[]) i1dim;

// Yangi massiv yaratish, keyin Array.Copy yordamida har bir elementni
// manba massivdagi kerakli turga aylantirish.
// Quyidagi kod boxed Int32 lar massivi yaratadi.
Object[] ob1dim = new Object[i1dim.Length];
Array.Copy(i1dim, ob1dim, i1dim.Length);

Array.Copy metodi shunchaki elementlarni bir massivdan boshqasiga nusxalaydigan metod emas. Copy metodi bir-biriga mos tushadigan xotira mintaqalarini to'g'ri boshqaradi, xuddi C ning memmove funksiyasi kabi. C ning memcpy funksiyasi esa mos tushadigan mintaqalarni to'g'ri boshqaramaydi. Copy metodi har bir massiv elementini nusxalashda konvertatsiya kerak bo'lsa, uni ham amalga oshiradi. Copy metodi quyidagi konvertatsiyalarni bajarishga qodir:

  • Value tur elementlarini reference tur elementlariga boxing qilish, masalan, Int32[] ni Object[] ga nusxalash.
  • Reference tur elementlarini value tur elementlariga unboxing qilish, masalan, Object[] ni Int32[] ga nusxalash.
  • CLR primitiv value turlarni kengaytirish, masalan, Int32[] elementlarini Double[] ga nusxalash.
  • Massiv turiga asoslangan mos kelishini isbotlab bo'lmaydigan massiv turlari orasida nusxalashda elementlarni daraja pasaytirish (downcast), masalan, Object[] dan IFormattable[] ga casting. Agar Object[] dagi har bir obyekt IFormattable ni amalga oshirsa, Copy muvaffaqiyatli bo'ladi.

Mana Copy ning foydaliligini ko'rsatadigan yana bir misol.

// Interfeys amalga oshiradigan value turni aniqlash.
internal struct MyValueType : IComparable {
   public Int32 CompareTo(Object obj) {
      ...
   }
}

public static class Program {
   public static void Main() {
      // 100 ta value turdan iborat massiv yaratish.
      MyValueType[] src = new MyValueType[100];

      // IComparable havolalari massivini yaratish.
      IComparable[] dest = new IComparable[src.Length];

      // Manba massivdagi elementlarni boxed versiyalariga
      // ishora qiluvchi qilib maqsad massivga initsializatsiya qilish.
      Array.Copy(src, dest, src.Length);
   }
}

Siz tasavvur qilganingizdek, Framework Class Library (FCL) Array ning Copy metodidan juda tez-tez foydalanadi.

Ba'zi hollarda massivni bir turdan boshqasiga casting qilish foydali bo'ladi. Bu funksionallik turi massiv kovariantsiyasi (array covariance) deb ataladi. Massiv kovariantsiyasidan foydalanganingizda, u bilan bog'liq ishlash jarimalari haqida xabardor bo'lishingiz kerak. Keling, quyidagi kodga ega bo'lsangiz deylik.

String[] sa = new String[100];
Object[] oa = sa;   // oa String elementlar massiviga ishora qiladi
oa[5] = "Jeff";     // Ishlash jarimalari: CLR oa element turini String ekanligini tekshiradi; OK
oa[3] = 5;          // Ishlash jarimalari: CLR oa element turini Int32 ekanligini tekshiradi;
                     // ArrayTypeMismatchException tashlaydi

Yuqoridagi kodda oa o'zgaruvchisi Object[] sifatida tiplantirilgan; biroq u haqiqatda String[] ga ishora qiladi. Kompilyator sizga 5 ni massiv elementiga qo'yishga urinuvchi kod yozishga ruxsat beradi, chunki 5 Int32 bo'lib, Object dan hosila bo'lgan. Albatta, CLR tur xavfsizligini ta'minlashi kerak va massiv elementiga tayinlashda CLR tayinlash qonuniy ekanligini ta'minlashi kerak. Shuning uchun CLR ish vaqtida massiv Int32 elementlarni o'z ichiga olishini tekshirishi kerak. Bu holda, shunday emas va tayinlashga ruxsat berilmaydi; CLR ArrayTypeMismatchException ni tashlaydi.

Eslatma

Agar siz shunchaki ba'zi massiv elementlarining nusxasini boshqa massivga ko'chirish kerak bo'lsa, System.Buffer ning BlockCopy metodi Array ning Copy metodidan tezroq ishlaydi. Biroq, Buffer ning BlockCopy si faqat primitiv turlarni qo'llab-quvvatlaydi; u Array ning Copy metodi kabi casting imkoniyatlarini taklif qilmaydi. Int32 parametrlari massiv ichidagi element indekslari emas, balki bayt ofseti sifatida ifodalanadi. BlockCopy haqiqatan ham bit bo'yicha mos keladigan bir massiv turidan boshqasiga ma'lumotlarni nusxalash uchun mo'ljallangan, masalan, Unicode belgilarni o'z ichiga olgan (to'g'ri bayt tartibli) Byte[] ni Char[] ga nusxalash. Bu metod dasturchilarga massivni ixtiyoriy turdagi xotira bloki sifatida ko'rib chiqish imkoniyatining yo'qligini qisman to'ldirishga yordam beradi.

Agar siz bir massivdan boshqasiga massiv elementlari to'plamini ishonchli nusxalash kerak bo'lsa, System.Array ning ConstrainedCopy metodidan foydalaning. Bu metod nusxalash operatsiyasi to'liq yakunlanishi yoki maqsad massivdagi hech qanday ma'lumotni buzmasdan istisno tashlashini kafolatlaydi. Bu ConstrainedCopy ni cheklangan bajarilish mintaqasida (CER) ishlatish imkonini beradi. Bu kafolatni taklif qilish uchun ConstrainedCopy manba massivning element turi maqsad massivning element turi bilan bir xil yoki undan hosila bo'lishini talab qiladi. Bundan tashqari, u hech qanday boxing, unboxing yoki daraja pasaytirish (downcasting) bajarmaydi.

Barcha Massivlar System.Array dan Bilvosita Hosila Bo'ladi

Massiv o'zgaruvchisini quyidagicha e'lon qilganingizda:

FileStream[] fsArray;

CLR avtomatik ravishda AppDomain uchun FileStream[] turini yaratadi. Bu tur System.Array turidan bilvosita hosila bo'ladi va shuning uchun System.Array turida aniqlangan barcha instansiya metodlari va xossalari FileStream[] turida meros bo'ladi, bu esa ushbu metodlarni va xossalarni fsArray o'zgaruvchisi yordamida chaqirish imkonini beradi. Bu massivlar bilan ishlashni juda qulay qiladi, chunki System.Array da aniqlangan ko'plab foydali instansiya metodlari va xossalari mavjud, masalan, Clone, CopyTo, GetLength, GetLongLength, GetLowerBound, GetUpperBound, Length, Rank va boshqalar.

System.Array turi shuningdek ko'p sonli juda foydali statik metodlarni ham taklif qiladi. Bu metodlarning barchasi massivga havolani parametr sifatida oladi. Foydali statik metodlardan ba'zilari: AsReadOnly, BinarySearch, Clear, ConstrainedCopy, ConvertAll, Copy, Exists, Find, FindAll, FindIndex, FindLast, FindLastIndex, ForEach, IndexOf, LastIndexOf, Resize, Reverse, Sort va TrueForAll. Bu metodlarning har biri uchun ko'plab yuklamalar mavjud. Aslida, ko'pgina metodlar kompilyatsiya paytida tur xavfsizligi va yaxshi ishlashni ta'minlaydigan generik yuklamalarni taqdim qiladi. Men sizni bu metodlarning qanchalik foydali va kuchli ekanligini tushunish uchun SDK hujjatlarini ko'rib chiqishga undayman.

Barcha Massivlar IEnumerable, ICollection va IList ni Bilvosita Amalga Oshiradi

Turli xil kolleksiya obyektlarida ishlaydigan ko'plab metodlar mavjud, chunki bu metodlar IEnumerable, ICollection va IList kabi parametrlar bilan e'lon qilingan. Massivlarni bu metodlarga uzatish mumkin, chunki System.Array ham bu uchta interfeysni amalga oshiradi. System.Array bu nogenerik interfeyslarni amalga oshiradi, chunki ular barcha elementlarni System.Object sifatida ko'radi. Biroq, System.Array bu interfeyslarning generik ekvivalentlarini ham amalga oshirsaydi, kompilyatsiya paytida yaxshiroq tur xavfsizligi va ishlashni ta'minlagan bo'lardi.

CLR jamoasi System.Array ga IEnumerable<T>, ICollection<T> va IList<T> ni amalga oshirishni xohlamadi, chunki bu ko'p o'lchamli massivlar va noldan boshlanmaydigan massivlar bilan bog'liq muammolar tufayli. Bu interfeyslarni System.Array da aniqlash ularni barcha massiv turlari uchun yoqgan bo'lardi. Buning o'rniga, CLR kichik bir hiyla qo'llaydi: bir o'lchamli, noldan boshlanadigan pastki chegarali massiv turi yaratilganda, CLR avtomatik ravishda massiv turini IEnumerable<T>, ICollection<T> va IList<T> ni amalga oshiruvchi qiladi (bu yerda T massivning element turi) va shuningdek massiv turi bazaviy turlari uchun ham uchta interfeysni amalga oshiradi, agar ular reference turlar bo'lsa. Quyidagi iyerarxik diagramma buni aniqroq ko'rsatadi.

Object
   Array (nogenerik IEnumerable, ICollection, IList)
      Object[]           (IEnumerable, ICollection, IList of Object)
         String[]         (IEnumerable, ICollection, IList of String)
         Stream[]         (IEnumerable, ICollection, IList of Stream)
            FileStream[]  (IEnumerable, ICollection, IList of FileStream)
         .
         .   (boshqa reference tur massivlari)
         .

Masalan, agar sizda quyidagi kod satri bo'lsa:

FileStream[] fsArray;

CLR FileStream[] turini yaratganida, bu tur avtomatik ravishda IEnumerable<FileStream>, ICollection<FileStream> va IList<FileStream> interfeyslarini amalga oshiradi. Bundan tashqari, FileStream[] turi bazaviy turlar uchun ham interfeyslarni amalga oshiradi: IEnumerable<Stream>, IEnumerable<Object>, ICollection<Stream>, ICollection<Object>, IList<Stream> va IList<Object>. Bu interfeyslarning barchasi CLR tomonidan avtomatik ravishda amalga oshirilganligi sababli, fsArray o'zgaruvchisi bu interfeyslardan biri zarur bo'lgan har qanday joyda ishlatilishi mumkin. Masalan, fsArray o'zgaruvchisini quyidagi prototiplarga ega metodlarga uzatish mumkin.

void M1(IList<FileStream> fsList) { ... }
void M2(ICollection<Stream> sCollection) { ... }
void M3(IEnumerable<Object> oEnumerable) { ... }

E'tibor bering, agar massiv value tur elementlarini o'z ichiga olsa, massiv turi elementning bazaviy turlari uchun interfeyslarni amalga oshirmaydi. Masalan, agar sizda quyidagi kod satri bo'lsa:

DateTime[] dtArray;  // Value turlar massivi

U holda DateTime[] turi IEnumerable<DateTime>, ICollection<DateTime> va IList<DateTime> ni amalga oshiradi; u System.ValueType yoki System.Object ustidan generik bo'lgan bu interfeyslarning versiyalarini amalga oshirmaydi. Bu shuni anglatadiki, dtArray o'zgaruvchisini avvalroq ko'rsatilgan M3 metodiga argument sifatida uzatib bo'lmaydi. Buning sababi shundaki, value turlar massivlari xotirada reference turlar massivlaridan farqli joylashtiriladi. Massivlarning xotirada joylashuvi ushbu bobda avvalroq muhokama qilingan.

Massivlarni Uzatish va Qaytarish

Massivni metod argumenti sifatida uzatayotganingizda, siz haqiqatda bu massivga havolani uzatyapsiz. Shuning uchun, chaqirilgan metod massivdagi elementlarni o'zgartirishi mumkin. Agar siz bunga ruxsat bermasangiz, massivning nusxasini yaratishingiz va bu nusxani metodga uzatishingiz kerak. E'tibor bering, Array.Copy metodi yuzaki (shallow) nusxalashni amalga oshiradi va shuning uchun, agar massivning elementlari reference turlar bo'lsa, yangi massiv allaqachon mavjud obyektlarga ishora qiladi.

Xuddi shunday, ba'zi metodlar massivga havolani qaytaradi. Agar metod massivni yaratib, initsializatsiya qilsa, massivga havolani qaytarish yaxshi. Lekin agar metod ichki massivga havolani qaytarmoqchi bo'lsa, siz metod chaqiruvchisiga bu massivga va uning elementlariga to'g'ridan-to'g'ri murojaat qilish imkoniyatini berishni xohlaysizmi yoki yo'qligini hal qilishingiz kerak. Agar shunday bo'lsa, massivning havolasini qaytaring. Lekin ko'pincha, siz metod chaqiruvchisiga bunday murojaat imkoniyatini bermasangiz, metod yangi massiv yaratib, Array.Copy ni chaqirishi va yangi massivga havolani qaytarishi kerak. Yana eslatib o'taman, Array.Copy asl massivning yuzaki nusxasini yaratadi.

Agar siz massivga havolani qaytaradigan metod aniqlasangiz va u massivda hech qanday element bo'lmasa, metod null yoki nol elementli massivga havolani qaytarishi mumkin. Bu turdagi metodlarni amalga oshirayotganingizda, Microsoft nol uzunlikdagi massivni qaytarishni qat'iy tavsiya qiladi, chunki buning amalga oshirilishi metodni chaqiruvchi dasturchi yozishi kerak bo'lgan kodni soddalashtiradi. Masalan, bu oson tushuniladigan kod, hatto hech qanday uchrashuvlar bo'lmasa ham to'g'ri ishlaydi.

// Bu kodni yozish va tushunish osonroq.
Appointment[] appointments = GetAppointmentsForToday();
for (Int32 a = 0; a < appointments.Length; a++) {
   ...
}

Quyidagi kod ham uchrashuvlar bo'lmasa to'g'ri ishlaydi. Biroq, bu kodni yozish va tushunish biroz qiyinroq.

// Bu kodni yozish va tushunish qiyinroq.
Appointment[] appointments = GetAppointmentsForToday();
if (appointments != null) {
   for (Int32 a = 0; a < appointments.Length; a++) {
      // appointemnts[a] bilan biror narsa qilish
   }
}

Agar siz metodlaringizni null o'rniga nol elementli massivlarni qaytaradigan qilib loyihalasangiz, metodlaringiz chaqiruvchilari uchun ishlash osonroq bo'ladi. Aytgancha, buni maydonlar uchun ham xuddi shunday qilishingiz kerak. Agar turingizda massivga havola bo'lgan maydon bo'lsa, u massivda hech qanday element bo'lmasa ham, maydonga massivga ishora qildirish haqida o'ylab ko'ring.

Noldan Boshqa Pastki Chegarali Massivlarni Yaratish

Avvalroq men noldan boshlanmaydigan pastki chegara bilan massivlarni yaratish va ular bilan ishlash mumkinligini aytgan edim. Siz Array ning statik CreateInstance metodini chaqirib massivlarni dinamik ravishda yaratishingiz mumkin. Bu metodning bir nechta yuklamalari mavjud, ular sizga massivdagi elementlarning turini, massivdagi o'lchamlar sonini, har bir o'lchamning pastki chegaralarini va har bir o'lchamdagi elementlar sonini ko'rsatish imkonini beradi. CreateInstance massiv uchun xotira ajratadi, parametr ma'lumotlarini massivning xotira blokining overhead qismida saqlaydi va massivga havolani qaytaradi. Agar massiv ikki yoki undan ko'p o'lchamga ega bo'lsa, CreateInstance dan qaytarilgan havolani ElementType[] o'zgaruvchisiga (ElementType ba'zi tur nomi) casting qilishingiz mumkin, bu massivdagi elementlarga kirishni osonlashtiradi. Agar massiv faqat bitta o'lchamga ega bo'lsa, C# da siz Array ning GetValue va SetValue metodlaridan foydalanishingiz kerak.

Mana ikki o'lchamli System.Decimal qiymatlar massivini dinamik ravishda yaratishni ko'rsatadigan ba'zi kodlar. Birinchi o'lcham 2005 dan 2009 gacha bo'lgan kalendar yillarni, ikkinchi o'lcham esa 1 dan 4 gacha bo'lgan chorakliklarni ifodalaydi. Kod dinamik massivdagi barcha elementlar ustidan takrorlaydi. Men massivning chegaralarini kodga qo'lda yozishim mumkin edi, lekin men ularning qo'llanilishini ko'rsatish uchun System.Array ning GetLowerBound va GetUpperBound metodlaridan foydalanishga qaror qildim.

using System;

public static class DynamicArrays {
   public static void Main() {
      // Men [2005..2009][1..4] ikki o'lchamli massivni xohlayman.
      Int32[] lowerBounds = { 2005, 1 };
      Int32[] lengths     = {    5, 4 };
      Decimal[,] quarterlyRevenue = (Decimal[,])
         Array.CreateInstance(typeof(Decimal), lengths, lowerBounds);

      Console.WriteLine("{0,4} {1,9} {2,9} {3,9} {4,9}",
         "Year", "Q1", "Q2", "Q3", "Q4");
      Int32 firstYear    = quarterlyRevenue.GetLowerBound(0);
      Int32 lastYear     = quarterlyRevenue.GetUpperBound(0);
      Int32 firstQuarter = quarterlyRevenue.GetLowerBound(1);
      Int32 lastQuarter  = quarterlyRevenue.GetUpperBound(1);

      for (Int32 year = firstYear; year <= lastYear; year++) {
         Console.Write(year + "  ");
         for (Int32 quarter = firstQuarter; quarter <= lastQuarter; quarter++) {
            Console.Write("{0,9:C}  ", quarterlyRevenue[year, quarter]);
         }
         Console.WriteLine();
      }
   }
}

Agar ushbu kodni kompilyatsiya qilib ishga tushirsangiz, quyidagi natijani olasiz.

Year       Q1       Q2       Q3       Q4
2005    $0.00    $0.00    $0.00    $0.00
2006    $0.00    $0.00    $0.00    $0.00
2007    $0.00    $0.00    $0.00    $0.00
2008    $0.00    $0.00    $0.00    $0.00
2009    $0.00    $0.00    $0.00    $0.00

Massivlarning Ichki Tuzilishi

Ichki tarzda CLR aslida ikki xil massivni qo'llab-quvvatlaydi:

  • Pastki chegarasi 0 bo'lgan bir o'lchamli massivlar. Bu massivlar ba'zan SZ (single-dimensional, zero-based) massivlar yoki vektorlar deb ataladi.
  • Noma'lum pastki chegarali bir o'lchamli va ko'p o'lchamli massivlar.

Siz turli xil massivlarni quyidagi kodni bajarish orqali ko'rishingiz mumkin (natija kodning izohlarida ko'rsatilgan).

using System;

public sealed class Program {
      public static void Main() {
         Array a;

         // 1-o'lchamli, 0-bazali massiv yaratish, elementi yo'q
         a = new String[0];
         Console.WriteLine(a.GetType());    // "System.String[]"

         // 1-o'lchamli, 0-bazali massiv yaratish, elementi yo'q
         a = Array.CreateInstance(typeof(String),
            new Int32[] { 0 }, new Int32[] { 0 });
         Console.WriteLine(a.GetType());    // "System.String[]"

         // 1-o'lchamli, 1-bazali massiv yaratish, elementi yo'q
         a = Array.CreateInstance(typeof(String),
            new Int32[] { 0 }, new Int32[] { 1 });
         Console.WriteLine(a.GetType());    // "System.String[*]"  <-- QIZIQ!

         Console.WriteLine();

         // 2-o'lchamli, 0-bazali massiv yaratish, elementi yo'q
         a = new String[0, 0];
         Console.WriteLine(a.GetType());    // "System.String[,]"

         // 2-o'lchamli, 0-bazali massiv yaratish, elementi yo'q
         a = Array.CreateInstance(typeof(String),
            new Int32[] { 0, 0 }, new Int32[] { 0, 0 });
         Console.WriteLine(a.GetType());    // "System.String[,]"

         // 2-o'lchamli, 1-bazali massiv yaratish, elementi yo'q
         a = Array.CreateInstance(typeof(String),
            new Int32[] { 0, 0 }, new Int32[] { 1, 1 });
         Console.WriteLine(a.GetType());    // "System.String[,]"
      }
}

Har bir Console.WriteLine yonida natijani ko'rsatuvchi izoh bor. Bir o'lchamli massivlar uchun noldan boshlanadigan massivlar System.String[] tur nomini ko'rsatadi, 1-bazali massiv esa System.String[*] tur nomini ko'rsatadi. * belgisi CLR ga bu massiv noldan boshlanmaydigan ekanligini bildiradi. E'tibor bering, C# sizga String[*] turli o'zgaruvchi e'lon qilishga ruxsat bermaydi va shuning uchun bir o'lchamli, noldan boshlanmaydigan massivga kirish uchun C# sintaksisidan foydalanish mumkin emas. Garchi siz Array ning GetValue va SetValue metodlarini chaqirishingiz mumkin bo'lsa-da, bu metod chaqiruvi overhead tufayli sekin bo'ladi.

Ko'p o'lchamli massivlar uchun noldan boshlanadigan va 1-bazali massivlar bir xil tur nomini ko'rsatadi: System.String[,]. CLR barcha ko'p o'lchamli massivlarni noldan boshlanmaydigan dek ko'radi, garchi ular haqiqatan ham noldan boshlanmasa ham. Bu siz tur nomini System.String[*,*] sifatida ko'rsatilishini kutishingizga olib keladi; biroq, CLR ko'p o'lchamli massivlar uchun * belgilarini ishlatmaydi, chunki ular doim mavjud bo'ladi va yulduzchalar ko'pchilik dasturchilarni chalkashtirib yuborardi.

Bir o'lchamli, noldan boshlanadigan massiv elementlariga kirish biroz tezroq bo'ladi, noldan boshlanmaydigan bir o'lchamli massiv yoki ko'p o'lchamli massiv elementlariga kirishdan. Buning bir nechta sabablari bor. Birinchidan, bir o'lchamli, noldan boshlanadigan massivlarni manipulyatsiya qilish uchun maxsus IL ko'rsatmalari mavjud — masalan, newarr, ldelem, ldelema, ldlen va stelem — va bu maxsus IL ko'rsatmalari JIT kompilyatoriga optimallashtirilgan kod chiqarishga olib keladi. Masalan, JIT kompilyator massiv noldan boshlanadigan deb faraz qiladigan kod chiqaradi va bu element kirishda offset ayirmashlik kerak emasligini anglatadi. Ikkinchidan, odatiy vaziyatlarda JIT kompilyator indeks diapazon tekshirish kodini loop dan tashqariga ko'chirishi mumkin va uni faqat bir marta bajarilishini ta'minlaydi. Masalan, quyidagi keng tarqalgan kodni ko'ring.

using System;

public static class Program {
   public static void Main() {
      Int32[] a = new Int32[5];
      for(Int32 index = 0; index < a.Length; index++) {
         // a[index] bilan biror narsa qilish
      }
   }
}

Bu kod haqida birinchi e'tibor berish kerak bo'lgan narsa, for loop test ifodasi massivning Length xossasiga chaqiruvdir. Length xossa bo'lganligi sababli, uzunlikni so'rash aslida metod chaqiruvini ifodalaydi. Biroq, JIT kompilyator Length ni Array klassidagi xossa ekanligini biladi va JIT kompilyator aslida xossani faqat bir marta chaqiruvchi va natijani loop ning har bir iteratsiyasida tekshiriladigan vaqtinchalik o'zgaruvchida saqlaydigan kod yaratadi. Natija shundaki, JITlangan kod tezdir. Aslida, ba'zi dasturchilar JIT kompilyatorning qobiliyatlarini past baholashdi va massivning Length xossasini lokal o'zgaruvchida keshlash orqali JIT kompilyatorga yordam berishga harakat qilib "aqlli kod" yozishdi. Biroq, siz keltiradigan har qanday urinish ishlashga salbiy ta'sir qilishi va kodingizni o'qish qiyinroq qilishi deyarli muqarrar. Siz Length xossasiga chaqiruvni loop ning oldingi kodida lokal o'zgaruvchida keshlamasdan massivning Length xossasini qoldirsangiz yaxshiroq bo'ladi.

Oldingi kod haqida e'tibor berish kerak bo'lgan ikkinchi narsa, JIT kompilyator for loop 0 dan Length - 1 gacha bo'lgan massiv elementlariga kirishini biladi. Shuning uchun JIT kompilyator ish vaqtida barcha massiv murojatlari massivning haqiqiy diapazonida bo'lishini tekshiruvchi kod yaratadi. Aniqroq qilib aytganda, JIT kompilyator (0 >= a.GetLowerBound(0)) && ((Length - 1) <= a.GetUpperBound(0)) ni tekshiruvchi kod yaratadi. Bu tekshiruv loop dan oldin faqat bir marta amalga oshiriladi. Agar tekshiruv to'g'ri bo'lsa, JIT massiv kirishni har bir element uchun tekshiruvchi kodni loop ichiga qo'ymaydi. Bu massiv kirishini juda tez amalga oshirish imkonini beradi.

Afsuski, men ushbu bobda avvalroq aytib o'tganimdek, noldan boshlanmaydigan bir o'lchamli massiv yoki ko'p o'lchamli massiv elementlariga kirish bir o'lchamli, noldan boshlanadigan massivga qaraganda ancha sekinroq. Bu massiv turlari uchun JIT kompilyator indeks tekshiruvni looplardan tashqariga ko'chirmaydi, shuning uchun har bir massiv kirishi ko'rsatilgan indekslarni tekshiradi. Bundan tashqari, JIT kompilyator massivning pastki chegaralarini ko'rsatilgan indeksdan ayiruvchi kodni qo'shadi, bu ham kodni sekinlashtiradi, hatto noldan boshlanadigan ko'p o'lchamli massivdan foydalanayotgan bo'lsangiz ham. Shuning uchun, agar ishlash siz uchun tashvish bo'lsa, to'g'ri burchakli massiv o'rniga massivlar massividan (tishli massivdan) foydalanishni ko'rib chiqishingiz mumkin.

C# va CLR shuningdek unsafe (tekshirib bo'lmaydigan) kod yordamida massivga kirishga ruxsat beradi, bu aslida massiv elementiga kirishda indeks chegaralari tekshirishni o'chirib qo'yish texnikasi. Bu unsafe massiv manipulyatsiya texnikasi elementlari SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Single, Double, Decimal, Boolean, sanab o'tilgan tur yoki maydonlari yuqoridagi turlardan birortasi bo'lgan value tur strukturasi bo'lgan massivlar bilan ishlatilishi mumkin.

Bu to'g'ridan-to'g'ri xotira murojatini amalga oshirish imkonini beradigan juda kuchli xususiyat. Agar bu xotira murojatlari massiv chegaralaridan tashqarida bo'lsa, istisno tashlanmaydi; buning o'rniga siz xotirani buzasiz, tur xavfsizligini buzasiz va ehtimol xavfsizlik teshigini ochyapsiz! Shu sababli, unsafe kodni o'z ichiga olgan assembly to'liq ishonchga ega bo'lishi yoki kamida Security Permission da Skip Verification yoqilgan bo'lishi kerak.

Quyidagi C# kodi ikki o'lchamli massivga kirish uchun uchta texnikani (xavfsiz, tishli va unsafe) ko'rsatadi.

using System;
using System.Diagnostics;

public static class Program {
   private const Int32 c_numElements = 10000;

   public static void Main() {
      // Ikki o'lchamli massiv e'lon qilish
      Int32[,] a2Dim = new Int32[c_numElements, c_numElements];

      // Ikki o'lchamli massivni tishli massiv sifatida e'lon qilish (vektorlar vektori)
      Int32[][] aJagged = new Int32[c_numElements][];
      for (Int32 x = 0; x < c_numElements; x++)
         aJagged[x] = new Int32[c_numElements];

      // 1: Oddiy, xavfsiz texnika bilan massivning barcha elementlariga kirish
      Safe2DimArrayAccess(a2Dim);

      // 2: Tishli massiv texnikasi bilan massivning barcha elementlariga kirish
      SafeJaggedArrayAccess(aJagged);

      // 3: Unsafe texnika bilan massivning barcha elementlariga kirish
      Unsafe2DimArrayAccess(a2Dim);
   }

   private static Int32 Safe2DimArrayAccess(Int32[,] a) {
      Int32 sum = 0;
      for (Int32 x = 0; x < c_numElements; x++) {
         for (Int32 y = 0; y < c_numElements; y++) {
            sum += a[x, y];
         }
      }
      return sum;
   }

   private static Int32 SafeJaggedArrayAccess(Int32[][] a) {
      Int32 sum = 0;
      for (Int32 x = 0; x < c_numElements; x++) {
         for (Int32 y = 0; y < c_numElements; y++) {
            sum += a[x][y];
         }
      }
      return sum;
   }

   private static unsafe Int32 Unsafe2DimArrayAccess(Int32[,] a) {
      Int32 sum = 0;
      fixed (Int32* pi = a) {
         for (Int32 x = 0; x < c_numElements; x++) {
            Int32 baseOfDim = x * c_numElements;
            for (Int32 y = 0; y < c_numElements; y++) {
               sum += pi[baseOfDim + y];
            }
         }
      }
      return sum;
   }
}

Unsafe2DimArrayAccess metodi unsafe modifikatori bilan belgilangan, bu C# ning fixed ifodasi ishlatilishi uchun kerak. Bu kodni kompilyatsiya qilish uchun C# kompilyatorini chaqirishda /unsafe kalitini ko'rsatishingiz yoki Microsoft Visual Studio ning Loyiha Xossalari panelining Build tabidagi Allow Unsafe Code katakchasini belgilashingiz kerak.

Shubhasiz, unsafe texnikaning qachon va qaerda o'z kodingiz tomonidan ishlatilishi eng yaxshi bo'lishini bilish kerak, lekin u bilan uchta jiddiy salbiy tomon borligidan ehtiyot bo'ling:

  • Massiv elementlarini manipulyatsiya qiluvchi kod boshqa texnikalar yordamida elementlarni o'qish va yozishdan ko'ra murakkabroq, chunki siz C# ning fixed ifodasi va xotira-manzil hisoblashlaridan foydalanayapsiz.
  • Agar siz hisoblashda xatoga yo'l qo'ysangiz, massivning bir qismi bo'lmagan xotiraga kirasiz. Bu noto'g'ri hisoblash, xotira buzilishi, tur xavfsizligi buzilishi va potensial xavfsizlik teshigiga olib kelishi mumkin.
  • Potensial muammolar tufayli CLR kamaytilgan xavfsizlik muhitlarida (masalan, Microsoft Silverlight) unsafe kodni ishga tushirishni taqiqlaydi.

Xavfsiz Bo'lmagan Massiv Kirishi va Fixed-Size Massiv

Xavfsiz bo'lmagan (unsafe) massiv kirishi juda kuchlidir, chunki u sizga quyidagilarga kirishga imkon beradi:

  • Managed heap da joylashgan managed massiv obyektidagi elementlar (oldingi bo'limda ko'rsatilganidek).
  • Boshqarilmaydigan (unmanaged) heap da joylashgan massivdagi elementlar. 14-Bob "Belgilar, Satrlar va Matn bilan Ishlash" da SecureString misoli unsafe massiv kirishidan foydalanishni ko'rsatdi, System.Runtime.InteropServices.Marshal klassining SecureStringToCoTaskMemUnicode metodi tomonidan qaytarilgan massivga kirish orqali.
  • Oqim (thread) stekida joylashgan massivdagi elementlar.

Ishlash juda muhim bo'lgan hollarda, managed heap da managed massiv obyektini ajratishdan saqlanib, massivni oqim stekida ajratishingiz mumkin, buning uchun C# ning stackalloc ifodasi ishlatiladi (C ning alloca funksiyasiga juda o'xshaydi). stackalloc ifodasi faqat bir o'lchamli, noldan boshlanadigan value tur elementlari massivini yaratish uchun ishlatilishi mumkin va value tur hech qanday reference tur maydonlarini o'z ichiga olmasligi kerak. Aslida, siz buni unsafe ko'rsatkichlar yordamida manipulyatsiya qilishingiz mumkin bo'lgan xotira blokini ajratish deb tasavvur qilishingiz kerak va shuning uchun siz bu manzilni FCL metodlarining ko'pchiligiga uzata olmaysiz. Albatta, stekda ajratilgan xotira (massiv) metod tugaganda avtomatik ravishda ozod qilinadi; ishlash yaxshilanishi ham shu yerda. Bu xususiyatdan foydalanish C# kompilyatoriga /unsafe kalitini ko'rsatishni talab qiladi.

Quyidagi koddagi StackallocDemo metodi C# ning stackalloc ifodasi qanday ishlatilishini ko'rsatadi.

using System;

public static class Program {
   public static void Main() {
      StackallocDemo();
      InlineArrayDemo();
   }

   private static void StackallocDemo() {
      unsafe {
         const Int32 width = 20;
         Char* pc = stackalloc Char[width]; // Stekda massiv ajratadi

         String s = "Jeffrey Richter";     // 15 ta belgi

         for (Int32 index = 0; index < width; index++) {
            pc[width - index - 1] =
               (index < s.Length) ? s[index] : '.';
         }

         // Quyidagi satr ".....rethciR yerffeJ" ni ko'rsatadi
         Console.WriteLine(new String(pc, 0, width));
      }
   }

   private static void InlineArrayDemo() {
      unsafe {
         CharArray ca;                         // Stekda massiv ajratadi
         Int32 widthInBytes = sizeof(CharArray);
         Int32 width = widthInBytes / 2;

         String s = "Jeffrey Richter"; // 15 ta belgi

         for (Int32 index = 0; index < width; index++) {
            ca.Characters[width - index - 1] =
               (index < s.Length) ? s[index] : '.';
         }

         // Quyidagi satr ".....rethciR yerffeJ" ni ko'rsatadi
         Console.WriteLine(new String(ca.Characters, 0, width));
      }
   }
}

internal unsafe struct CharArray {
   // Bu massiv strukturaning ichiga joylashtirilgan
   public fixed Char Characters[20];
}

Odatda, massivlar reference turlar bo'lganligi sababli, strukturada aniqlangan massiv maydoni haqiqatan ham massiv uchun ko'rsatkichdir yoki havola; massivning o'zi strukturaning xotirasidan tashqarida yashaydi. Biroq, yuqoridagi koddagi CharArray strukturasi ko'rsatganidek, massivni to'g'ridan-to'g'ri strukturaning ichiga joylashtirish mumkin. Massivni to'g'ridan-to'g'ri struktura ichiga joylashtirish uchun bir nechta talablar mavjud:

  • Tur struktura (value tur) bo'lishi kerak; siz massivni klass (reference tur) ichiga joylashtirolmaysiz.
  • Maydon yoki uni aniqlayotgan struktura unsafe kalit so'zi bilan belgilanishi kerak.
  • Massiv maydoni fixed kalit so'zi bilan belgilanishi kerak.
  • Massiv bir o'lchamli va noldan boshlanadigan bo'lishi kerak.
  • Massivning element turi quyidagi turlardan biri bo'lishi kerak: Boolean, Char, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single yoki Double.

Inline massivlar odatda boshqarilmaydigan (unmanaged) kod bilan interoperatsiya qilinadigan stsenariylarda ishlatiladi, boshqarilmaydigan ma'lumot strukturasi ham inline massivga ega bo'lgan holatlarda. Biroq, inline massivlar boshqa stsenariyalarda ham ishlatilishi mumkin. Yuqoridagi kodda ko'rsatilgan InlineArrayDemo metodi inline massivni qanday ishlatish bo'yicha misolni taqdim qiladi. InlineArrayDemo metodi StackallocDemo metodi bilan bir xil funksiyani bajaradi; u buni faqat boshqacha usulda amalga oshiradi.

Muhim

Unsafe massiv kirishi xotirani bevosita manipulyatsiya qilishni anglatadi. Agar bu xotira murojatlari massiv chegaralaridan tashqarida bo'lsa, istisno tashlanmaydi — buning o'rniga siz xotirani buzasiz, tur xavfsizligini buzasiz va potensial xavfsizlik teshigini ochishingiz mumkin. Shu sababli, bu texnikani faqat haqiqatan ham ishlash muhim bo'lgan hollarda va ehtiyotkorlik bilan ishlating.

Xulosa

Ushbu bobda siz massivlar haqida chuqur bilim oldingiz: massiv elementlarini initsializatsiya qilish, massivlarni casting qilish, System.Array dan hosila bo'lish, IEnumerable, ICollection va IList interfeyslarini bilvosita amalga oshirish, massivlarni metodlarga uzatish va qaytarish, noldan boshqa pastki chegarali massivlar yaratish, massivlarning ichki tuzilishi va ishlash xususiyatlari, hamda unsafe massiv kirishi va fixed-size massivlar haqida o'rgandingiz. Eng muhim jihat — imkon qadar bir o'lchamli, noldan boshlanadigan massivlardan (vektorlardan) foydalaning, chunki ular eng tez ishlashni ta'minlaydi.