25-Bob: WinRT Komponentlari bilan Interop
CLR proyeksiyalari va WinRT komponent tur tizimi qoidalari, framework proyeksiyalari, C# da WinRT komponentlarini aniqlash
Windows 8 operatsion tizim funksionalligiga kirish imkonini beruvchi yangi klass kutubxonasi bilan birga keladi. Bu klass kutubxonasining rasmiy nomi Windows Runtime (WinRT) bo'lib, uning komponentlari WinRT tur tizimi yordamida foydalanish mumkin. WinRT Common Language Runtime (CLR) birinchi marta joriy etilganida ko'zlagan ko'plab maqsadlarga ega, masalan, ilova ishlab chiqishni soddalashtirish va turli dasturlash tillarida yozilgan kodni bir-biri bilan oson o'zaro aloqa qildirish imkonini yaratish. Xususan, Microsoft WinRT komponentlarini nativ C/C++ dan, JavaScript dan (Microsoft ning "Chakra" JavaScript virtual mashinasi yordamida), shuningdek C# va Visual Basic dan CLR orqali ishlatishni qo'llab-quvvatlaydi.
25-1 rasm Windows ning WinRT komponentlari tomonidan ochilgan xususiyat turlarini va ulardan foydalanish uchun Microsoft qo'llab-quvvatlaydigan turli tillarni ko'rsatadi. Nativ C/C++ bilan amalga oshirilgan ilovalar uchun dasturchi o'z kodini har bir CPU arxitekturasi (x86, x64 va ARM) uchun alohida kompilyatsiya qilishi kerak. Microsoft .NET Framework dasturchilar esa o'z kodini faqat bir marta Intermediate Language (IL) ga kompilyatsiya qilishlari kerak, so'ngra CLR uni xost mashinaning CPU siga mos nativ kodga kompilyatsiya qiladi. JavaScript dasturchilari esa manba kodini o'z ilovasiga jo'natadi va "Chakra" virtual mashinasi manba kodini tahlil qilib, xost mashinaning CPU siga mos nativ kodga kompilyatsiya qiladi. Boshqa kompaniyalar ham WinRT komponentlari bilan interop qiluvchi tillar va muhitlar yaratishi mumkin.
Windows Store ilovalari va desktop ilovalar ushbu WinRT komponentlaridan foydalanib operatsion tizim funksionalligidan foydalanishi mumkin. Bugungi kunda Windows tarkibida jo'natiladigan WinRT komponentlari soni .NET Framework klass kutubxonasiga nisbatan ancha kam. Biroq bu dizayn bo'yicha shunday, chunki komponentlar operatsion tizim eng yaxshi bajaradigan narsani ochishga qaratilgan: apparat ta'minotni abstraksiyalash va ilovalararo imkoniyatlarni dasturchi uchun taqdim etish. Shuning uchun WinRT komponentlarining aksariyati saqlash (storage), tarmoq (networking), grafika, media, xavfsizlik, oqimlar (threading) kabi xususiyatlarni ochadi. Boshqa yadro til xizmatlari (masalan, satr manipulyatsiyasi) va murakkabroq freymvorklar (masalan, LINQ) operatsion tizim tomonidan taqdim etilmaydi va uning o'rniga WinRT komponentlariga kirish uchun ishlatiladigan til tomonidan ta'minlanadi.
Windows Runtime tur tizimining muhim dizayn maqsadlaridan biri — dasturchilarga muvaffaqiyatli bo'lish imkonini berish, ya'ni ilova yozishda ular allaqachon tanish bo'lgan texnologiyalar, asboblar, amaliyotlar va konvensiyalardan foydalanish. Bunga erishish uchun ba'zi WinRT xususiyatlari tegishli dasturlash texnologiyalariga proyeksiya qilinadi. .NET Framework dasturchilari uchun ikki xil proyeksiya mavjud:
- CLR proyeksiyalari — CLR tomonidan yashirin ravishda bajariladigan moslashmalar bo'lib, odatda metadatani qayta talqin qilish bilan bog'liq. Keyingi bo'lim WinRT Komponent Tur Tizimi Qoidalari va CLR ushbu qoidalarni .NET Framework dasturchiga qanday proyeksiya qilishi haqida.
- Framework proyeksiyalari — Framework Class Library ga joriy etilgan yangi API lardan foydalanib, kodda aniq bajariladigan moslashmalar. Framework proyeksiyalari WinRT tur tizimi va CLR tur tizimi o'rtasidagi nomutanosiblikni hal qilish uchun kerak bo'lganda va CLR buni yashirin qilishi juda murakkab bo'lganda ishlatiladi. Framework proyeksiyalari ushbu bobda keyinroq muhokama qilinadi.
CLR Proyeksiyalari va WinRT Komponent Tur Tizimi Qoidalari
WinRT komponentlari CLR tur tizimiga o'xshash tur tizimiga muvofiq keladi. CLR WinRT turini ko'rganida, odatda bu turdan CLR ning oddiy COM interop texnologiyalari orqali foydalanish imkonini beradi. Lekin ba'zi hollarda CLR WinRT turini yashiradi (uni dinamik ravishda private qilib) va keyin turni boshqa tur orqali ochadi. Ichki tarzda CLR muayyan turlar uchun (metadata orqali) qidiradi va bu turlarni Framework Class Library dagi turlarga moslashtiradi. CLR yashirin ravishda proyeksiya qiladigan WinRT turlarining to'liq ro'yxati uchun http://msdn.microsoft.com/en-us/library/windows/apps/hh995050.aspx ga qarang.
WinRT Tur Tizimi Asosiy Tushunchalari
WinRT tur tizimi CLR tur tizimiga qaraganda unchalik boy emas. Quyidagi ro'yxat WinRT tur tizimining asosiy tushunchalarini va CLR loyihalarning ularga qanday munosabatda bo'lishini tavsiflaydi:
-
Fayl nomlari va nomlar fazolari (namespaces) —
.winmdfaylining nomi WinRT komponentlarini o'z ichiga olgan nomlar fazosiga mos kelishi kerak. Masalan,Wintellectnomli faylWintellect.WindowsStoreyokiWintellect.WindowsStoreichki nomlar fazosida aniqlangan WinRT komponentlariga ega bo'lishi kerak. Windows fayl tizimi katta-kichik harflarga sezgir emasligi sababli, faqat registr bilan farqlanadigan nomlar fazolariga ruxsat berilmaydi. Shuningdek, WinRT komponent nomlar fazosi bilan bir xil nomga ega bo'lolmaydi. -
Umumiy asos tur (Common base type) — WinRT komponentlari umumiy asos klassni bo'lishmaydi. CLR WinRT turini proyeksiya qilganida, WinRT turi go'yo
System.Objectdan hosila bo'lgandek ko'rinadi va shuning uchun barcha WinRT turlariToString,GetHashCode,EqualsvaGetTypekabi umumiy (public) metodlarni meros qilib oladi. Shunday qilib, C# da WinRT obyektidan foydalanganingizda, obyektSystem.Objectdan hosila bo'lgandek ko'rinadi va siz WinRT obyektlarini kodingiz bo'ylab uzatishingiz hamda ularning "meros bo'lgan" metodlarini, masalanToStringni chaqirishingiz mumkin. - Asosiy ma'lumot turlari (Core data types) — WinRT tur tizimi Boolean, imzosiz bayt, 16-bit, 32-bit va 64-bit imzolangan va imzosiz butun sonlar, bitta va ikki aniqlikdagi suzuvchi nuqtali sonlar, 16-bit belgi (character), satrlar (strings) va void kabi asosiy ma'lumot turlarini qo'llab-quvvatlaydi.1 CLR dagi kabi, boshqa barcha ma'lumot turlari ushbu asosiy turlardan tashkil topadi.
- Klasslar (Classes) — WinRT obyektga yo'naltirilgan tur tizimidir, ya'ni WinRT komponentlari ma'lumot abstraksiyasi, meros olish va polimorfizmni qo'llab-quvvatlaydi.2 Biroq, ba'zi tillar (masalan, JavaScript) tur meros olishni qo'llab-quvvatlamaydi va ushbu tillarga moslash uchun deyarli hech bir WinRT komponent meros olishdan foydalanmaydi. Bu shuni anglatadiki, ular polimorfizmdan ham foydalanmaydi. Amalda, faqat JavaScript bo'lmagan tillar tomonidan iste'mol qilinadigan WinRT komponentlari meros olish va polimorfizmni ishlatadi. Windows bilan birga keladigan WinRT komponentlari orasidan faqat XAML komponentlari (foydalanuvchi interfeyslari yaratish uchun) meros olish va polimorfizmdan foydalanadi. JavaScript da yozilgan ilovalar o'z foydalanuvchi interfeysi uchun HTML va CSS ishlatadi.
-
Strukturalar (Structures) — WinRT strukturalarni (value turlar) qo'llab-quvvatlaydi va ularning instansiyalari COM interoperabillik chegarasi orqali qiymat bo'yicha marshallashtiriladi. CLR qiymat turlaridan farqli o'laroq, WinRT strukturalari faqat asosiy ma'lumot turlari yoki boshqa WinRT strukturalari bo'lgan public maydonlarga ega bo'lishi mumkin.3 Bundan tashqari, WinRT strukturalarida konstruktorlar yoki yordamchi metodlar bo'lishi mumkin emas. Qulaylik uchun CLR ba'zi operatsion tizim WinRT strukturalarini ba'zi nativ CLR turlari sifatida proyeksiya qiladi, ular esa konstruktorlar va yordamchi metodlarni taklif qiladi. Ushbu proyeksiya qilingan turlar CLR dasturchiga tabiiyroq his qildiriladi. Misollar qatoriga
Windows.Foundationnomlar fazosida aniqlanganPoint,Rect,SizevaTimeSpanstrukturalari kiradi. -
Nullable strukturalar — WinRT API lari nullable strukturalarni (value turlar) ochishi mumkin. CLR WinRT ning
Windows.Foundation.IReference<T>interfeysini CLR ningSystem.Nullable<T>turi sifatida proyeksiya qiladi. -
Enumeratsiyalar (Enumerations) — Enumeratsiya qiymati oddiy imzolangan yoki imzosiz 32-bit butun son sifatida uzatiladi. C# da enumeratsiya turini aniqlasangiz, uning asosiy turi
intyokiuintbo'lishi kerak. Shuningdek, imzolangan 32-bit butun sonli enumlar alohida (diskreet) qiymatlar deb hisoblanadi, imzosiz 32-bit enumlar esa bayroqlar (flags) sifatida OR operatsiyasi bilan birlashtirish mumkin deb hisoblanadi. - Interfeyslar (Interfaces) — WinRT interfeysining a'zolari parametrlar va qaytarish turlari uchun faqat WinRT-mos turlarni ko'rsatishi kerak.
-
Metodlar (Methods) — WinRT metod qayta yuklash (overloading) ni cheklangan ravishda qo'llab-quvvatlaydi. Xususan, JavaScript dinamik tipizatsiyaga ega bo'lganligi sababli, u parametrlarning turlari bo'yicha emas, faqat parametrlar soni bo'yicha farqlanadigan metodlarni ajrata oladi. Masalan, JavaScript son (number) ni satr kutayotgan metodga bemalol uzatadi. Biroq, JavaScript bitta parametrli va ikki parametrli metodlarni ajrata oladi. Bundan tashqari, WinRT operator overload metodlarini va standart argument qiymatlarini qo'llab-quvvatlamaydi. Argumentlar faqat kirish (in) yoki chiqish (out) bo'lishi mumkin, lekin hech qachon
ref(in va out birga) bo'lmaydi. Qo'shimcha ma'lumot uchun keyingi punktdagi "Massivlar" ga qarang. - Xususiyatlar (Properties) — WinRT xususiyatlari o'z ma'lumot turi uchun faqat WinRT-mos turlarni ko'rsatishi kerak. WinRT parametrli xususiyatlarni (indexerlar) yoki faqat yozish mumkin (write-only) xususiyatlarni qo'llab-quvvatlamaydi.
-
Delegatlar (Delegates) — WinRT delegat turlari parametr turlari va qaytarish turlari uchun faqat WinRT komponentlarini ko'rsatishi kerak. WinRT komponentiga delegat uzatilganda, delegat obyekti CCW (COM Callable Wrapper) bilan o'raladi va WinRT komponent uni iste'mol qilguncha garbage collect qilinmaydi. WinRT delegatlari
BeginInvokevaEndInvokemetodlariga ega emas. -
Hodisalar (Events) — WinRT komponentlari WinRT delegat turidan foydalanib hodisalarni ochishi mumkin. Ko'pchilik WinRT komponentlari sealed bo'lganligi sababli (meros olish yo'q), WinRT
TypedEventHandlerdelegatini aniqlaydi, unda jo'natuvchi (sender) parametri generic tur (CLR dagiSystem.Objecto'rniga):public delegate void TypedEventHandler<TSender, TResult>(TSender sender, TResult args);Shuningdek, CLR proyeksiya qiladigan
Windows.Foundation.EventHandler<T>WinRT delegat turi ham mavjud bo'lib, u .NET Framework ning tanishSystem.EventHandler<T>delegat turi sifatida proyeksiya qilinadi. -
Istisnolar (Exceptions) — Ichki tarzda, WinRT komponentlari, COM komponentlari singari, o'z holatini maxsus semantikaga ega 32-bit butun son bo'lgan HRESULT qiymatlari orqali bildiradi. CLR WinRT HRESULT qiymatlarini
Windows.Foundation.HResultturidagi istisno obyektlari sifatida proyeksiya qiladi. WinRT API muvaffaqiyatsiz HRESULT qiymatini qaytarganida, CLR mosExceptiondan hosila klasning instansiyasini tashlaydi. Masalan,0x8007000e(E_OUTOFMEMORY) HRESULTSystem.OutOfMemoryExceptionga moslashtiriladi. Boshqa HRESULT qiymatlariHResultxususiyatida HRESULT qiymatini o'z ichiga olganSystem.Exceptionobyektini tashlashga sabab bo'ladi. C# da amalga oshirilgan WinRT komponent istalgan turdagi istisnoni tashlashi mumkin va CLR uni tegishli HRESULT qiymatiga aylantiradi. HRESULT qiymatini to'liq boshqarish uchun istisno obyektiningHResultxususiyatiga muayyan HRESULT qiymatini belgilang va keyin obyektni tashlang. -
Satrlar (Strings) — Albatta, siz WinRT va CLR tur tizimlari o'rtasida o'zgarmas (immutable) satrlarni uzatishingiz mumkin. Biroq, WinRT tur tizimi satrning
nullqiymatga ega bo'lishiga ruxsat bermaydi. Agar siz WinRT API ning satr parametriganulluzatsangiz, CLR buni aniqlaydi vaArgumentNullExceptiontashlaydi; uning o'rniga WinRT API ga bo'sh satr uzatish uchunString.Emptydan foydalaning. Satrlar WinRT API ga havola orqali uzatiladi; ular yo'lda pin-lanadi va qaytishda unpin qilinadi. WinRT API dan CLR ga qaytarilgan satrlar doimo nusxalanadi. CLR satr massivini (String[]) WinRT API ga yoki undan uzatishda massivning barcha satr elementlari bilan nusxasi yaratiladi va nusxa boshqa tomonga uzatiladi yoki qaytariladi. -
Sanalar va vaqtlar (Dates and Times) — WinRT
Windows.Foundation.DateTimestrukturasi UTC sana/vaqtni ifodalaydi. CLR WinRTDateTimestrukturasini .NET Framework ningSystem.DateTimeOffsetstrukturasi sifatida proyeksiya qiladi, chunkiDateTimeOffset.NET Framework ningSystem.DateTimestrukturasiga nisbatan afzalroq. CLR WinRT dan qaytarilgan UTC vaqtni hosil bo'lganDateTimeOffsetinstansiyasida mahalliy vaqtga aylantiradi. CLR WinRT API gaDateTimeOffsetni UTC vaqt sifatida uzatadi. -
URI lar — CLR WinRT
Windows.Foundation.Uriturini .NET Framework ningSystem.Urituri sifatida proyeksiya qiladi. .NET Framework Uri ni WinRT API ga uzatishda, agar URI nisbiy bo'lsa, CLRArgumentExceptiontashlaydi; WinRT faqat absolyut URI larni qo'llab-quvvatlaydi. URI lar doimo interop chegarasi orqali nusxalanadi. -
IClosable/IDisposable — CLR WinRT ning
Windows.Foundation.IClosableinterfeysini (faqatClosemetodi bor) .NET Framework ningSystem.IDisposableinterfeysi (Disposemetodi bilan) sifatida proyeksiya qiladi. Shu yerda e'tiborga olish kerak bo'lgan muhim narsa shundaki, barcha WinRT API lar I/O amallarini asinxron bajaradi.IClosableinterfeysiClosedeb ataladi vaCloseAsyncemas, shuning uchunClosemetodi hech qanday I/O amalni bajarmaydi. Bu .NET Framework daDisposeodatda qanday ishlashidan semantik jihatdan farq qiladi. .NET Framework da amalga oshirilgan turlar uchunDisposeni chaqirish I/O amalni bajarishi mumkin va aslida buferlangan ma'lumotlarni qurilmani yopishdan oldin yozishga sabab bo'lishi ko'p uchraydi. C# kodi WinRT turidaDisposeni chaqirganida, I/O (masalan, buferlangan ma'lumotlarni yozish) bajarilmasligi mumkin va ma'lumot yo'qotilishi ehtimoli bor. Ma'lumot yo'qolishining oldini olish uchun WinRT chiqish oqimlarini o'raydigan komponentlar uchun yopishdan oldin aniq flush chaqiruvlarini amalga oshirishingiz kerak. Masalan,DataWriterdan foydalanganda, doim uningStoreAsyncmetodini chaqirishingiz kerak. - Massivlar (Arrays) — WinRT API lari bir o'lchamli, noldan boshlanadigan massivlarni qo'llab-quvvatlaydi. WinRT massivning elementlarini metodga kirish yoki chiqish sifatida marshallash mumkin; hech qachon kirish va chiqish birga emas. Bu sababli, siz massivni WinRT API ga uzatib, API massiv elementlarini o'zgartirib, keyin API qaytganidan keyin o'zgartirilgan elementlarga kirish imkoniga ega emas.4 Biroq, bu kontrakt faol ravishda tekshirilmaydi, shuning uchun ba'zi proyeksiyalar massiv tarkibini ikki yo'nalishda ham marshallashi mumkin. Bu odatda ishlash yaxshilanishi tufayli tabiiy ravishda sodir bo'ladi. Masalan, agar massiv strukturalar o'z ichiga olsa, CLR oddiy massivni pin-laydi, uni WinRT API ga uzatadi va qaytishda unpin qiladi. Amalda, massiv tarkibi uzatiladi, WinRT API tarkibni o'zgartirishi mumkin va o'zgartirilgan tarkib qaytariladi. Biroq, bu misolda WinRT API kontraktni buzmoqda va bu xatti-harakatning ishlashi kafolatlanmagan. Aslida, agar API jarayondan tashqarida (out-of-process) ishlaydigan WinRT komponentida chaqirilsa, ishlamaydi.
- Kolleksiyalar (Collections) — WinRT API ga kolleksiya uzatilganda, CLR kolleksiya obyektini CCW bilan o'raydi va CCW ga havolani WinRT API ga uzatadi. WinRT kodi CCW dagi a'zoni chaqirganida, chaqiruvchi oqim interop chegarasini kesib o'tadi va bu ishlash zarar yetkazadi. Massivlardan farqli o'laroq, bu shuni anglatadiki, WinRT API ga kolleksiya uzatish API ga kolleksiyani joyida o'zgartirish imkonini beradi va kolleksiya elementlarining nusxalari yaratilmaydi. 25-1 jadval WinRT kolleksiya interfeyslarini va CLR ularni qanday proyeksiya qilishini ko'rsatadi.
1 Imzolangan bayt (signed byte) WinRT tomonidan qo'llab-quvvatlanmaydi.
2 Ma'lumot abstraksiyasi amalda kuchli qo'llanadi, chunki WinRT klasslari public maydonlarga ega bo'lishi mumkin emas.
3 Enumeratsiyalar ham ruxsat etiladi, chunki ular aslida faqat 32-bit butun sonlar.
4 Bu shuni anglatadiki, System.Array.Sort kabi API ga ega bo'lolmaysiz. Qiziq, barcha tillar (C, C++, C#, Visual Basic va JavaScript) massiv elementlarini kirish va chiqishda uzatishni qo'llab-quvvatlaydi, lekin WinRT tur tizimi bunga ruxsat bermaydi.
25-1 Jadval: WinRT Kolleksiya Interfeyslari va Proyeksiya qilingan CLR Kolleksiya Turlari
| WinRT Kolleksiya Turi ( Windows.Foundation.Collections nomlar fazosi) |
Proyeksiya qilingan CLR Kolleksiya Turi ( System.Collections.Generic nomlar fazosi) |
|---|---|
IIterable<T> | IEnumerable<T> |
IVector<T> | IList<T> |
IVectorView<T> | IReadOnlyList<T> |
IMap<K, V> | IDictionary<TKey, TValue> |
IMapView<K, V> | IReadOnlyDictionary<TKey, TValue> |
IKeyValuePair<K, V> | KeyValuePair<TKey, TValue> |
Yuqoridagi jadvaldan ko'rinib turibdiki, CLR jamoasi WinRT tur tizimi va CLR tur tizimi o'rtasidagi interop ni iloji boricha uzluksiz qilish uchun ko'p ish qilgan,5 shunda boshqariladigan kod dasturchilari o'z kodlarida WinRT komponentlaridan osongina foydalanishlari mumkin.
5 Ko'proq bilish uchun http://msdn.microsoft.com/en-us/library/windows/apps/hh995050.aspx ga o'ting va CLRandtheWindowsRuntime.docx hujjatini yuklab oling.
Framework Proyeksiyalari
CLR WinRT turini .NET Framework dasturchiga yashirin proyeksiya qila olmasa, dasturchi framework proyeksiyalaridan foydalanib, aniq moslashma qilishi kerak. Uchta asosiy texnologiya mavjud bo'lib, ularda framework proyeksiyalari zarur: asinxron dasturlash, WinRT oqimlari va .NET Framework oqimlari o'rtasidagi interop, va CLR bilan WinRT o'rtasida xom ma'lumot bloklarini uzatish. Ushbu uchta framework proyeksiya ushbu bobning keyingi uchta bo'limida muhokama qilinadi. Ko'plab ilovalar ushbu texnologiyalardan foydalanishi kerak bo'lganligi sababli, ularni yaxshi tushunishingiz va samarali ishlatishingiz muhim.
Asinxron WinRT API larni .NET Koddan Chaqirish
Oqim sinxron I/O amalini bajarganida, oqim noaniq muddatga bloklashi mumkin. GUI oqimi sinxron I/O amali tugashini kutganida, ilovaning foydalanuvchi interfeysi foydalanuvchi kiritishiga — teginish, sichqoncha va stylus hodisalariga javob berishni to'xtatadi, bu foydalanuvchini bezovta qiladi. Javob bermaydigan ilovalarning oldini olish uchun I/O amallarini bajaradigan WinRT komponentlari funksionallikni asinxron API lar orqali ochadi. Aslida, CPU amalini bajaradigan WinRT komponentlari ham, agar CPU amali 50 millisekunddan ko'proq vaqt olishi mumkin bo'lsa, bu funksionallikni asinxron API lar orqali ochadi. Javob beruvchi ilovalar yaratish haqida ko'proq ma'lumot uchun ushbu kitobning V-Qismi, "Threading" ga qarang.
WinRT API larning aksariyati asinxron bo'lganligi sababli, ular bilan samarali ishlash uchun ularni C# dan qanday chaqirishni tushunish kerak. Buni tushunish uchun quyidagi kodni ko'rib chiqing:
public void WinRTAsyncIntro() {
IAsyncOperation<StorageFile> asyncOp = KnownFolders.MusicLibrary.GetFileAsync("Song.mp3");
asyncOp.Completed = OpCompleted;
// Ixtiyoriy: keyinroq asyncOp.Cancel() ni chaqiring
}
// ESLATMA: Callback metodi GUI yoki thread pool oqimi orqali bajariladi:
private void OpCompleted(IAsyncOperation<StorageFile> asyncOp, AsyncStatus status) {
switch (status) {
case AsyncStatus.Completed: // Natijani qayta ishlash
StorageFile file = asyncOp.GetResults(); /* Tugallandi... */ break;
case AsyncStatus.Canceled: // Bekor qilishni qayta ishlash
/* Bekor qilindi... */ break;
case AsyncStatus.Error: // Istisnoni qayta ishlash
Exception exception = asyncOp.ErrorCode; /* Xatolik... */ break;
}
asyncOp.Close();
}
WinRTAsyncIntro metodi WinRT GetFileAsync metodini chaqirib, foydalanuvchining musiqa kutubxonasida fayl qidiradi. Asinxron amallarni bajaradigan barcha WinRT API lari Async qo'shimchasi bilan nomlanadi va ularning barchasi WinRT IAsyncXxx interfeysini amalga oshiruvchi obyektni qaytaradi; bu misolda TResult WinRT StorageFile turi bo'lgan IAsyncOperation<TResult> interfeysi. Ushbu obyekt, havola asyncOp o'zgaruvchisiga joylashtirilgan, kutilayotgan asinxron amalni ifodalaydi. Kodingiz kutilayotgan amal tugaganida qandaydir tarzda xabardor bo'lishi kerak. Buning uchun callback metodini (OpCompleted) amalga oshirishingiz, delegat yaratishingiz va delegatni asyncOp ning Completed xususiyatiga tayinlashingiz kerak. Endi amal tugaganda, callback metodi biror oqim orqali (lekin majburiy GUI oqimi emas) chaqiriladi. Agar amal Completed xususiyatiga delegat tayinlanishidan oldin tugagan bo'lsa, tizim callback ni iloji boricha tezroq chaqiradi. Boshqacha aytganda, bu yerda poyga holati (race condition) mavjud, lekin IAsyncXxx interfeysini amalga oshiruvchi obyekt poygani siz uchun hal qiladi va kodingiz to'g'ri ishlashini ta'minlaydi.
WinRTAsyncIntro metodi oxirida qayd etilganidek, agar kutilayotgan amalni bekor qilmoqchi bo'lsangiz, barcha IAsyncXxx interfeyslari taklif qiladigan Cancel metodini ixtiyoriy ravishda chaqirishingiz mumkin. Barcha asinxron amallar uchta mumkin bo'lgan sabablardan biri tufayli tugaydi: amal muvaffaqiyatli tugaydi, amal aniq bekor qilinadi yoki amal xatolik bilan tugaydi. Amal quyidagi sabablardan biri tufayli tugaganida, tizim callback metodini chaqiradi, unga asl XxxAsync metodi qaytargan obyektga havola va AsyncStatus ni uzatadi. Mening OpCompleted metodimda men status parametrini tekshiraman va muvaffaqiyatli tugash natijasini qayta ishlayman, aniq bekor qilishni boshqaraman yoki xatolikni boshqaraman.6 Shuningdek, e'tibor bering, amal tugaganini qayta ishlagandan so'ng, IAsyncXxx interfeys obyekti o'zining Close metodini chaqirib tozalanishi kerak.
6 IAsyncInfo interfeysi callback metodining status parametriga uzatiladigan bir xil qiymatni o'z ichiga olgan Status xususiyatini taklif qiladi. Parametr qiymat bo'yicha uzatilganligi sababli, parametrga murojaat qilish ilovangiz ishlashi uchun yaxshiroqdir, IAsyncInfo ning Status xususiyatini so'rash o'rniga, chunki xususiyatni so'rash WinRT API ni RCW orqali chaqiradi.
25-2 rasm turli IAsyncXxx interfeyslarini ko'rsatadi. To'rtta asosiy interfeys IAsyncInfo interfeysidan hosila bo'ladi. Ikkita IAsyncAction interfeysi sizga amal tugaganini bilish usulini beradi, lekin ularning amallari qaytarish qiymati yo'q (ularning GetResults metodlari void qaytarish turiga ega). Ikkita IAsyncOperation interfeysi ham sizga amal tugaganini bilish usulini beradi va shuningdek qaytarish qiymatini olish imkonini beradi (ularning GetResults metodlari generic TResult qaytarish turiga ega).
Ikkita IAsyncXxxWithProgress interfeysi kodingizga asinxron amal ishlayotganida davriy ravishda progress yangiliklari olish imkonini beradi. Aksariyat asinxron amallar progress yangiliklari taklif qilmaydi, lekin ba'zilari (masalan, fon rejimidagi yuklab olish va jo'natish) taklif qiladi. Davriy progress yangiliklari olish uchun kodingizda boshqa callback metodi aniqlang, unga murojaat qiluvchi delegat yarating va delegatni IAsyncXxxWithProgress obyektining Progress xususiyatiga tayinlang. Callback metodingiz chaqirilganda, unga turi generic TProgress turiga mos argument uzatiladi.
.NET Framework da biz System.Threading.Tasks nomlar fazosidagi turlardan asinxron amallarni soddalashtirish uchun foydalanamiz. Men ushbu turlarni va ulardan foydalanishni 27-Bob, "Hisoblashga Bog'langan Asinxron Amallar" da va 28-Bob, "I/O ga Bog'langan Asinxron Amallar" da tushuntiraman. Bundan tashqari, C# async va await kalit so'zlarini taklif qiladi, ular sizga ketma-ket dasturlash modelidan foydalanib asinxron amallarni bajarish imkonini beradi va kodingizni sezilarli darajada soddalashtiradi.
Quyidagi kod yuqorida eslatilgan WinRTAsyncIntro metodining qayta yozilishi. Biroq, bu versiya .NET Framework tomonidan ta'minlangan ba'zi extension metodlardan foydalanadi, ular WinRT asinxron dasturlash modelini qulayroq C# dasturlash modeliga aylantiradi.
using System; // WindowsRuntimeSystemExtensions dagi extension
// metodlar uchun kerak
.
.
.
public async void WinRTAsyncIntro() {
try {
StorageFile file = await KnownFolders.MusicLibrary.GetFileAsync("Song.mp3");
/* Tugallandi... */
}
catch (OperationCanceledException) { /* Bekor qilindi... */ }
catch (SomeOtherException ex) { /* Xatolik... */ }
}
Bu yerda sodir bo'layotgan narsa shundaki, C# ning await operatoridan foydalanish kompilyatorga GetFileAsync dan qaytarilgan IAsyncOperation<StorageFile> interfeysida GetAwaiter metodini qidirish imkonini beradi. Bu interfeys GetAwaiter metodini taqdim etmaydi va shuning uchun kompilyator extension metodni qidiradi. Yaxshiyamki, .NET Framework jamoasi System.Runtime.WindowsRuntime.dll da bir nechta extension metodlarni ta'minlagan bo'lib, ular WinRT ning IAsyncXxx interfeyslaridan biriga ega bo'lganingizda chaqirilishi mumkin.
namespace System {
public static class WindowsRuntimeSystemExtensions {
public static TaskAwaiter GetAwaiter(this IAsyncAction source);
public static TaskAwaiter GetAwaiter<TProgress>(this IAsyncActionWithProgress<TProgress>
source);
public static TaskAwaiter<TResult> GetAwaiter<TResult>(this IAsyncOperation<TResult>
source);
public static TaskAwaiter<TResult> GetAwaiter<TResult, TProgress>(
this IAsyncOperationWithProgress<TResult, TProgress> source);
}
}
Ichki tarzda, bu metodlarning barchasi TaskCompletionSource ni yaratadi va IAsyncXxx obyektiga asinxron amal tugaganida TaskCompletionSource ning yakuniy holatini o'rnatadigan callback ni chaqirishni buyuradi. Ushbu extension metodlardan qaytarilgan TaskAwaiter obyekti oxir-oqibat C# await qiladigan narsadir. Asinxron amal tugaganida, TaskAwaiter obyekti kodni asl oqim bilan bog'langan SynchronizationContext orqali davom etishini ta'minlaydi (28-Bobda muhokama qilingan). Keyin oqim C# kompilyatori yaratgan kodni bajaradi, bu kod TaskCompletionSource ning Task xususiyatining Result xususiyatini so'raydi, bu natijani (StorageFile) qaytaradi, bekor qilish holatida OperationCanceledException tashlaydi yoki xatolik yuz berganda boshqa istisnoni tashlaydi.
Yuqorida ko'rsatilgan narsa asinxron WinRT API ni chaqirish va uning natijasini aniqlashning keng tarqalgan stsenariyi. Lekin oldingi kodda men bekor qilish sodir bo'lganligini qanday aniqlash mumkinligini ko'rsatdim, amalni haqiqatan ham qanday bekor qilishni ko'rsatmadim. Bundan tashqari, progress yangilanishlarni qanday boshqarishni ko'rsatmadim. Bekor qilish va progress yangilanishlarni to'g'ri boshqarish uchun kompilyator avtomatik ravishda GetAwaiter extension metodlaridan birini chaqirishi o'rniga, WindowsRuntimeSystemExtensions klassi shuningdek aniqlaydigan AsTask extension metodlaridan birini aniq chaqirishingiz kerak.
namespace System {
public static class WindowsRuntimeSystemExtensions {
public static Task AsTask<TProgress>(this IAsyncActionWithProgress<TProgress> source,
CancellationToken cancellationToken, IProgress<TProgress> progress);
public static Task<TResult> AsTask<TResult, TProgress>(
this IAsyncOperationWithProgress<TResult, TProgress> source,
CancellationToken cancellationToken, IProgress<TProgress> progress);
// Soddaroq overloadlar bu yerda ko'rsatilmagan
}
}
Endi sizga to'liq rasmni ko'rsataman. Quyida asinxron WinRT API ni bekor qilish va progress bilan to'liq qo'llab-quvvatlab chaqirish usuli:
using System; // WindowsRuntimeSystemExtensions ning AsTask uchun
using System.Threading; // CancellationTokenSource uchun
internal sealed class MyClass {
private CancellationTokenSource m_cts = new CancellationTokenSource();
// ESLATMA: GUI oqimidan chaqirilsa, barcha kod GUI oqimi orqali bajariladi:
private async void MappingWinRTAsyncToDotNet(WinRTType someWinRTObj) {
try {
// XxxAsync IAsyncOperationWithProgress<IBuffer, UInt32> qaytaradi deb faraz qiling
IBuffer result = await someWinRTObj.XxxAsync(...)
.AsTask(m_cts.Token, new Progress<UInt32>(ProgressReport));
/* Tugallandi... */
}
catch (OperationCanceledException) { /* Bekor qilindi... */ }
catch (SomeOtherException) { /* Xatolik... */ }
}
private void ProgressReport(UInt32 progress) { /* Progress yangilash... */ }
public void Cancel() { m_cts.Cancel(); } // Keyinroq chaqiriladi
}
Ba'zi o'quvchilar ushbu AsTask metodlari ichki tarzda WinRT IAsyncXxx ni .NET Framework Task ga qanday aylantirishi va oxir-oqibat await qilinishini tushunishni xohlashlarini bilaman. Quyidagi kod eng murakkab AsTask metodining ichki tarzda qanday amalga oshirilganini ko'rsatadi. Soddaroq overloadlar, albatta, bundan oddiyroq.
public static Task<TResult> AsTask<TResult, TProgress>(
this IAsyncOperationWithProgress<TResult, TProgress> asyncOp,
CancellationToken ct = default(CancellationToken),
IProgress<TProgress> progress = null) {
// CancellationTokenSource bekor qilinganda, asinxron amalni bekor qilish
ct.Register(() => asyncOp.Cancel());
// Asinxron amal progress xabar qilganda, progress callback ga xabar berish
asyncOp.Progress = (asyncInfo, p) => progress.Report(p);
// Bu TaskCompletionSource asinxron amalning tugashini kuzatadi
var tcs = new TaskCompletionSource<TResult>();
// Asinxron amal tugaganda, TaskCompletionSource ga xabar berish
// TaskCompletionSource ni await qiluvchi kod bu sodir bo'lganda boshqaruvni qaytaradi
asyncOp.Completed = (asyncOp2, asyncStatus) => {
switch (asyncStatus) {
case AsyncStatus.Completed: tcs.SetResult(asyncOp2.GetResults()); break;
case AsyncStatus.Canceled: tcs.SetCanceled(); break;
case AsyncStatus.Error: tcs.SetException(asyncOp2.ErrorCode); break;
}
};
// Chaqiruvchi kod ushbu qaytarilgan Task ni await qilganida, u GetAwaiter ni chaqiradi,
// bu Task atrofida SynchronizationContext ni o'raydi va tugash
// SynchronizationContext obyektining kontekstida sodir bo'lishini ta'minlaydi
return tcs.Task;
}
WinRT Oqimlari (Streams) va .NET Oqimlari O'rtasidagi Interop
Ko'plab .NET Framework klasslari System.IO.Stream dan hosila bo'lgan turlarda ishlaydi, masalan, serializatsiya va LINQ to XML. WinRT ning IStorageFile yoki IStorageFolder interfeyslarini amalga oshiruvchi WinRT obyektidan Stream hosila turini talab qiluvchi .NET Framework klassi bilan foydalanish uchun, System.IO.WindowsRuntimeStorageExtensions klassida aniqlangan extension metodlardan foydalanamiz.
namespace System.IO { // System.Runtime.WindowsRuntime.dll da aniqlangan
public static class WindowsRuntimeStorageExtensions {
public static Task<Stream> OpenStreamForReadAsync(this IStorageFile file);
public static Task<Stream> OpenStreamForWriteAsync(this IStorageFile file);
public static Task<Stream> OpenStreamForReadAsync(this IStorageFolder rootDirectory,
String relativePath);
public static Task<Stream> OpenStreamForWriteAsync(this IStorageFolder rootDirectory,
String relativePath, CreationCollisionOption creationCollisionOption);
}
}
Quyida WinRT StorageFile ni ochib, uning tarkibini .NET Framework XElement obyektiga o'qish uchun ushbu extension metodlardan birini ishlatiladigan misol:
async Task<XElement> FromStorageFileToXElement(StorageFile file) {
using (Stream stream = await file.OpenStreamForReadAsync()) {
return XElement.Load(stream);
}
}
Nihoyat, System.IO.WindowsRuntimeStreamExtensions klassi WinRT oqim interfeyslarini (masalan, IRandomAccessStream, IInputStream yoki IOutputStream) .NET Framework ning Stream turiga va aksincha "cast" qiladigan extension metodlarini taklif qiladi.
namespace System.IO { // System.Runtime.WindowsRuntime.dll da aniqlangan
public static class WindowsRuntimeStreamExtensions {
public static Stream AsStream(this IRandomAccessStream winRTStream);
public static Stream AsStream(this IRandomAccessStream winRTStream, Int32 bufferSize);
public static Stream AsStreamForRead(this IInputStream winRTStream);
public static Stream AsStreamForRead(this IInputStream winRTStream, Int32 bufferSize);
public static Stream AsStreamForWrite(this IOutputStream winRTStream);
public static Stream AsStreamForWrite(this IOutputStream winRTStream, Int32 bufferSize);
public static IInputStream AsInputStream (this Stream clrStream);
public static IOutputStream AsOutputStream(this Stream clrStream);
}
}
Quyida WinRT IInputStream ni .NET Framework Stream obyektiga "cast" qilish uchun extension metodlardan birini ishlatadigan misol:
XElement FromWinRTStreamToXElement(IInputStream winRTStream) {
Stream netStream = winRTStream.AsStreamForRead();
return XElement.Load(netStream);
}
E'tibor bering, .NET Framework tomonidan ta'minlangan "cast" extension metodlari oddiy cast dan biroz ko'proq ish qiladi. Xususan, WinRT oqimini .NET Framework oqimiga moslashtiradigan metodlar managed heap da WinRT oqimi uchun yashirin ravishda bufer yaratadi. Natijada, ko'pchilik amallar ushbu buferga yozadi va interop chegarasini kesib o'tishi shart emas, bu esa ishlashni sezilarli yaxshilaydi. Bu ayniqsa ko'p sonli kichik I/O amallarini o'z ichiga olgan stsenariylarda, masalan, XML hujjatini tahlil qilishda muhim.
Garchi ko'p hollarda standart buferlanish ishlash va xotira iste'moli o'rtasida yaxshi muvozanatni ta'minlasa-da, ba'zi hollarda bufer hajmini 16-KB standartdan farqli qilib sozlashni xohlashingiz mumkin. AsStreamXxx metodlari buning uchun overloadlar taklif qiladi. Masalan, agar juda katta fayl bilan uzoq muddat ishlashingizni va boshqa buferlangan oqimlar bir vaqtda kamdan-kam ishlatilishini bilsangiz, juda katta bufer so'rash orqali qo'shimcha ishlash yutuqlariga erishishingiz mumkin. Aksincha, past kechikish (low-latency) talab qiladigan tarmoq stsenarilarida tarmoqdan faqat ilova aniq so'ragan baytlar o'qilishini ta'minlashni xohlashingiz mumkin. Bunday hollarda AsStreamXxx metodlariga nol baytli bufer hajmini ko'rsating va bufer obyekti yaratilmaydi.
.NET Framework ning oqim proyeksiyalaridan foydalanishning afzalliklaridan biri shundaki, agar bir xil WinRT oqim instansiyasida AsStreamXxx metodini bir nechta marta ishlatsangiz, turli, uzilgan buferlar yaratilishi va bitta buferga yozilgan ma'lumotlar boshqasida ko'rinmasligi haqida tashvishlanishingiz shart emas. .NET Framework API lari har bir oqim obyektining yagona adapter instansiyasiga ega bo'lishini ta'minlaydi va barcha foydalanuvchilar bitta buferni bo'lishadi.
CLR va WinRT O'rtasida Ma'lumot Bloklarini Uzatish
Iloji boricha, oldingi bo'limda muhokama qilingan oqim framework proyeksiyalaridan foydalaning, chunki ular juda yaxshi ishlash ko'rsatkichlariga ega. Biroq, CLR va WinRT komponentlari o'rtasida xom ma'lumot bloklarini uzatishingiz kerak bo'lgan paytlar ham bo'lishi mumkin. Masalan, WinRT ning fayl va socket oqim komponentlari xom bloklar o'qish va yozishni talab qiladi. Shuningdek, WinRT ning kriptografik komponentlari ma'lumot bloklarini shifrlash va deshifrlash qiladi, va bitmap piksellari xom ma'lumot bloklarida saqlanadi.
.NET Framework da ma'lumot blokini olishning odatiy usuli bayt massivi (Byte[]) yoki MemoryStream klassi orqalidir. Albatta, bayt massivi va MemoryStream obyektlarini to'g'ridan-to'g'ri WinRT komponentlariga uzatib bo'lmaydi. Shuning uchun WinRT WinRT API lariga uzatilishi mumkin bo'lgan xom ma'lumot bloklarini ifodalovchi IBuffer interfeysi va obyektlarini aniqlaydi. WinRT IBuffer interfeysi quyidagicha aniqlangan:
namespace Windows.Storage.Streams {
public interface IBuffer {
UInt32 Capacity { get; } // Buferning maksimal hajmi (baytlarda)
UInt32 Length { get; set; } // Bufer hozirda ishlayotgan baytlar soni
}
}
Ko'rib turganingizdek, IBuffer obyektining maksimal hajmi va uzunligi bor; lekin g'alatona, bu interfeys buferdagi ma'lumotlardan o'qish yoki unga yozish imkoniyatini bermaydi. Buning asosiy sababi shundaki, WinRT turlari o'z metadatasida ko'rsatkichlarni (pointers) ifodalay olmaydi, chunki ko'rsatkichlar ba'zi tillar (masalan, JavaScript yoki xavfsiz C# kodi) bilan yaxshi moslanmaydi. Shunday qilib, IBuffer obyekti aslida CLR va WinRT API lari o'rtasida xotira manzilini uzatish usuli. Baytlarga xotira manzilida kirish uchun IBufferByteAccess deb nomlangan ichki COM interfeysi ishlatiladi. E'tibor bering, bu interfeys ko'rsatkich qaytarganligi sababli COM interfeysi va WinRT interfeysi emas. .NET Framework jamoasi ushbu COM interfeysi uchun ichki RCW ni aniqlagan:
namespace System.Runtime.InteropServices.WindowsRuntime {
[Guid("905a0fef-bc53-11df-8c49-001e4fc686da")]
[InterfaceType(ComInterfaceType.InterfaceIsUnknown)]
[ComImport]
internal interface IBufferByteAccess {
unsafe Byte* Buffer { get; }
}
}
Ichki tarzda CLR IBuffer obyektini olib, uning IBufferByteAccess interfeysini so'raydi va keyin bufer ichidagi baytlarga xavfsiz bo'lmagan ko'rsatkich olish uchun Buffer xususiyatini so'raydi. Ko'rsatkich bilan baytlarga to'g'ridan-to'g'ri kirish mumkin.
Dasturchilarga ko'rsatkichlarni manipulyatsiya qiladigan xavfsiz bo'lmagan (unsafe) kod yozishdan qochish uchun, Framework Class Library CLR bayt massivlari va oqimlarni WinRT IBuffer obyektlariga bog'lash uchun bir qator extension metodlarini aniqlagan WindowsRuntimeBufferExtensions klassini o'z ichiga oladi. Ushbu extension metodlarni chaqirish uchun manba kodingizga using System.Runtime.InteropServices.WindowsRuntime; direktivasini qo'shganingizga ishonch hosil qiling.
namespace System.Runtime.InteropServices.WindowsRuntime {
public static class WindowsRuntimeBufferExtensions {
public static IBuffer AsBuffer(this Byte[] source);
public static IBuffer AsBuffer(this Byte[] source, Int32 offset, Int32 length);
public static IBuffer AsBuffer(this Byte[] source, Int32 offset, Int32 length, Int32
capacity);
public static IBuffer GetWindowsRuntimeBuffer(this MemoryStream stream);
public static IBuffer GetWindowsRuntimeBuffer(this MemoryStream stream, Int32 position,
Int32 length);
}
}
Shunday qilib, agar Byte[] ga ega bo'lsangiz va uni IBuffer talab qiladigan WinRT API ga uzatmoqchi bo'lsangiz, Byte[] da AsBuffer ni chaqirgan kifoya. Bu samarali tarzda Byte[] ga havolani IBuffer interfeysini amalga oshiruvchi obyekt ichiga o'raydi; Byte[] ning tarkibi nusxalanmaydi, shuning uchun bu juda samarali. Xuddi shunday, agar siz ommaviy ko'rinadigan Byte[] ni o'raydigan MemoryStream obyektiga ega bo'lsangiz, MemoryStream ning buferini IBuffer interfeysini amalga oshiruvchi obyekt ichiga o'rash uchun GetWindowsRuntimeBuffer ni chaqirgan kifoya. Yana, buferning tarkibi nusxalanmaydi, shuning uchun bu juda samarali. Quyidagi metod ikkala stsenariyni ko'rsatadi:
private async Task ByteArrayAndStreamToIBuffer(IRandomAccessStream winRTStream, Int32 count) {
Byte[] bytes = new Byte[count];
await winRTStream.ReadAsync(bytes.AsBuffer(), (UInt32)bytes.Length, InputStreamOptions.None);
Int32 sum = bytes.Sum(b => b); // Baytlarga Byte[] orqali kirish
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms)) {
sw.Write("This string represents data in a stream");
sw.Flush();
UInt32 bytesWritten = await winRTStream.WriteAsync(ms.GetWindowsRuntimeBuffer());
}
}
AsBuffer yoki GetWindowsRuntimeBuffer extension metodlarini chaqirganingizda, bu metodlar manba obyektini IBuffer interfeysini amalga oshiruvchi klass ichidagi obyektga o'raydi. CLR keyin bu obyekt uchun CCW yaratadi va uni WinRT API ga uzatadi. WinRT API IBufferByteAccess interfeysining Buffer xususiyatini so'rab, asosiy bayt massiviga ko'rsatkich oladi, bayt massivi pin-lanadi va manzil WinRT API ga qaytariladi, shunda u ma'lumotlarga kirishi mumkin. Asosiy bayt massivi WinRT API ichki tarzda IBufferByteAccess COM interfeysida COM ning Release metodini chaqirganida unpin qilinadi.
Agar WinRT API IBuffer ni sizga qaytarsa, u holda ma'lumot nativ xotirada bo'lishi ehtimoli bor va siz ushbu ma'lumotlarga boshqariladigan koddan kirish usulini topishingiz kerak bo'ladi. Buning uchun biz WindowsRuntimeBufferExtensions klassi tomonidan aniqlangan boshqa extension metodlarga murojaat qilamiz.
namespace System.Runtime.InteropServices.WindowsRuntime {
public static class WindowsRuntimeBufferExtensions {
public static Stream AsStream(this IBuffer source);
public static Byte[] ToArray(this IBuffer source);
public static Byte[] ToArray(this IBuffer source, UInt32 sourceIndex, Int32 count);
// Ko'rsatilmagan: IBuffer va Byte[] o'rtasida baytlarni uzatish uchun CopyTo metodi
// Ko'rsatilmagan: GetByte, IsSameData metodlari
}
}
AsStream metodi IBuffer manbasini o'raydigan Stream dan hosila bo'lgan obyektni yaratadi. Ushbu Stream obyekti bilan siz Stream ning Read, Write va shunga o'xshash metodlarini chaqirib IBuffer dagi ma'lumotlarga kirishingiz mumkin. ToArray metodi ichki tarzda Byte[] ni ajratadi va keyin manba IBuffer dan barcha baytlarni Byte[] ga nusxalaydi; shu sababli ushbu extension metod xotira iste'moli va CPU vaqti jihatidan potentsial ravishda qimmatga tushishi mumkinligini bilib qo'ying.
WindowsRuntimeBufferExtensions klassi shuningdek IBuffer va Byte[] o'rtasida baytlarni nusxalash uchun bir nechta CopyTo metodi overloadlariga, IBuffer dan bir vaqtda bitta baytni oladigan GetByte metodiga va ikkita IBuffer obyektining tarkibi bir xil yoki yo'qligini solishtiradigan IsSameData metodiga ega. Ko'pchilik ilovalar uchun bu metodlarni chaqirish ehtiyoji bo'lmaydi.
Shuningdek, .NET Framework managed heap da baytlari joylashgan IBuffer obyektini yaratish imkonini beruvchi System.Runtime.InteropServices.WindowsRuntimeBuffer klassini aniqlashini ta'kidlamoqchiman. Xuddi shunday, nativ heap da baytlari joylashgan IBuffer obyektini yaratish imkonini beruvchi Windows.Storage.Streams.Buffer deb nomlangan WinRT komponent mavjud. Ko'pchilik .NET Framework dasturchilari uchun kodingizda ushbu klasslarning birortasini aniq ishlatish zarurati bo'lmasligi kerak.
C# da WinRT Komponentlarini Aniqlash
Hozirgacha men C# dan WinRT komponentlarini qanday iste'mol qilishga e'tibor qaratdim. Biroq, siz C# da WinRT komponentlarini ham aniqlashingiz mumkin va keyin bu komponentlarni nativ C/C++, C#/Visual Basic, JavaScript va potentsial boshqa tillar ishlatishi mumkin. Garchi bu mumkin bo'lsa-da, biz buni qilish mantiqiy bo'lgan stsenariylar haqida o'ylashimiz kerak. Masalan, agar komponentning yagona iste'molchilari CLR ustida ishlaydigan boshqa boshqariladigan tillar bo'lsa, WinRT komponentini C# da aniqlash umuman mantiqsiz. Buning sababi WinRT tur tizimi CLR tur tizimiga qaraganda ancha kam imkoniyatlarga ega va shuning uchun ko'proq cheklovlarga ega.
Men shuningdek C# da nativ C/C++ kod tomonidan iste'mol qilinishi mumkin bo'lgan WinRT komponentini amalga oshirish juda mantiqiy deb o'ylamayman. Nativ C/C++ dan foydalanuvchi dasturchilar odatda ilovalarini ishlash va/yoki xotira iste'moli haqida jiddiy g'amxo'rlik qilib amalga oshiradilar. Ular boshqariladigan kod bilan amalga oshirilgan WinRT komponentidan foydalanishni xohlashlari ehtimoldan yiroq, chunki bu ularning jarayoniga CLR ni yuklashga majbur qiladi va bu garbaж kolleksiyalari va kodni just-in-time kompilyatsiya qilish sababli ularning xotira talablari va ishlashini oshiradi. Shu sababli, ko'pchilik WinRT komponentlari (Windows bilan jo'natiladigan komponentlar o'zi ham) nativ kodda amalga oshiriladi. Albatta, C++ ilovaning ishlashga sezgir bo'lmagan qismlari bo'lishi mumkin va bunday paytlarda .NET Framework funksionalligidan foydalanish unumdorlikni oshirish uchun mantiqiy bo'lishi mumkin. Masalan, Bing Maps o'z foydalanuvchi interfeysini chizish uchun nativ C++ va DirectX dan foydalanadi, lekin biznes mantiq uchun C# ni ham ishlatadi.
Shunday qilib, menga C# da amalga oshirilgan WinRT komponentlari uchun eng mos joy sifatida Windows Store ilova dasturchilari ko'rinadi — ular foydalanuvchi interfeysini HTML va CSS bilan qurib, keyin foydalanuvchi interfeysini C# WinRT komponentida amalga oshirilgan biznes mantiq kodi bilan bog'lash uchun JavaScript ni "yopishqich kod" sifatida ishlatadi. Boshqa stsenariy HTML/JavaScript ilovasidan mavjud Framework Class Library funksionalligidan (masalan, Windows Communication Foundation) foydalanish bo'ladi. HTML va JavaScript bilan ishlaydigan dasturchilar brauzer dvigateli bilan keladigan ishlash va xotira sarfini allaqachon qabul qilishga tayyor va hatto CLR bilan keladigan qo'shimcha ishlash va xotira sarfini ham qabul qilishga tayyor bo'lishi mumkin.
C# da WinRT komponentini yaratish uchun avvalo Microsoft Visual Studio da "Windows Runtime Component" loyihasini yaratishingiz kerak. Bu aslida oddiy klass kutubxonasi loyihasini yaratadi; biroq, C# kompilyatori .winmdobj fayl kengaytmali fayl yaratish uchun /t:winmdobj buyruq satri kaliti bilan ishga tushiriladi. Ushbu kalit ko'rsatilganda, kompilyator ba'zi IL kodlarini odatdagidan boshqacha yaratadi. Masalan, WinRT komponentlari hodisalarga delegatlarni qo'shish va olib tashlashni CLR dan farqli qiladi, shuning uchun hodisa uchun add va remove metodlarini aniq amalga oshirishni keyinroq ko'rsataman.
Kompilyator .winmdobj faylni yaratganidan so'ng, WinMD eksport yordamchi dasturi (WinMDExp.exe) ishga tushiriladi va unga kompilyator tomonidan yaratilgan .winmdobj, .pdb va .xml (doc) fayllar uzatiladi. WinMDExp.exe yordamchi dasturi faylingizning metadatasini tekshiradi, turlaringiz ushbu bobning boshida muhokama qilingan turli WinRT tur tizimi qoidalariga rioya qilishini ta'minlaydi. Yordamchi dastur shuningdek .winmdobj faylidagi metadatani o'zgartiradi; u IL kodini umuman o'zgartirmaydi. Xususan, yordamchi dastur barcha CLR turlarini ekvivalent WinRT turlariga moslashtiradi. Masalan, .NET Framework ning IList<String> turiga havolalar WinRT ning IVector<String> turiga o'zgartiriladi. WinMDExp.exe yordamchi dasturining chiqishi boshqa dasturlash tillari iste'mol qilishi mumkin bo'lgan .winmd faylidir.
Siz .NET Framework ning Intermediate Language Disassembler yordamchi dasturi (ILDasm.exe) yordamida .winmd fayl tarkibini tekshirishingiz mumkin. Odatda ILDasm.exe faylning xom tarkibini ko'rsatadi. Biroq, ILDasm.exe /project buyruq satri kalitini qo'llab-quvvatlaydi, u sizga CLR proyeksiya qilgan WinRT turlarini ularning .NET Framework ekvivalentlariga o'zgartirilgandan keyingi metadatani ko'rsatadi.
Boshqariladigan kod boshqariladigan kodda yozilgan WinRT komponentini iste'mol qilganida, CLR WinRT komponentiga go'yo u oddiy boshqariladigan komponent bo'lgandek munosabatda bo'ladi. Ya'ni, CLR CCW va RCW larni yaratmaydi va shuning uchun WinRT API lariga ushbu wrapperlar orqali murojaat qilmaydi. Bu ishlashni sezilarli yaxshilaydi. Biroq, komponentni sinab ko'rishda API lar boshqa tildan (masalan, nativ C/C++ yoki JavaScript) chaqirilgandek chaqirilmaydi. Shunday qilib, ishlash va xotira overhead ning haqiqiy bo'lmaganidan tashqari, boshqariladigan kod WinRT API ga String talab qiladigan joyga null ni ArgumentNullException tashlanmasdan uzatishi mumkin. Va boshqariladigan kodda amalga oshirilgan WinRT API lari kiritilgan (passed in) massivlarni manipulyatsiya qilishi mumkin va chaqiruvchi API qaytganida o'zgartirilgan massiv tarkibini ko'radi; odatda WinRT tur tizimi API ga uzatilgan massivlarni o'zgartirishni taqiqlaydi. Bular siz kuzata oladigan farqlarning faqat bir qismi, shuning uchun ehtiyot bo'ling.
WinRT Komponent Kodi Namunasi
Quyidagi kod C# da turli WinRT komponentlarini qanday amalga oshirishni ko'rsatadi. Komponentlar ushbu bob davomida muhokama qilingan ko'plab xususiyatlardan foydalanadi va nima sodir bo'layotganini tushuntirish uchun kod bo'ylab ko'plab izohlar mavjud. Agar C# da WinRT komponentini amalga oshirishingiz kerak bo'lsa, ushbu kodni namuna sifatida ishlatishni tavsiya qilaman.
/*****************************************************************************
Module: WinRTComponents.cs
Notices: Copyright (c) 2012 by Jeffrey Richter
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Metadata;
// Nomlar fazosi assembly nomiga mos kelishi KERAK va "Windows" bo'lishi mumkin emas
namespace Wintellect.WinRTComponents {
// [Flags] // enum int bo'lsa, bo'lmasligi kerak; enum uint bo'lsa, kerak
public enum WinRTEnum : int { // Enum int yoki uint tomonidan asoslanishi kerak
None,
NotNone
}
// Strukturalar faqat asosiy ma'lumot turlari, String va boshqa strukturalarni
// o'z ichiga olishi mumkin
// Konstruktorlar yoki metodlarga ruxsat berilmaydi
public struct WinRTStruct {
public Int32 ANumber;
public String AString;
public WinRTEnum AEnum; // Aslida faqat 32-bit butun son
}
// Delegatlar signaturada WinRT-mos turlarga ega bo'lishi kerak (BeginInvoke/EndInvoke yo'q)
public delegate String WinRTDelegate(Int32 x);
// Interfeyslar metodlar, xususiyatlar va hodisalarga ega bo'lishi mumkin,
// lekin generic bo'lishi mumkin emas.
public interface IWinRTInterface {
// Nullable<T> IReference<T> sifatida marshallashtiriladi
Int32? InterfaceProperty { get; set; }
}
// [Version(#)] atributisiz a'zolar klassning 1-versiyasiga tegishli
// va WinMDExp.exe tomonidan yaratilgan bitta asosiy COM interfeysning bir qismi
[Version(1)]
// Klass Object dan hosila, sealed, generic emas bo'lishi kerak,
// faqat WinRT interfeyslarini amalga oshiradi va public a'zolar WinRT turlari bo'lishi kerak
public sealed class WinRTClass : IWinRTInterface {
// Public maydonlarga ruxsat berilmaydi
#region Klass statik metodlar, xususiyatlar va hodisalarni ochishi mumkin
public static String StaticMethod(String s) { return "Returning " + s; }
public static WinRTStruct StaticProperty { get; set; }
// JavaScript da 'out' parametrlari qaytarish qiymati bilan birga obyekt
// xususiyatlari sifatida qaytariladi
public static String OutParameters(out WinRTStruct x, out Int32 year) {
x = new WinRTStruct { AEnum = WinRTEnum.NotNone, ANumber = 333, AString = "Jeff" };
year = DateTimeOffset.Now.Year;
return "Grant";
}
#endregion
// Konstruktor argument olishi mumkin, lekin out/ref argument emas
public WinRTClass(Int32? number) { InterfaceProperty = number; }
public Int32? InterfaceProperty { get; set; }
// Faqat ToString ni qayta yozish mumkin
public override String ToString() {
return String.Format("InterfaceProperty={0}",
InterfaceProperty.HasValue ? InterfaceProperty.Value.ToString() : "(not set)");
}
public void ThrowingMethod() {
throw new InvalidOperationException("My exception message");
// Muayyan HRESULT ni tashlash uchun, uning o'rniga COMException ishlating
//const Int32 COR_E_INVALIDOPERATION = unchecked((Int32)0x80131509);
//throw new COMException("Invalid Operation", COR_E_INVALIDOPERATION);
}
#region Massivlar uzatiladi, qaytariladi YOKI to'ldiriladi; hech qachon aralash emas
public Int32 PassArray([ReadOnlyArray] /* [In] implied */ Int32[] data) {
// ESLATMA: O'zgartirilgan massiv tarkibi marshallashtirilmasligi MUMKIN;
// massivni o'zgartirmang
return data.Sum();
}
public Int32 FillArray([WriteOnlyArray] /* [Out] implied */ Int32[] data) {
// ESLATMA: Asl massiv tarkibi marshallashtirilmasligi MUMKIN;
// undan o'qishdan oldin massivga yozing
for (Int32 n = 0; n < data.Length; n++) data[n] = n;
return data.Length;
}
public Int32[] ReturnArray() {
// Massiv qaytarishda marshallashtiriladi
return new Int32[] { 1, 2, 3 };
}
#endregion
// Kolleksiyalar havola orqali uzatiladi
public void PassAndModifyCollection(IDictionary<String, Object> collection) {
collection["Key2"] = "Value2"; // Kolleksiyani interop orqali joyida o'zgartiradi
}
#region Metod qayta yuklash (overloading)
// Bir xil parametrlar soniga ega overloadlar JavaScript da bir xil deb hisoblanadi
public void SomeMethod(Int32 x) { }
[Windows.Foundation.Metadata.DefaultOverload] // Buni standart overload qiladi
public void SomeMethod(String s) { }
#endregion
#region Avtomatik amalga oshirilgan hodisa
public event WinRTDelegate AutoEvent;
public String RaiseAutoEvent(Int32 number) {
WinRTDelegate d = AutoEvent;
return (d == null) ? "No callbacks registered" : d(number);
}
#endregion
#region Qo'lda amalga oshirilgan hodisa
// Hodisaning ro'yxatga olingan delegatlarini kuzatuvchi private maydon
private EventRegistrationTokenTable<WinRTDelegate> m_manualEvent = null;
// Hodisaning add va remove metodlarini qo'lda amalga oshirish
public event WinRTDelegate ManualEvent {
add {
// Mavjud jadvalni oladi yoki jadval hali initsializatsiya qilinmagan bo'lsa
// yangi yaratadi
return EventRegistrationTokenTable<WinRTDelegate>
.GetOrCreateEventRegistrationTokenTable(ref m_manualEvent)
.AddEventHandler(value);
}
remove {
EventRegistrationTokenTable<WinRTDelegate>
.GetOrCreateEventRegistrationTokenTable(ref m_manualEvent)
.RemoveEventHandler(value);
}
}
public String RaiseManualEvent(Int32 number) {
WinRTDelegate d = EventRegistrationTokenTable<WinRTDelegate>
.GetOrCreateEventRegistrationTokenTable(ref m_manualEvent).InvocationList;
return (d == null) ? "No callbacks registered" : d(number);
}
#endregion
#region Asinxron metodlar
// Asinxron metodlar IAsync[Action|Operation](WithProgress) QAYTARISHI KERAK
// ESLATMA: Boshqa tillar DateTimeOffset ni Windows.Foundation.DateTime sifatida ko'radi
public IAsyncOperationWithProgress<DateTimeOffset, Int32> DoSomethingAsync() {
// System.Runtime.InteropServices.WindowsRuntime.AsyncInfo ning Run metodlaridan
// foydalanib, to'liq boshqariladigan kodda yozilgan private metodni chaqirish
return AsyncInfo.Run<DateTimeOffset, Int32>(DoSomethingAsyncInternal);
}
// Asinxron amalni oddiy .NET texnologiyalari orqali private metod bilan amalga oshirish
private async Task<DateTimeOffset> DoSomethingAsyncInternal(
CancellationToken ct, IProgress<Int32> progress) {
for (Int32 x = 0; x < 10; x++) {
// Bu kod bekor qilish va progress xabar berishni qo'llab-quvvatlaydi
ct.ThrowIfCancellationRequested();
if (progress != null) progress.Report(x * 10);
await Task.Delay(1000); // Asinxron biror narsani simulyatsiya qilish
}
return DateTimeOffset.Now; // Yakuniy qaytarish qiymati
}
public IAsyncOperation<DateTimeOffset> DoSomethingAsync2() {
// Agar bekor qilish va progress kerak bo'lmasa,
// System.WindowsRuntimeSystemExtensions ning AsAsync[Action|Operation] Task
// extension metodlaridan foydalaning (ular ichki tarzda AsyncInfo.Run ni chaqiradi)
return DoSomethingAsyncInternal(default(CancellationToken), null).AsAsyncOperation();
}
#endregion
// Versiya chiqarganingizdan so'ng, yangi a'zolarni [Version(#)] atributi bilan belgilang
// shunda WinMDExp.exe yangi a'zolarni boshqa asosiy COM interfeysiga joylashtiradi.
// Bu zarur, chunki COM interfeyslari o'zgarmas bo'lishi kerak.
[Version(2)]
public void NewMethodAddedInV2() {}
}
}
JavaScript dan WinRT Komponentlariga Kirish
Quyidagi JavaScript kodi yuqoridagi barcha WinRT komponentlari va xususiyatlariga qanday kirish mumkinligini ko'rsatadi:
function () {
// Nomlar fazosiga kirishni kodda qulayroq qilish
var WinRTComps = Wintellect.WinRTComponents;
// ESLATMA: JavaScript VM WinRT API larni camel casing orqali proyeksiya qiladi
// WinRT turining statik metodi va xususiyatiga kirish
var s = WinRTComps.WinRTClass.staticMethod(null); // ESLATMA: JavaScript bu yerda "null" uzatadi!
var struct = { anumber: 123, astring: "Jeff", aenum: WinRTComps.WinRTEnum.notNone };
WinRTComps.WinRTClass.staticProperty = struct;
s = WinRTComps.WinRTClass.staticProperty; // Qaytarib o'qish
// Agar metod out parametrlarga ega bo'lsa, ular va qaytarish qiymati
// obyektning xususiyatlari sifatida qaytariladi
var s = WinRTComps.WinRTClass.outParameters();
var name = s.value; // Qaytarish qiymati
var struct = s.x; // Bitta 'out' parametr
var year = s.year; // Boshqa 'out' parametr
// WinRT komponentining instansiyasini yaratish
var winRTClass = new WinRTComps.WinRTClass(null);
s = winRTClass.toString(); // ToString() ni chaqirish
// throw va catch ni ko'rsatish
try { winRTClass.throwingMethod(); }
catch (err) { }
// Massiv uzatish
var a = [1, 2, 3, 4, 5];
var sum = winRTClass.passArray(a);
// Massiv to'ldirish
var arrayOut = [7, 7, 7]; // ESLATMA: fillArray barcha nollarni ko'radi!
var length = winRTClass.fillArray(arrayOut); // Qaytganida, arrayOut = [0, 1, 2]
// Massiv qaytarish
a = winRTClass.returnArray(); // a = [ 1, 2, 3]
// Kolleksiya uzatish va elementlarini o'zgartirish
var localSettings = Windows.Storage.ApplicationData.current.localSettings;
localSettings.values["Key1"] = "Value1";
winRTClass.passAndModifyCollection(localSettings.values);
// Qaytganida, localSettings.values da 2 ta kalit/qiymat juftligi bor
// Qayta yuklangan metodni chaqirish
winRTClass.someMethod(5); // Aslida String uzatib "5" chaqiradi
// Avtomatik amalga oshirilgan hodisani iste'mol qilish
var f = function (v) { return v.target; };
winRTClass.addEventListener("autoevent", f, false);
s = winRTClass.raiseAutoEvent(7);
// Qo'lda amalga oshirilgan hodisani iste'mol qilish
winRTClass.addEventListener("manualevent", f, false);
s = winRTClass.raiseManualEvent(8);
// Progress, bekor qilish va xatolikni boshqaraydigan asinxron metodni chaqirish
var promise = winRTClass.doSomethingAsync();
promise.then(
function (result) { console.log("Async op complete: " + result); },
function (error) { console.log("Async op error: " + error); },
function (progress) {
console.log("Async op progress: " + progress);
//if (progress == 30) promise.cancel(); // Bekor qilishni sinash uchun
});
}
Ushbu bobda siz Windows Runtime (WinRT) va uning komponentlari bilan CLR qanday ishlashini o'rgandingiz. CLR proyeksiyalari WinRT turlarini .NET dasturchilarga tanish va qulay ko'rinishda taqdim etadi. Framework proyeksiyalari esa asinxron dasturlash, oqimlar o'rtasida interop va xom ma'lumot bloklarini uzatish kabi muhim stsenariylarni qamrab oladi. C# da WinRT komponentlarini aniqlash imkoniyati esa HTML/JavaScript ilovalar bilan .NET biznes mantiqini birlashtirish uchun ideal yechimdir.