a fost întrebat 24 aprilie 16 la 18:36
Nu este clar cum sunt construite covarianța și contravariatul. Exemplul arată că un delegat poate primi o referință la două metode, cu parametri de intrare și ieșire diferiți. Aici covarianța este ChangeIt change = IncrB; și contravarianța ChangeIt change = IncrA; În general, definiția covarianței și contravarierii nu este clară, sunt prea confuze. - NaughtyBrain 24 Apr '16 la 19:20
În primul rând, să vedem ce este această versiune.
Să avem două cursuri, Mașină și BMW. Evident, BMW este o subclasă a Mașinii. fiecare bej este o mașină.
De obicei, ei spun acest lucru: "oriunde utilizați Mașină. puteți utiliza BMW. " Acest lucru este de fapt aproape adevărat, dar nu destul.
Exemplu: dacă aveți o listă de mașini, nu puteți utiliza în schimb lista BMW. De ce? Și iată de ce. Să aveți o listă
Dacă ar fi avut o listă ce putea fi citită numai. atunci problema nu ar fi fost:
Deci, ce avem? În ciuda faptului că BMW este o mașină, lista BMW nu mai este neapărat o listă de mașini. Dar lista BMW, care poate fi citită, este în continuare o listă de mașini.
Acum înapoi la varianță. Vorbim despre covarianță într-un sens general, dacă se schimbă ceva într-un mod similar. În cazul claselor de moștenire: putem folosi BMW în locul mașinii. și în același mod putem să le numim IEnumerable
Bine, aceasta a fost o introducere lungă, acum să ne întoarcem la subiect: covarianța delegaților. Să avem un delegat, în funcție de tipul de Mașină. Schimbarea definiției sale privind mașina pe BMW. pot folosi noul delegat în locul celui vechi?
Să ne gândim logic. Dacă avem un astfel de delegat:
(este nevoie de intrarea Car.and da o alta copie a masinii), este posibil sa o inlocuiti cu functia descrisa de delegatii de acest tip:
Desigur, nu, pentru că delegatul poate prelua orice funcție și funcția noastră dorește numai BMW. Deci, nu există o covarianță aici: această funcție nu poate fi utilizată acolo unde este necesar acest delegat.
Dar dacă tipul variantei noastre de date (adică Car) este doar în poziția tipului returnat:
apoi în locul ei puteți utiliza o funcție de acest tip:
(dacă o mașină era potrivită, atunci BMW este, de asemenea, potrivit).
Aceasta este covarianța delegaților: în cazul în care un delegat este solicitat de la tine în cod, puteți oferi un delegat covariante în schimb.
Cod exemplu care folosește acest lucru:
Contravarianța funcționează invers: puteți folosi un delegat care lucrează cu tipul de bază unde este de așteptat delegatul cu tipul derivat. Aceasta funcționează pentru argumentele funcției:
Aceasta funcționează din aceleași motive ca și covarianța: dacă testerul este potrivit pentru orice tip de mașină, atunci poate funcționa și cu BMW.
Fiecare dintre tipurile de parametri generali de delegați sau tip de interfață trebuie marcat ca covarianți sau contravarianți. Acest lucru nu duce la consecințe nedorite, dar va permite delegațiilor dvs. să fie utilizate în mai multe scenarii și vă va permite să aruncați tipul de variabilă a delegatului generalizat la același tip de delegat cu un alt tip de parametru.
Parametrii-tipuri pot fi:
- Invariantă. Tipul de parametru nu poate fi modificat.
- Contravariant. Tipul de parametru poate fi convertit dintr-o clasă la
clasa derivată din acesta. În C #, tipul contravariant
este marcat de cuvântul cheie din. Tip de parametru contravariant
poate apărea numai în poziția de intrare, de exemplu, ca
argumentele metodei. - Covarianță. Argumentul de tip poate fi convertit dintr-o clasă într-una din clasele sale de bază. În C #, tipul de covarianță este notat de cuvântul cheie afară. Parametrul covarianțial al tipului generalizat poate apărea numai în poziția de ieșire, de exemplu, ca valoare de retur a metodei.
Să presupunem că există următorul tip de delegat:
Aici parametrul T este marcat cu cuvântul din. ceea ce face contravariant, iar parametrul TRESULT este marcat cu cuvântul. făcându-l covariant. Se va declara următoarea variabilă:
Acesta poate fi adus la tipul MyDelegate cu alte tipuri de parametri:
Aceasta indică faptul că fn1 se referă la o funcție care primește un Obiect și returnează o ArgumentException. Variabila fn2 încearcă să facă referire la o metodă care primește un șir și returnează o excepție. Deoarece putem transmite un String unei metode care necesită un tip de Obiect (tipul String derivă din Object) și rezultatul metodei care returnează ArgumentException. poate fi interpretată ca Excepție (tip ArgumentException este derivată din Excepție), codul prezentat aici va fi compilat, iar la etapa de compilare etapa de securitate va fi păstrată.
Notă Variația funcționează numai dacă compilatorul poate seta capacitatea de a converti legăturile între tipuri. Cu alte cuvinte, variația nu este aplicabilă tipurilor semnificative din cauza necesității de box.