[ Pobierz całość w formacie PDF ]
do składowej ma tylko typ, w którym jest ona zdefiniowana, oraz klasy pochodne
(a także pochodne tych klas itd.). Jest to wskazywane przez słowo kluczowe
Srotected języka C#.
Dostęp podzespołowy typ lub składowa są dostępne tylko w podzespole,
w którym zostały zaimplementowane. Jest to często domyślne ustawienie typów.
W języku C# jest to określane przez słowo kluczowe internal.
Dostęp rodzinny (chroniony) lub podzespołowy dostęp do składowej ma typ,
w którym jest ona zdefiniowana, podklasy tego typu oraz dowolny kod wewnątrz
tego samego podzespołu, czyli elementy spełniające określone wyżej kryteria
dostępu rodzinnego lub podzespołowego. Jest to wskazywane przez słowa kluczowe
Srotected internal języka C#.
48 Część I Podstawowe informacje o CLR
Dostęp rodzinny (chroniony) i podzespołowy dostęp do składowej ma tylko
typ, w którym jest ona zdefiniowana, oraz podklasy tego typu znajdujące
się w tym samym podzespole, czyli elementy spełniające zarówno kryteria dostępu
rodzinnego, jak i podzespołowego. C# nie obsługuje tego poziomu dostępności.
Zagnieżdżanie
Część leksykalna opisanych wyżej reguł dostępności staje się istotna dopiero podczas pracy
z zagnieżdżonymi definicjami typów. Jest to oczywiście zależne od języka, ponieważ w CLR
nie występuje pojęcie zasięgu leksykalnego (pominąwszy reguły dostępu do pól z poziomu
metod oraz wczytywania zmiennych lokalnych, argumentów i innych rzeczy związanych
z ramką aktywacji metody). CLR pozwala jednak językom na tworzenie zagnieżdżonych
typów klasy pierwszej.
Na przykład C# umożliwia zagnieżdżenie typu wewnątrz innego typu (itd.):
internal class Outer
{
private static int state;
internal class Inner
{
void Foo() { state++; }
}
}
Aby uzyskać dostęp do klasy Inner na zewnątrz Outer, należy użyć kwalifikowanej nazwy
Outer.Inner. Typy wewnętrzne mają tę samą widoczność co zawierający je typ, a reguły
dostępności są takie same jak w przypadku każdej innej składowej. Język może oczywiście
oferować zasady przesłaniania, ale większość tego nie robi. Dostępność można również
określić ręcznie, jak w przypadku wewnętrznej klasy Inner oznaczonej jako internal.
Wewnętrzny typ Inner ma dostęp do wszystkich składowych typu zewnętrznego, nawet
prywatnych, tak jak każda zwykła składowa Outer. Co więcej, ma dostęp do dowolnych
składowych family (Srotected) w hierarchii typów swojej zewnętrznej klasy.
Składowe typów
Typ może mieć dowolną liczbę składowych. Składowe te tworzą interfejs i implementację
typu, obejmując dane i realizowane operacje. Składowymi są konstruktory, metody, pola,
właściwości i zdarzenia. W tym punkcie omówimy je szczegółowo, wspominając także
o pewnych ogólnych zagadnieniach.
Istnieją dwa typy składowych: instancyjne i statyczne. Dostęp do składowych instancyjnych
uzyskuje się za pośrednictwem instancji typu, a do składowych statycznych za pośred-
nictwem samego typu, a nie jego instancji. Składowe statyczne są zasadniczo składowymi
typu, ponieważ pojęciowo należą do samego typu. Składowe instancyjne mają dostęp do
dowolnych składowych statycznych lub instancyjnych osiągalnych leksykalnie (według
reguł zasięgu danego języka), natomiast składowe statyczne mają dostęp tylko do innych
składowych statycznych tego samego typu.
Rozdział 2. Wspólny system typów 49
Pola
Pole to nazwana zmienna, która wskazuje typizowany slot danych przechowywany w instancji
typu. Pola definiują dane związane z instancją. Pola statyczne są pogrupowane według typu
i domeny aplikacji (w przybliżeniu odpowiadającej procesowi; więcej informacji można
znalezć w rozdziale 10.). Nazwy pól w danym typie muszą być niepowtarzalne, choć typy
pochodne mogą je przedefiniowywać tak, aby wskazywały odmienne lokacje. Rozmiar typu
wartościowego jest w przybliżeniu równy sumie rozmiarów wszystkich jego pól (może na
to wpływać uzupełnianie, które gwarantuje, że instancje są wyrównane do granicy słowa ma-
szynowego). Obiekty są podobne, choć (jak opisano wyżej) wnoszą pewne koszty dodatkowe.
Na przykład poniższy typ określa zbiór pól, jedno statyczne i pięć instancyjnych:
class FieldExample
{
private static int idCounter;
protected int id;
public string name;
public int x;
public int y;
private System.DateTime createDate;
}
Oto odpowiednik tego typu w tekstowym IL:
.class private auto ansi beforefieldinit FieldExample
extends [mscorlib]System.Object
{
.field private static int32 idCounter
.field family int32 id
.field public string name
.field public int32 x
.field public int32 y
.field private valuetype [mscorlib]System.DateTime createDate
}
Informacje w składowej statycznej możemy zapisywać lub odczytywać za pomocą metod
statycznych albo instancyjnych, a informacje w składowych instancyjnych za pomocą
metod instancyjnych FieldExamSle.
Rozmiar instancji FieldExamSle jest sumą rozmiarów jej pól: id (4 bajty), name (4 bajty
w komputerze 32-bitowym), x (4 bajty), y (4 bajty) oraz createDate (pole typu DateTime
liczące 8 bajtów). Aączny rozmiar to 24 bajty. Zauważmy, że pole name jest zarządzaną refe-
rencją i zwiększa się w komputerach 64-bitowych, zatem typ FieldExamSle miałby w nich
28 bajtów. Ponadto dodatkowe koszty sprawiłyby, że obiekt FieldExamSle na stercie GC liczy-
łby przynajmniej 32 bajty (w komputerze 32-bitowym), a prawdopodobnie jeszcze więcej.
Pola tylko do odczytu
Pole może być oznaczone jako initonly w IL (readonly w C#), co wskazuje, że po pełnym
utworzeniu instancji jego wartość nie może zostać zmieniona. Składowe statyczne przezna-
czone tylko do odczytu mogą zostać ustawione wyłącznie podczas inicjalizacji typu, którą
50 Część I Podstawowe informacje o CLR
na przykład w C# można wygodnie przeprowadzić za pomocą inicjalizatora zmiennej, a pózniej
nie mogą być modyfikowane.
class ReadOnlyFieldExample
{
private readonly static int staticData; // Możemy tu ustawić to pole
static ReadOnlyFieldExample()
{
// Pole staticData możemy również ustawić tutaj
}
private readonly int instanceData; // Możemy tu ustawić to pole
public ReadOnlyFieldExample()
{
// Pole instanceData możemy również ustawić
// w jednym z konstruktorów ReadOnlyFieldExample
}
}
Nie należy jednak mylić funkcji tylko do odczytu z niezmiennością. Jeśli na przykład pole
przeznaczone tylko do odczytu odwołuje się do zmiennej struktury danych, zawartość tej
struktury można zmieniać. Tylko do odczytu oznacza po prostu, że nie można zaktuali-
zować referencji tak, aby wskazywała nową instancję struktury danych. Oto przykład:
class ReadOnlyFieldBadExample
{
public static readonly int[] ConstantNumbers = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
};
}
// Jakiś szkodliwy kod może nadal zmieniać liczby w tablicy;
// nie może tylko ustawić zmiennej ConstantNumbers na nową tablicę!
ReadOnlyFieldBadExample.ConstantNumbers[0] = 9;
ReadOnlyFieldBadExample.ConstantNumbers[1] = 8;
// & [ Pobierz całość w formacie PDF ]
zanotowane.pl doc.pisz.pl pdf.pisz.pl rafalstec.xlx.pl
do składowej ma tylko typ, w którym jest ona zdefiniowana, oraz klasy pochodne
(a także pochodne tych klas itd.). Jest to wskazywane przez słowo kluczowe
Srotected języka C#.
Dostęp podzespołowy typ lub składowa są dostępne tylko w podzespole,
w którym zostały zaimplementowane. Jest to często domyślne ustawienie typów.
W języku C# jest to określane przez słowo kluczowe internal.
Dostęp rodzinny (chroniony) lub podzespołowy dostęp do składowej ma typ,
w którym jest ona zdefiniowana, podklasy tego typu oraz dowolny kod wewnątrz
tego samego podzespołu, czyli elementy spełniające określone wyżej kryteria
dostępu rodzinnego lub podzespołowego. Jest to wskazywane przez słowa kluczowe
Srotected internal języka C#.
48 Część I Podstawowe informacje o CLR
Dostęp rodzinny (chroniony) i podzespołowy dostęp do składowej ma tylko
typ, w którym jest ona zdefiniowana, oraz podklasy tego typu znajdujące
się w tym samym podzespole, czyli elementy spełniające zarówno kryteria dostępu
rodzinnego, jak i podzespołowego. C# nie obsługuje tego poziomu dostępności.
Zagnieżdżanie
Część leksykalna opisanych wyżej reguł dostępności staje się istotna dopiero podczas pracy
z zagnieżdżonymi definicjami typów. Jest to oczywiście zależne od języka, ponieważ w CLR
nie występuje pojęcie zasięgu leksykalnego (pominąwszy reguły dostępu do pól z poziomu
metod oraz wczytywania zmiennych lokalnych, argumentów i innych rzeczy związanych
z ramką aktywacji metody). CLR pozwala jednak językom na tworzenie zagnieżdżonych
typów klasy pierwszej.
Na przykład C# umożliwia zagnieżdżenie typu wewnątrz innego typu (itd.):
internal class Outer
{
private static int state;
internal class Inner
{
void Foo() { state++; }
}
}
Aby uzyskać dostęp do klasy Inner na zewnątrz Outer, należy użyć kwalifikowanej nazwy
Outer.Inner. Typy wewnętrzne mają tę samą widoczność co zawierający je typ, a reguły
dostępności są takie same jak w przypadku każdej innej składowej. Język może oczywiście
oferować zasady przesłaniania, ale większość tego nie robi. Dostępność można również
określić ręcznie, jak w przypadku wewnętrznej klasy Inner oznaczonej jako internal.
Wewnętrzny typ Inner ma dostęp do wszystkich składowych typu zewnętrznego, nawet
prywatnych, tak jak każda zwykła składowa Outer. Co więcej, ma dostęp do dowolnych
składowych family (Srotected) w hierarchii typów swojej zewnętrznej klasy.
Składowe typów
Typ może mieć dowolną liczbę składowych. Składowe te tworzą interfejs i implementację
typu, obejmując dane i realizowane operacje. Składowymi są konstruktory, metody, pola,
właściwości i zdarzenia. W tym punkcie omówimy je szczegółowo, wspominając także
o pewnych ogólnych zagadnieniach.
Istnieją dwa typy składowych: instancyjne i statyczne. Dostęp do składowych instancyjnych
uzyskuje się za pośrednictwem instancji typu, a do składowych statycznych za pośred-
nictwem samego typu, a nie jego instancji. Składowe statyczne są zasadniczo składowymi
typu, ponieważ pojęciowo należą do samego typu. Składowe instancyjne mają dostęp do
dowolnych składowych statycznych lub instancyjnych osiągalnych leksykalnie (według
reguł zasięgu danego języka), natomiast składowe statyczne mają dostęp tylko do innych
składowych statycznych tego samego typu.
Rozdział 2. Wspólny system typów 49
Pola
Pole to nazwana zmienna, która wskazuje typizowany slot danych przechowywany w instancji
typu. Pola definiują dane związane z instancją. Pola statyczne są pogrupowane według typu
i domeny aplikacji (w przybliżeniu odpowiadającej procesowi; więcej informacji można
znalezć w rozdziale 10.). Nazwy pól w danym typie muszą być niepowtarzalne, choć typy
pochodne mogą je przedefiniowywać tak, aby wskazywały odmienne lokacje. Rozmiar typu
wartościowego jest w przybliżeniu równy sumie rozmiarów wszystkich jego pól (może na
to wpływać uzupełnianie, które gwarantuje, że instancje są wyrównane do granicy słowa ma-
szynowego). Obiekty są podobne, choć (jak opisano wyżej) wnoszą pewne koszty dodatkowe.
Na przykład poniższy typ określa zbiór pól, jedno statyczne i pięć instancyjnych:
class FieldExample
{
private static int idCounter;
protected int id;
public string name;
public int x;
public int y;
private System.DateTime createDate;
}
Oto odpowiednik tego typu w tekstowym IL:
.class private auto ansi beforefieldinit FieldExample
extends [mscorlib]System.Object
{
.field private static int32 idCounter
.field family int32 id
.field public string name
.field public int32 x
.field public int32 y
.field private valuetype [mscorlib]System.DateTime createDate
}
Informacje w składowej statycznej możemy zapisywać lub odczytywać za pomocą metod
statycznych albo instancyjnych, a informacje w składowych instancyjnych za pomocą
metod instancyjnych FieldExamSle.
Rozmiar instancji FieldExamSle jest sumą rozmiarów jej pól: id (4 bajty), name (4 bajty
w komputerze 32-bitowym), x (4 bajty), y (4 bajty) oraz createDate (pole typu DateTime
liczące 8 bajtów). Aączny rozmiar to 24 bajty. Zauważmy, że pole name jest zarządzaną refe-
rencją i zwiększa się w komputerach 64-bitowych, zatem typ FieldExamSle miałby w nich
28 bajtów. Ponadto dodatkowe koszty sprawiłyby, że obiekt FieldExamSle na stercie GC liczy-
łby przynajmniej 32 bajty (w komputerze 32-bitowym), a prawdopodobnie jeszcze więcej.
Pola tylko do odczytu
Pole może być oznaczone jako initonly w IL (readonly w C#), co wskazuje, że po pełnym
utworzeniu instancji jego wartość nie może zostać zmieniona. Składowe statyczne przezna-
czone tylko do odczytu mogą zostać ustawione wyłącznie podczas inicjalizacji typu, którą
50 Część I Podstawowe informacje o CLR
na przykład w C# można wygodnie przeprowadzić za pomocą inicjalizatora zmiennej, a pózniej
nie mogą być modyfikowane.
class ReadOnlyFieldExample
{
private readonly static int staticData; // Możemy tu ustawić to pole
static ReadOnlyFieldExample()
{
// Pole staticData możemy również ustawić tutaj
}
private readonly int instanceData; // Możemy tu ustawić to pole
public ReadOnlyFieldExample()
{
// Pole instanceData możemy również ustawić
// w jednym z konstruktorów ReadOnlyFieldExample
}
}
Nie należy jednak mylić funkcji tylko do odczytu z niezmiennością. Jeśli na przykład pole
przeznaczone tylko do odczytu odwołuje się do zmiennej struktury danych, zawartość tej
struktury można zmieniać. Tylko do odczytu oznacza po prostu, że nie można zaktuali-
zować referencji tak, aby wskazywała nową instancję struktury danych. Oto przykład:
class ReadOnlyFieldBadExample
{
public static readonly int[] ConstantNumbers = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
};
}
// Jakiś szkodliwy kod może nadal zmieniać liczby w tablicy;
// nie może tylko ustawić zmiennej ConstantNumbers na nową tablicę!
ReadOnlyFieldBadExample.ConstantNumbers[0] = 9;
ReadOnlyFieldBadExample.ConstantNumbers[1] = 8;
// & [ Pobierz całość w formacie PDF ]