Weźmy taki przykład dla typów wartościowych:
short[] x1 = new short[] { -34 };
ushort[] x2 = new ushort[] { 65000 };
//ushort[] x3 = (ushort[])(new short[] { -34 });
//short[] x4 = (short[])(new ushort[] { 34 });
//ushort[] x5 = (short[])x2;
//short[] x6 = (ushort[])x1;
//unchecked
//{
// ushort[] x5 = (short[])x2;
//}
//object[] x7 = (object[])x1;
object x8 = x1;
object x9 = x2;
ushort[] x10 = (ushort[])x8;
short[] x11 = (short[])x9;
//object[] x11a = (object[])x8;
//int[] x11b = (int[])x8;
bool x12 = x8 is short[];
bool x13 = x8 is ushort[];
bool x14 = x8 is int[];
bool x14a = x8.GetType().IsArray && x8.GetType().GetElementType() == typeof(short);
bool x14b = x8.GetType().IsArray && x8.GetType().GetElementType() == typeof(ushort);
int[] x16 = new int[] { -435 };
object x17 = x16;
bool x18 = x17 is int[];
bool x19 = x17 is uint[];
bool x20 = x17 is short[];
bool x21 = x17 is long[];
uint[] x22 = (uint[])x17;
Debug.WriteLine("x12: " + x12);
Debug.WriteLine("x13: " + x13);
Debug.WriteLine("x14: " + x14);
Debug.WriteLine("x14a: " + x14a);
Debug.WriteLine("x14b: " + x14b);
Debug.WriteLine("x18: " + x18);
Debug.WriteLine("x19: " + x19);
Debug.WriteLine("x20: " + x20);
Debug.WriteLine("x21: " + x21);
Debug.WriteLine("x10[0]: " + x10[0]);
Debug.WriteLine("x11[0]: " + x11[0]);
Debug.WriteLine("x22[0]: " + x22[0]);
Zakomentowane fragmenty albo się nie skompilują, albo zwrócą wyjątek. Wynik:
x12: True x13: True x14: False x14a: True x14b: False x18: True x19: True x20: False x21: False x10[0]: 65502 x11[0]: -536 x22[0]: 4294966861
Jak widzimy bezpośrednie rzutowanie jednego typu tablicy w inny typ tablicy nawet jeśli elementy tablicy są niejawnie konwertowalne nie jest możliwe. Kompilator nie pozwala na taką operację raportując ją jako błędną. Nawet jeśli spróbujemy użyć bloku
unchecked
. W przypadku rzutowania typów wartościowych typu:
uint x = (uint)-3
, kompilator także zwróci błąd. Tyle, że tutaj możemy go obejść za pomocą bloku
unchecked
. Poza tym rzutowanie zmiennej typu
uint
na zmienną typu
int
jest możliwe w sposób niejawny:
uint x = 5; int y = (int)x
. Kompilator daje nam do dyspozycji tego typu niejawne operatory konwersji. Ale już za pośrednictwem
object
możemy rzutować tablice
int[]
na
uint[]
. Rzutowanie elementów tablicy odbywa się tak jak byśmy stosowali
(uint)int_value
. Zauważmy, że np.
x1
i
x10
to referencjami do tego samego obiektu. W tym wypadku kompilator po prostu przestaje sprawdzać w trakcie kompilacji jak stosujemy dalej naszą zmienną typu
object
, Zwróćmy uwagę na wartośc zmiennych
x12
i
x13
. Operator
is
nie rozróżnia pomiędzy
short[]
, a
ushort[]
. Dla zmiennej
x14a
i
x14b
jest pokazane jak należy takie tablice rozróżniać, jeśli tego potrzebujemy. Rzutowanie np. pomiędzy
int[]
i
short[]
nie jest możliwe, gdyż rzutowanie nie tworzy nowej tablicy, co było by w tym wypadku potrzebne, ale tylko tworzy referencje innego typu wskazującego w to samo miejsce pamięci. Z tego samego powodu zawiedzie rzutowanie pomiędzy np.
int[]
, a
object[]
. W przypadku tablic obiektów rozpatrzmy taki przykład:
class A
{
}
class B : A
{
}
{
B[] bb = new B[] { new B() };
A[] aa = (A[])bb;
B[] cc = (B[])aa;
//aa[0] = new A();
Debug.ReferenceEquals(aa, bb);
//Debug.Assert(bb.GetType().IsSubclassOf(aa.GetType()));
Debug.Assert(typeof(B).IsSubclassOf(typeof(A)));
}
{
A[] dd = new A[] { new B() };
//B[] ee = (B[])dd;
B[] ee = new B[1];
Array.Copy(dd, ee, 1);
}
{
A[] dd = new A[] { new A() };
//B[] ee = (B[])dd;
}
Widzimy, że rzutowanie tablicy elementów typu
B
na tablicę elementów typu
A
jest możliwa. Dla takiej tablicy jest możliwe także rzutowanie powrotne. Jednakże próba wstawienia do tablicy typu
A[]
będącej zrzutowaną tablicą typu
B[]
elementu typu
A
zakończy się niepowodzeniem. Choć rzutowanie tablicy typu
B[]
na tablicę typu
A[]
jest możliwe to zauważmy, że tablica typu
B[]
nie dziedziczy po tablicy typu
A[]
. Rzutowanie w drugą stronę, tzn. z tablicy typu
A[]
na tablicę typu
B[]
nie jest możliwe. Oczywiście o ile nasza tablica
A[]
nie jest tak naprawdę tablicą typu
B[]
. Jednakże w tym momencie możemy się posłużyć do takiej operacji metodą
Array.Copy()
, która indywidualnie dla każdego kopiowanego obiektu sprawdza jego kompatybilność. Jeszcze jeden przykład:
class D
{
public static implicit operator D(E a)
{
return new D();
}
}
class E
{
public static explicit operator E(D a)
{
return new E();
}
}
{
D[] dd = new D[] { new D() };
//E[] ee = (E[])dd;
E[] ee = new E[1];
//Array.Copy(dd, ee, 1);
}
{
D[] dd = new D[] { new D() };
object x = dd;
//E[] ee = (E[])x;
}
{
E[] ee = new E[] { new E() };
//D[] dd = (D[])ee;
D[] dd = new D[1];
//Array.Copy(ee, dd, 1);
}
{
E[] ee = new E[] { new E() };
object x = ee;
//D[] dd = (D[])x;
}
Widzimy, że istnienie jawnego, czy niejawnego operatora konwersji wcale nie umożliwia nam konwersji tablic takich elementów. Także metoda
Array.Copy()
nie potrafi skorzystać z naszych operatorów konwersji. Ciekawa różnica pomiędzy tablicą typów wartościowych, a referencyjnych:
Debug.WriteLine(String.Format("{0}", new int[] { 1, 2, 3 }));
Debug.WriteLine(String.Format("{0}, {1}, {2}", new string[] { "a", "b", "c" }));
Tablica
int[]
nie może zostać zrzutowana na tablice
object[]
, w przeciwieństwie do tablicy
string[]
. Poprzednio zostało pokazane, że niemożliwa jest rzutowanie tablicy typów wartościowych na tablicę obiektów (i odwrotnie). Jak i niemożliwe jest rzutowanie pomiędzy tablicami różnych typów wartościowych. Czy możemy problemy tego rodzaju obejść za pomocą
Array.Copy()
?
{
int[] aa = new int[] { 1, 2, 3 };
object[] bb = new object[3];
Array.Copy(aa, bb, 3);
bb[0] = 567;
Array.Copy(bb, aa, 3);
long[] cc = new long[3];
Array.Copy(aa, cc, 3);
cc[0] = 456;
//Array.Copy(cc, aa, 3);
}
Widzimy, że konwersja pomiędzy różnymi typami wartościowymi jest niemożliwa. Ale za to pakowanie typów prostych i rozpakowywanie typów referencyjnych działa z tą metodą bez problemu.
Brak komentarzy:
Prześlij komentarz