System typu wspólnego
Wspólny system typów definiuje, w jaki sposób typy są deklarowane, używane i zarządzane w środowisku uruchomieniowym języka wspólnego, a także jest ważną częścią obsługi środowiska uruchomieniowego na potrzeby integracji między językami. Typowy system typów wykonuje następujące funkcje:
Ustanawia strukturę, która ułatwia integrację między językami, bezpieczeństwo typów i wykonywanie kodu o wysokiej wydajności.
Udostępnia model zorientowany obiektowo, który obsługuje kompletną implementację wielu języków programowania.
Definiuje reguły, które muszą być zgodne z językami, co pomaga zagwarantować, że obiekty napisane w różnych językach mogą współdziałać ze sobą.
Udostępnia bibliotekę zawierającą typy danych pierwotnych (takie jak Boolean, , CharByte, Int32i UInt64) używane w tworzeniu aplikacji.
Typy na platformie .NET
Wszystkie typy na platformie .NET są typami wartości lub typami referencyjnymi.
Typy wartości to typy danych, których obiekty są reprezentowane przez rzeczywistą wartość obiektu. Jeśli wystąpienie typu wartości jest przypisane do zmiennej, ta zmienna otrzymuje nową kopię wartości.
Typy referencyjne to typy danych, których obiekty są reprezentowane przez odwołanie (podobne do wskaźnika) do rzeczywistej wartości obiektu. Jeśli typ odwołania jest przypisany do zmiennej, ta zmienna odwołuje się (wskazuje na) oryginalną wartość. Nie wykonano kopii.
Wspólny system typów na platformie .NET obsługuje następujące pięć kategorii typów:
Klasy
Klasa jest typem odwołania, który może pochodzić bezpośrednio z innej klasy i pochodzi niejawnie z System.Objectklasy . Klasa definiuje operacje, które obiekt (który jest wystąpieniem klasy) może wykonywać (metody, zdarzenia lub właściwości) oraz dane, które zawiera obiekt (pola). Mimo że klasa zazwyczaj zawiera zarówno definicję, jak i implementację (w przeciwieństwie do interfejsów, które zawierają tylko definicję bez implementacji), może mieć jeden lub więcej elementów członkowskich, które nie mają implementacji.
W poniższej tabeli opisano niektóre cechy, które może mieć klasa. Każdy język obsługujący środowisko uruchomieniowe umożliwia wskazanie, że element członkowski klasy lub klasy ma co najmniej jedną z tych cech. Jednak poszczególne języki programowania przeznaczone dla platformy .NET mogą nie udostępniać wszystkich tych cech.
Characteristic | opis |
---|---|
sealed | Określa, że inna klasa nie może pochodzić z tego typu. |
implements | Wskazuje, że klasa używa co najmniej jednego interfejsu, zapewniając implementacje składowych interfejsu. |
abstract | Wskazuje, że nie można utworzyć wystąpienia klasy. Aby go używać, musisz utworzyć inną klasę. |
Dziedziczy | Wskazuje, że wystąpienia klasy mogą być używane w dowolnym miejscu, w jakim określono klasę bazową. Klasa pochodna dziedziczona z klasy bazowej może używać implementacji wszystkich publicznych elementów członkowskich dostarczonych przez klasę bazową lub klasa pochodna może zastąpić implementację publicznych składowych własnymi implementacjami. |
wyeksportowane lub nie wyeksportowane | Wskazuje, czy klasa jest widoczna poza zestawem, w którym jest zdefiniowana. Ta cecha ma zastosowanie tylko do klas najwyższego poziomu, a nie do klas zagnieżdżonych. |
Uwaga
Klasę można również zagnieżdżać w klasie nadrzędnej lub strukturze. Zagnieżdżone klasy mają również cechy składowe. Aby uzyskać więcej informacji, zobacz Typy zagnieżdżone.
Składowe klasy, które nie mają implementacji, są elementami składowych abstrakcyjnych. Klasa, która ma co najmniej jeden abstrakcyjny składowy, jest abstrakcyjna; nie można utworzyć nowych wystąpień. Niektóre języki, które są przeznaczone dla środowiska uruchomieniowego, umożliwiają oznaczenie klasy jako abstrakcyjnej, nawet jeśli żadna z jej składowych nie jest abstrakcyjna. Klasę abstrakcyjną można użyć, gdy chcesz hermetyzować podstawowy zestaw funkcji, które klasy pochodne mogą dziedziczyć lub zastępować w razie potrzeby. Klasy, które nie są abstrakcyjne, są określane jako klasy betonowe.
Klasa może zaimplementować dowolną liczbę interfejsów, ale może dziedziczyć tylko jedną klasę bazową oprócz System.Objectklasy , z której wszystkie klasy dziedziczą niejawnie. Wszystkie klasy muszą mieć co najmniej jeden konstruktor, który inicjuje nowe wystąpienia klasy. Jeśli konstruktor nie zostanie jawnie zdefiniowany, większość kompilatorów automatycznie udostępni konstruktor bez parametrów.
Struktury
Struktura jest typem wartości, który pochodzi niejawnie z System.ValueTypeklasy , który z kolei pochodzi z System.Objectklasy . Struktura jest przydatna do reprezentowania wartości, których wymagania dotyczące pamięci są małe, oraz do przekazywania wartości jako parametrów według wartości do metod, które mają silnie typizowane parametry. Na platformie .NET wszystkie pierwotne typy danych (Boolean, Byte, CharDecimalInt16Int32Int64DoubleDateTimeSingleUInt16SByteUInt32i UInt64) są definiowane jako struktury.
Podobnie jak klasy, struktury definiują zarówno dane (pola struktury), jak i operacje, które można wykonywać na tych danych (metody struktury). Oznacza to, że można wywoływać metody w strukturach, w tym metody wirtualne zdefiniowane w System.Object klasach i System.ValueType oraz wszelkie metody zdefiniowane na samym typie wartości. Innymi słowy, struktury mogą mieć pola, właściwości i zdarzenia, a także metody statyczne i niestatyczne. Możesz tworzyć wystąpienia struktur, przekazywać je jako parametry, przechowywać je jako zmienne lokalne lub przechowywać je w polu innego typu wartości lub typu odwołania. Struktury mogą również implementować interfejsy.
Typy wartości różnią się również od klas pod kilkoma względami. Po pierwsze, chociaż niejawnie dziedziczą z System.ValueTypeklasy , nie mogą bezpośrednio dziedziczyć z dowolnego typu. Podobnie wszystkie typy wartości są zapieczętowane, co oznacza, że żaden inny typ nie może pochodzić od nich. Nie wymagają również konstruktorów.
Dla każdego typu wartości środowisko uruchomieniowe języka wspólnego dostarcza odpowiedni typ pola, który jest klasą o tym samym stanie i zachowaniu co typ wartości. Wystąpienie typu wartości jest w polu, gdy jest przekazywane do metody, która akceptuje parametr typu System.Object. Jest on rozpakowany (czyli konwertowany z wystąpienia klasy z powrotem na wystąpienie typu wartości), gdy kontrolka powraca z wywołania metody akceptującego typ wartości jako parametr by-reference. Niektóre języki wymagają użycia specjalnej składni, gdy wymagany jest typ pola; inne automatycznie używają typu pola, gdy jest to konieczne. Podczas definiowania typu wartości definiujesz zarówno pole, jak i typ rozpasany.
Wyliczenia
Wyliczenie to typ wartości, który dziedziczy bezpośrednio z System.Enum i dostarcza alternatywne nazwy wartości bazowego typu pierwotnego. Typ wyliczenia ma nazwę, podstawowy typ, który musi być jednym z wbudowanych, podpisanych lub niepodpisanych typów liczb całkowitych (takich jak Byte, Int32lub UInt64) i zestawu pól. Pola są polami literału statycznego, z których każda reprezentuje stałą. Tę samą wartość można przypisać do wielu pól. W takim przypadku należy oznaczyć jedną z wartości jako podstawową wartość wyliczenia dla konwersji odbicia i ciągu.
Wartość typu bazowego można przypisać do wyliczenia i odwrotnie (nie jest wymagane rzutowanie przez środowisko uruchomieniowe). Możesz utworzyć wystąpienie wyliczenia i wywołać metody System.Enum, a także wszelkie metody zdefiniowane w typie bazowym wyliczenia. Jednak niektóre języki mogą nie zezwalać na przekazanie wyliczenia jako parametru, gdy wymagane jest wystąpienie typu bazowego (lub odwrotnie).
Następujące dodatkowe ograniczenia dotyczą wyliczeń:
Nie mogą definiować własnych metod.
Nie mogą implementować interfejsów.
Nie mogą definiować właściwości ani zdarzeń.
Nie mogą być ogólne, chyba że są ogólne tylko dlatego, że są zagnieżdżone w typie ogólnym. Oznacza to, że wyliczenie nie może mieć własnych parametrów typu.
Uwaga
Typy zagnieżdżone (w tym wyliczenia) utworzone za pomocą języka Visual Basic, C# i C++ zawierają parametry typu wszystkich ujętych typów ogólnych i dlatego są ogólne, nawet jeśli nie mają własnych parametrów typu. Aby uzyskać więcej informacji, zobacz "Zagnieżdżone typy" w temacie referencyjnym Type.MakeGenericType .
Atrybut FlagsAttribute określa specjalny rodzaj wyliczenia nazywane polem bitowym. Samo środowisko uruchomieniowe nie rozróżnia tradycyjnych wyliczeń i pól bitowych, ale język może to zrobić. Po wprowadzeniu tego rozróżnienia operatory bitowe mogą być używane w polach bitowych, ale nie w wyliczeniach, aby wygenerować nienazwane wartości. Wyliczenia są zwykle używane dla list unikatowych elementów, takich jak dni tygodnia, nazwy kraju lub regionu itd. Pola bitowe są zwykle używane w przypadku list cech lub ilości, które mogą wystąpić w połączeniu, takich jak Red And Big And Fast
.
W poniższym przykładzie pokazano, jak używać pól bitowych i tradycyjnych wyliczeń.
using System;
using System.Collections.Generic;
// A traditional enumeration of some root vegetables.
public enum SomeRootVegetables
{
HorseRadish,
Radish,
Turnip
}
// A bit field or flag enumeration of harvesting seasons.
[Flags]
public enum Seasons
{
None = 0,
Summer = 1,
Autumn = 2,
Winter = 4,
Spring = 8,
All = Summer | Autumn | Winter | Spring
}
public class Example
{
public static void Main()
{
// Hash table of when vegetables are available.
Dictionary<SomeRootVegetables, Seasons> AvailableIn = new Dictionary<SomeRootVegetables, Seasons>();
AvailableIn[SomeRootVegetables.HorseRadish] = Seasons.All;
AvailableIn[SomeRootVegetables.Radish] = Seasons.Spring;
AvailableIn[SomeRootVegetables.Turnip] = Seasons.Spring |
Seasons.Autumn;
// Array of the seasons, using the enumeration.
Seasons[] theSeasons = new Seasons[] { Seasons.Summer, Seasons.Autumn,
Seasons.Winter, Seasons.Spring };
// Print information of what vegetables are available each season.
foreach (Seasons season in theSeasons)
{
Console.Write(String.Format(
"The following root vegetables are harvested in {0}:\n",
season.ToString("G")));
foreach (KeyValuePair<SomeRootVegetables, Seasons> item in AvailableIn)
{
// A bitwise comparison.
if (((Seasons)item.Value & season) > 0)
Console.Write(String.Format(" {0:G}\n",
(SomeRootVegetables)item.Key));
}
}
}
}
// The example displays the following output:
// The following root vegetables are harvested in Summer:
// HorseRadish
// The following root vegetables are harvested in Autumn:
// Turnip
// HorseRadish
// The following root vegetables are harvested in Winter:
// HorseRadish
// The following root vegetables are harvested in Spring:
// Turnip
// Radish
// HorseRadish
Imports System.Collections.Generic
' A traditional enumeration of some root vegetables.
Public Enum SomeRootVegetables
HorseRadish
Radish
Turnip
End Enum
' A bit field or flag enumeration of harvesting seasons.
<Flags()> Public Enum Seasons
None = 0
Summer = 1
Autumn = 2
Winter = 4
Spring = 8
All = Summer Or Autumn Or Winter Or Spring
End Enum
' Entry point.
Public Class Example
Public Shared Sub Main()
' Hash table of when vegetables are available.
Dim AvailableIn As New Dictionary(Of SomeRootVegetables, Seasons)()
AvailableIn(SomeRootVegetables.HorseRadish) = Seasons.All
AvailableIn(SomeRootVegetables.Radish) = Seasons.Spring
AvailableIn(SomeRootVegetables.Turnip) = Seasons.Spring Or _
Seasons.Autumn
' Array of the seasons, using the enumeration.
Dim theSeasons() As Seasons = {Seasons.Summer, Seasons.Autumn, _
Seasons.Winter, Seasons.Spring}
' Print information of what vegetables are available each season.
For Each season As Seasons In theSeasons
Console.WriteLine(String.Format( _
"The following root vegetables are harvested in {0}:", _
season.ToString("G")))
For Each item As KeyValuePair(Of SomeRootVegetables, Seasons) In AvailableIn
' A bitwise comparison.
If (CType(item.Value, Seasons) And season) > 0 Then
Console.WriteLine(" " + _
CType(item.Key, SomeRootVegetables).ToString("G"))
End If
Next
Next
End Sub
End Class
' The example displays the following output:
' The following root vegetables are harvested in Summer:
' HorseRadish
' The following root vegetables are harvested in Autumn:
' Turnip
' HorseRadish
' The following root vegetables are harvested in Winter:
' HorseRadish
' The following root vegetables are harvested in Spring:
' Turnip
' Radish
' HorseRadish
Interfejsy
Interfejs definiuje kontrakt, który określa relację "może zrobić" lub "ma" relację. Interfejsy są często używane do implementowania funkcji, takich jak porównywanie i sortowanie (interfejsy iIComparable<T>), testowanie równości (IEquatable<T>interfejs) lub wyliczanie elementów w kolekcji (IEnumerableinterfejs iIEnumerable<T>).IComparable Interfejsy mogą mieć właściwości, metody i zdarzenia, z których wszystkie są elementami abstrakcyjnymi; oznacza to, że mimo że interfejs definiuje elementy członkowskie i ich podpisy, pozostawia go do typu implementujący interfejs w celu zdefiniowania funkcjonalności każdego elementu członkowskiego interfejsu. Oznacza to, że każda klasa lub struktura, która implementuje interfejs, musi podać definicje abstrakcyjnych składowych zadeklarowanych w interfejsie. Interfejs może wymagać implementacji dowolnej klasy lub struktury w celu zaimplementowania co najmniej jednego innego interfejsu.
Następujące ograniczenia dotyczą interfejsów:
- Interfejs można zadeklarować przy użyciu dowolnych ułatwień dostępu, ale wszystkie elementy członkowskie interfejsu muszą mieć dostęp publiczny.
- Interfejsy nie mogą definiować konstruktorów.
- Interfejsy nie mogą definiować pól.
- Interfejsy mogą definiować tylko elementy członkowskie wystąpień. Nie mogą definiować statycznych elementów członkowskich.
Każdy język musi zapewniać reguły mapowania implementacji na interfejs, który wymaga elementu członkowskiego, ponieważ więcej niż jeden interfejs może zadeklarować element członkowski z tym samym podpisem, a te elementy członkowskie mogą mieć oddzielne implementacje.
Delegaci
Delegaty są typami referencyjnymi, które służą celowi podobnemu do wskaźników funkcji w języku C++. Są one używane do obsługi zdarzeń i funkcji wywołania zwrotnego na platformie .NET. W przeciwieństwie do wskaźników funkcji delegaty są bezpieczne, weryfikowalne i bezpieczne. Typ delegata może reprezentować dowolną metodę wystąpienia lub metodę statyczną, która ma zgodny podpis.
Parametr delegata jest zgodny z odpowiednim parametrem metody, jeśli typ parametru delegata jest bardziej restrykcyjny niż typ parametru metody, ponieważ gwarantuje to, że argument przekazany do delegata można bezpiecznie przekazać do metody.
Podobnie zwracany typ delegata jest zgodny z zwracanym typem metody, jeśli zwracany typ metody jest bardziej restrykcyjny niż zwracany typ delegata, ponieważ gwarantuje to bezpieczne rzutowanie wartości zwracanej metody na zwracany typ delegata.
Na przykład delegat, który ma parametr typu IEnumerable i zwracany typ Object , może reprezentować metodę, która ma parametr typu Object i zwracaną wartość typu IEnumerable. Aby uzyskać więcej informacji i przykładowy kod, zobacz Delegate.CreateDelegate(Type, Object, MethodInfo).
Mówi się, że delegat jest powiązany z metodą, która reprezentuje. Oprócz powiązania z metodą delegat może być powiązany z obiektem. Obiekt reprezentuje pierwszy parametr metody i jest przekazywany do metody za każdym razem, gdy delegat jest wywoływany. Jeśli metoda jest metodą wystąpienia, powiązany obiekt jest przekazywany jako niejawny this
parametr (Me
w Visual Basic); jeśli metoda jest statyczna, obiekt jest przekazywany jako pierwszy formalny parametr metody, a sygnatura delegata musi odpowiadać pozostałym parametrom. Aby uzyskać więcej informacji i przykładowy kod, zobacz System.Delegate.
Wszyscy delegaci dziedziczą z System.MulticastDelegateelementu , który dziedziczy z System.Delegateelementu . Języki C#, Visual Basic i C++ nie zezwalają na dziedziczenie z tych typów. Zamiast tego udostępniają słowa kluczowe do deklarowania delegatów.
Ponieważ delegaty dziedziczą z MulticastDelegateelementu , delegat ma listę wywołań, która jest listą metod reprezentowanych przez delegata i wykonywanych po wywołaniu delegata. Wszystkie metody na liście otrzymują argumenty podane podczas wywoływanego delegata.
Uwaga
Wartość zwracana nie jest zdefiniowana dla delegata, który ma więcej niż jedną metodę na liście wywołań, nawet jeśli delegat ma typ zwracany.
W wielu przypadkach, takich jak w przypadku metod wywołania zwrotnego, delegat reprezentuje tylko jedną metodę, a jedynymi akcjami, które należy wykonać, są tworzenie delegata i wywoływanie go.
W przypadku delegatów reprezentujących wiele metod platforma .NET udostępnia metody Delegate klas i MulticastDelegate delegatów do obsługi operacji, takich jak dodawanie metody do listy wywołań delegata (metoda), usuwanie metody ( Delegate.Combine Delegate.Remove metody) i pobieranie listy wywołań ( Delegate.GetInvocationList metoda).
Uwaga
Nie trzeba używać tych metod dla delegatów obsługi zdarzeń w języku C#, C++i Visual Basic, ponieważ te języki zapewniają składnię dodawania i usuwania programów obsługi zdarzeń.
Definicje typów
Definicja typu obejmuje następujące elementy:
- Wszystkie atrybuty zdefiniowane w typie.
- Dostępność typu (widoczność).
- Nazwa typu.
- Typ podstawowy typu.
- Wszystkie interfejsy implementowane przez typ.
- Definicje poszczególnych elementów członkowskich typu.
Atrybuty
Atrybuty zapewniają dodatkowe metadane zdefiniowane przez użytkownika. Najczęściej są one używane do przechowywania dodatkowych informacji o typie w zestawie lub modyfikowaniu zachowania elementu członkowskiego typu w środowisku projektowania lub w czasie wykonywania.
Atrybuty są klasami, które dziedziczą z System.Attributeklasy . Języki, które obsługują używanie atrybutów, mają własną składnię do stosowania atrybutów do elementu języka. Atrybuty można stosować do niemal dowolnego elementu języka; określone elementy, do których można zastosować atrybut, są definiowane przez AttributeUsageAttribute klasę, która jest stosowana do tej klasy atrybutów.
Ułatwienia dostępu typu
Wszystkie typy mają modyfikator, który kontroluje ich dostępność z innych typów. W poniższej tabeli opisano dostęp do typów obsługiwanych przez środowisko uruchomieniowe.
Ułatwienia dostępu | opis |
---|---|
public | Typ jest dostępny dla wszystkich zestawów. |
zestaw | Typ jest dostępny tylko z poziomu zestawu. |
Dostępność typu zagnieżdżonego zależy od jego domeny ułatwień dostępu, która jest określana zarówno przez zadeklarowaną dostępność elementu członkowskiego, jak i domenę ułatwień dostępu natychmiast zawierającego typ. Jednak domena ułatwień dostępu typu zagnieżdżonego nie może przekroczyć tego typu zawierającego.
Domena ułatwień dostępu zagnieżdżonego elementu członkowskiego M
zadeklarowanego w typie T
w programie P
jest definiowana w następujący sposób (zauważając, że M
może to być typ):
Jeśli zadeklarowane ułatwienia dostępu to
public
, domena ułatwień dostępu jestM
domeną ułatwień dostępuM
.T
Jeśli zadeklarowane ułatwienia dostępu to , domena
M
ułatwień dostępu jest skrzyżowaniem domeny ułatwień dostępuM
zT
tekstemP
programu i tekstem programu dowolnego typu pochodzącego zT
zadeklarowanego pozaP
.protected internal
Jeśli zadeklarowane ułatwienia dostępu to , domena ułatwień dostępu jest skrzyżowaniem domeny ułatwień dostępu
M
zT
tekstemT
programu i dowolnym typem pochodzącym zT
.M
protected
Jeśli zadeklarowane ułatwienia dostępu to
internal
, domena ułatwień dostępu jestM
skrzyżowaniem domeny ułatwień dostępuM
wT
pliku z tekstemP
programu .Jeśli zadeklarowane ułatwienia dostępu to
private
, domena ułatwień dostępuM
programuM
to tekstT
programu .
Nazwy typów
Wspólny system typów nakłada tylko dwa ograniczenia dotyczące nazw:
- Wszystkie nazwy są kodowane jako ciągi znaków Unicode (16-bitowych).
- Nazwy nie mogą mieć osadzonej (16-bitowej) wartości 0x0000.
Jednak większość języków nakłada dodatkowe ograniczenia dotyczące nazw typów. Wszystkie porównania są wykonywane na podstawie bajtów bajtowych i dlatego są zależne od wielkości liter i ustawień regionalnych.
Mimo że typ może odwoływać się do typów z innych modułów i zestawów, typ musi być w pełni zdefiniowany w ramach jednego modułu platformy .NET. (Jednak w zależności od obsługi kompilatora można go podzielić na wiele plików kodu źródłowego). Nazwy typów muszą być unikatowe tylko w przestrzeni nazw. Aby w pełni zidentyfikować typ, nazwa typu musi być kwalifikowana przez przestrzeń nazw zawierającą implementację typu.
Podstawowe typy i interfejsy
Typ może dziedziczyć wartości i zachowania z innego typu. Wspólny system typów nie zezwala na dziedziczenie typów z więcej niż jednego typu podstawowego.
Typ może implementować dowolną liczbę interfejsów. Aby zaimplementować interfejs, typ musi implementować wszystkie wirtualne elementy członkowskie tego interfejsu. Metodę wirtualną można zaimplementować za pomocą typu pochodnego i można wywołać statycznie lub dynamicznie.
Składowe typu
Środowisko uruchomieniowe umożliwia definiowanie elementów członkowskich typu, co określa zachowanie i stan typu. Składowe typu obejmują następujące elementy:
Pola
Pole opisuje i zawiera część stanu typu. Pola mogą być dowolnego typu obsługiwane przez środowisko uruchomieniowe. Najczęściej pola są albo private
lub protected
, aby były dostępne tylko z poziomu klasy lub klasy pochodnej. Jeśli wartość pola może zostać zmodyfikowana spoza jego typu, zazwyczaj jest używany metodę dostępu zestawu właściwości. Publicznie uwidocznione pola są zwykle tylko do odczytu i mogą mieć dwa typy:
- Stałe, których wartość jest przypisywana w czasie projektowania. Są to statyczne elementy członkowskie klasy, chociaż nie są zdefiniowane przy użyciu słowa kluczowego
static
(Shared
w Visual Basic). - Zmienne tylko do odczytu, których wartości można przypisać w konstruktorze klasy.
Poniższy przykład ilustruje te dwa użycia pól tylko do odczytu.
using System;
public class Constants
{
public const double Pi = 3.1416;
public readonly DateTime BirthDate;
public Constants(DateTime birthDate)
{
this.BirthDate = birthDate;
}
}
public class Example
{
public static void Main()
{
Constants con = new Constants(new DateTime(1974, 8, 18));
Console.Write(Constants.Pi + "\n");
Console.Write(con.BirthDate.ToString("d") + "\n");
}
}
// The example displays the following output if run on a system whose current
// culture is en-US:
// 3.1416
// 8/18/1974
Public Class Constants
Public Const Pi As Double = 3.1416
Public ReadOnly BirthDate As Date
Public Sub New(birthDate As Date)
Me.BirthDate = birthDate
End Sub
End Class
Public Module Example
Public Sub Main()
Dim con As New Constants(#8/18/1974#)
Console.WriteLine(Constants.Pi.ToString())
Console.WriteLine(con.BirthDate.ToString("d"))
End Sub
End Module
' The example displays the following output if run on a system whose current
' culture is en-US:
' 3.1416
' 8/18/1974
Właściwości
Właściwość nazywa wartość lub stan typu i definiuje metody pobierania lub ustawiania wartości właściwości. Właściwości mogą być typami pierwotnymi, kolekcjami typów pierwotnych, typami zdefiniowanymi przez użytkownika lub kolekcjami typów zdefiniowanych przez użytkownika. Właściwości są często używane do zachowania publicznego interfejsu typu niezależnego od rzeczywistej reprezentacji typu. Dzięki temu właściwości mogą odzwierciedlać wartości, które nie są bezpośrednio przechowywane w klasie (na przykład gdy właściwość zwraca obliczoną wartość) lub aby przeprowadzić walidację przed przypisaniem wartości do pól prywatnych. Poniższy przykład ilustruje ten ostatni wzorzec.
using System;
public class Person
{
private int m_Age;
public int Age
{
get { return m_Age; }
set {
if (value < 0 || value > 125)
{
throw new ArgumentOutOfRangeException("The value of the Age property must be between 0 and 125.");
}
else
{
m_Age = value;
}
}
}
}
Public Class Person
Private m_Age As Integer
Public Property Age As Integer
Get
Return m_Age
End Get
Set
If value < 0 Or value > 125 Then
Throw New ArgumentOutOfRangeException("The value of the Age property must be between 0 and 125.")
Else
m_Age = value
End If
End Set
End Property
End Class
Oprócz dołączania samej właściwości wspólny język pośredni (CIL) dla typu zawierającego właściwość czytelną zawiera get_
metodę propertyname, oraz CIL dla typu zawierającego właściwość zapisywalną zawiera set_
metodę propertyname.
Metody
Metoda opisuje operacje, które są dostępne w typie. Podpis metody określa dozwolone typy wszystkich jego parametrów i jego wartości zwracanej.
Chociaż większość metod definiuje dokładną liczbę parametrów wymaganych dla wywołań metod, niektóre metody obsługują zmienną liczbę parametrów. Ostatni zadeklarowany parametr tych metod jest oznaczony atrybutem ParamArrayAttribute . Kompilatory języka zwykle udostępniają słowo kluczowe, takie jak params
w języku C# i ParamArray
Visual Basic, które jawnie wykorzystuje ParamArrayAttribute niepotrzebne.
Konstruktory
Konstruktor jest specjalnym rodzajem metody, która tworzy nowe wystąpienia klasy lub struktury. Podobnie jak każda inna metoda, konstruktor może zawierać parametry; jednak konstruktory nie mają wartości zwracanej (czyli zwracają wartość void
).
Jeśli kod źródłowy klasy nie definiuje jawnie konstruktora, kompilator zawiera konstruktor bez parametrów. Jeśli jednak kod źródłowy klasy definiuje tylko sparametryzowane konstruktory, kompilatory Visual Basic i C# nie generują konstruktora bez parametrów.
Jeśli kod źródłowy struktury definiuje konstruktory, muszą być sparametryzowane; struktura nie może definiować konstruktora bez parametrów, a kompilatory nie generują konstruktorów bez parametrów dla struktur ani innych typów wartości. Wszystkie typy wartości mają niejawny konstruktor bez parametrów. Ten konstruktor jest implementowany przez środowisko uruchomieniowe języka wspólnego i inicjuje wszystkie pola struktury do ich wartości domyślnych.
Zdarzenia
Zdarzenie definiuje zdarzenie, na które można odpowiedzieć, i definiuje metody subskrybowania, anulowania subskrypcji i zgłaszania zdarzenia. Zdarzenia są często używane do informowania o innych typach zmian stanu. Aby uzyskać więcej informacji, zobacz Zdarzenia.
Zagnieżdżone typy
Typ zagnieżdżony jest typem, który jest elementem członkowskim innego typu. Typy zagnieżdżone powinny być ściśle powiązane z ich typem zawierającym i nie mogą być przydatne jako typ ogólnego przeznaczenia. Typy zagnieżdżone są przydatne, gdy typ deklarowany używa i tworzy wystąpienia typu zagnieżdżonego, a użycie typu zagnieżdżonego nie jest widoczne w publicznych elementach członkowskich.
Typy zagnieżdżone są mylące dla niektórych deweloperów i nie powinny być widoczne publicznie, chyba że istnieje przekonujący powód widoczności. W dobrze zaprojektowanej bibliotece deweloperzy rzadko powinni używać typów zagnieżdżonych do tworzenia wystąpień obiektów lub deklarowania zmiennych.
Cechy elementów członkowskich typu
Wspólny system typów umożliwia członkom typów różne cechy; jednak języki nie są wymagane do obsługi wszystkich tych cech. W poniższej tabeli opisano cechy składowych.
Characteristic | Może mieć zastosowanie do | opis |
---|---|---|
abstract | Metody, właściwości i zdarzenia | Typ nie dostarcza implementacji metody. Typy, które dziedziczą lub implementują metody abstrakcyjne, muszą dostarczyć implementację dla metody . Jedynym wyjątkiem jest to, że typ pochodny sam jest typem abstrakcyjnym. Wszystkie metody abstrakcyjne są wirtualne. |
prywatne, rodzinne, zgromadzenie, rodzina i zgromadzenie, rodzina lub zgromadzenie lub publiczne | wszystkie | Definiuje dostępność elementu członkowskiego: private Dostępne tylko z poziomu tego samego typu co element członkowski lub w zagnieżdżonym typie. family Dostępny z poziomu tego samego typu co element członkowski i z typów pochodnych, które dziedziczą z niego. zestaw Dostępny tylko w zestawie, w którym zdefiniowano typ. rodzina i zestaw Dostępne tylko z typów, które kwalifikują się zarówno do dostępu rodziny, jak i zestawu. rodzina lub zestaw Dostępne tylko z typów, które kwalifikują się do dostępu rodziny lub zestawu. public Dostępny z dowolnego typu. |
końcowe | Metody, właściwości i zdarzenia | Metody wirtualnej nie można zastąpić w typie pochodnym. |
inicjowanie tylko | Pola | Wartość można zainicjować tylko i nie można jej zapisać po zainicjowaniu. |
wystąpienie | Pola, metody, właściwości i zdarzenia | Jeśli element członkowski nie jest oznaczony jako static (C# i C++), (Visual Basic), Shared virtual (C# i C++) lub Overridable (Visual Basic), jest członkiem wystąpienia (nie ma słowa kluczowego wystąpienia). Istnieje tyle kopii takich elementów członkowskich w pamięci, jak istnieją obiekty, które go używają. |
literal | Pola | Wartość przypisana do pola jest wartością stałą znaną w czasie kompilacji typu wartości wbudowanej. Pola literału są czasami określane jako stałe. |
newslot lub przesłonięć | wszystkie | Definiuje sposób interakcji elementu członkowskiego z dziedziczone elementy członkowskie, które mają ten sam podpis: newslot Ukrywa dziedziczone elementy członkowskie, które mają ten sam podpis. override Zastępuje definicję dziedziczonej metody wirtualnej. Wartość domyślna to newslot. |
static | Pola, metody, właściwości i zdarzenia | Element członkowski należy do typu, na który jest zdefiniowany, a nie do określonego wystąpienia typu; element członkowski istnieje nawet wtedy, gdy wystąpienie typu nie zostanie utworzone i jest współużytkowane przez wszystkie wystąpienia typu. |
virtual | Metody, właściwości i zdarzenia | Metodę można zaimplementować za pomocą typu pochodnego i można wywołać statycznie lub dynamicznie. Jeśli jest używane wywołanie dynamiczne, typ wystąpienia, które wykonuje wywołanie w czasie wykonywania (a nie typ znany w czasie kompilacji) określa, która implementacja metody jest wywoływana. Aby statycznie wywołać metodę wirtualną, zmienna może być rzutowania na typ, który używa żądanej wersji metody. |
Przeciążenie
Każdy element członkowski typu ma unikatowy podpis. Podpisy metody składają się z nazwy metody i listy parametrów (kolejność i typy argumentów metody). W obrębie typu można zdefiniować wiele metod o tej samej nazwie, o ile ich podpisy różnią się. Jeśli zdefiniowano co najmniej dwie metody o tej samej nazwie, metoda jest określana jako przeciążona. Na przykład w System.Charmetodzie IsDigit metoda jest przeciążona. Jedna metoda przyjmuje metodę Char. Druga metoda przyjmuje metodę String i Int32.
Uwaga
Typ zwracany nie jest traktowany jako część podpisu metody. Oznacza to, że metody nie mogą być przeciążone, jeśli różnią się tylko typem zwracanym.
Dziedziczenie, zastępowanie i ukrywanie elementów członkowskich
Typ pochodny dziedziczy wszystkie elementy członkowskie typu podstawowego; oznacza to, że te elementy członkowskie są zdefiniowane i dostępne dla typu pochodnego. Zachowanie lub cechy odziedziczonych elementów członkowskich można modyfikować na dwa sposoby:
Typ pochodny może ukryć dziedziczony element członkowski, definiując nowy element członkowski z tym samym podpisem. Można to zrobić, aby wcześniej publiczny element członkowski był prywatny lub zdefiniować nowe zachowanie dla dziedziczonej metody, która jest oznaczona jako
sealed
.Typ pochodny może zastąpić dziedziczonej metody wirtualnej. Metoda zastępowania zawiera nową definicję metody, która zostanie wywołana na podstawie typu wartości w czasie wykonywania, a nie typu zmiennej znanej w czasie kompilacji. Metoda może zastąpić metodę wirtualną tylko wtedy, gdy metoda wirtualna nie jest oznaczona jako
sealed
i nowa metoda jest co najmniej tak dostępna jak metoda wirtualna.