Udostępnij za pośrednictwem


Omówienie typów ogólnych w języku C++/interfejsie wiersza polecenia

Typy ogólne są typami sparametryzowanymi obsługiwanymi przez środowisko uruchomieniowe języka wspólnego. Typ sparametryzowany jest typem zdefiniowanym przy użyciu nieznanego parametru typu określonego podczas użycia ogólnego.

Dlaczego typy ogólne?

Język C++ obsługuje szablony oraz szablony i typy ogólne obsługują sparametryzowane typy do tworzenia klas kolekcji typizowanej. Jednak szablony zapewniają parametryzacja czasu kompilacji. Nie można odwołać się do zestawu zawierającego definicję szablonu i utworzyć nowe specjalizacje szablonu. Po skompilowaniu wyspecjalizowany szablon wygląda jak każda inna klasa lub metoda. Natomiast typy ogólne są emitowane w MSIL jako typ sparametryzowany znany przez środowisko uruchomieniowe jako typ sparametryzowany; kod źródłowy, który odwołuje się do zestawu zawierającego typ ogólny, może tworzyć specjalizacje typu ogólnego. Aby uzyskać więcej informacji na temat porównywania standardowych szablonów języka C++ i typów ogólnych, zobacz Generics and Templates (C++/CLI).

Funkcje ogólne i typy

Typy klas, o ile są typami zarządzanymi, mogą być ogólne. Przykładem może być List klasa. Typ obiektu na liście będzie parametrem typu. Jeśli potrzebujesz List klasy dla wielu różnych typów obiektów, zanim typy ogólne zostały użyte List System::Object jako typ elementu. Pozwoliłoby to jednak na używanie dowolnego obiektu (w tym obiektów niewłaściwego typu) na liście. Taka lista będzie nazywana nietypową klasą kolekcji. W najlepszym razie można sprawdzić typ w czasie wykonywania i zgłosić wyjątek. Można też użyć szablonu, który utraciłby jakość ogólną po skompilowaniu do zestawu. Użytkownicy zestawu nie mogli utworzyć własnych specjalizacji szablonu. Typy ogólne umożliwiają tworzenie typowych klas kolekcji, np List<int> . (odczytanych jako "Lista int") i List<double> ("Lista podwójnej"), co spowodowałoby wygenerowanie błędu czasu kompilacji, jeśli próbowano umieścić typ, którego kolekcja nie została zaprojektowana do akceptowania w typowanej kolekcji. Ponadto te typy pozostają ogólne po ich skompilowaniu.

Opis składni klas ogólnych można znaleźć w klasach ogólnych (C++/CLI). Nowa przestrzeń nazw , System.Collections.Genericwprowadza zestaw sparametryzowanych typów kolekcji, w tym Dictionary<TKey,TValue>, List<T> i LinkedList<T>.

Zarówno funkcje składowe wystąpień, jak i statycznych składowych klasy, delegaty i funkcje globalne mogą być również ogólne. Funkcje ogólne mogą być konieczne, jeśli parametry funkcji są nieznanego typu lub jeśli sama funkcja musi pracować z typami ogólnymi. W wielu przypadkach, w których System::Object można było użyć w przeszłości jako parametru dla nieznanego typu obiektu, można zamiast tego użyć ogólnego parametru typu, co pozwala na bardziej bezpieczny kod typu. Każda próba przekazania typu, dla którego funkcja nie została zaprojektowana, zostanie oznaczona jako błąd w czasie kompilacji. Przy użyciu System::Object jako parametru funkcji nieumyślne przekazanie obiektu, z którym funkcja nie miała do czynienia, nie zostanie wykryta, i trzeba będzie rzutować nieznany typ obiektu do określonego typu w treści funkcji i uwzględnić możliwość invalidCastException. W przypadku ogólnego kodu próbującego przekazać obiekt do funkcji spowodowałoby konflikt typu, dzięki czemu treść funkcji ma gwarancję poprawnego typu.

Te same korzyści mają zastosowanie do klas kolekcji opartych na rodzajach ogólnych. Klasy kolekcji w przeszłości będą używane System::Object do przechowywania elementów w kolekcji. Wstawienie obiektów typu, dla którego kolekcja nie została zaprojektowana, nie została oflagowana w czasie kompilacji, a często nawet wtedy, gdy obiekty zostały wstawione. Zazwyczaj obiekt jest rzutowany na inny typ, gdy był dostępny w kolekcji. Tylko wtedy, gdy rzutowanie nie powiodło się, wykryto nieoczekiwany typ. Typy ogólne rozwiązuje ten problem w czasie kompilacji, wykrywając dowolny kod, który wstawia typ, który nie jest zgodny (lub niejawnie konwertuje na) parametr typu kolekcji ogólnej.

Aby uzyskać opis składni, zobacz Funkcje ogólne (C++/CLI).

Terminologia używana z rodzajami ogólnymi

Parametry typu

Deklaracja ogólna zawiera co najmniej jeden nieznany typ znany jako parametry typu. Parametry typu otrzymują nazwę, która oznacza typ w treści deklaracji ogólnej. Parametr typu jest używany jako typ w treści deklaracji ogólnej. Deklaracja ogólna dla List<T> elementu zawiera parametr typu T.

Argumenty typu

Argument typu jest rzeczywistym typem używanym zamiast parametru typu, gdy typ ogólny jest wyspecjalizowany dla określonego typu lub typów. Na przykład int jest argumentem typu w pliku List<int>. Typy wartości i typy dojść są jedynymi typami dozwolonymi jako argument typu ogólnego.

Typ skonstruowany

Typ skonstruowany na podstawie typu ogólnego jest określany jako typ skonstruowany. Typ nie jest w pełni określony, taki jak List<T> otwarty typ skonstruowany; typ w pełni określony, taki jak List<double>, jest typu zamkniętego skonstruowanego lub wyspecjalizowanego typu. Otwarte typy konstruowane mogą być używane w definicji innych typów ogólnych lub metod i mogą nie być w pełni określone do momentu określenia otaczającego rodzaju ogólnego. Na przykład poniżej przedstawiono użycie typu otwartego skonstruowanego jako klasy bazowej dla klasy ogólnej:

// generics_overview.cpp
// compile with: /clr /c
generic <typename T>

ref class List {};

generic <typename T>

ref class Queue : public List<T> {};

Ograniczenie

Ograniczenie to ograniczenie dotyczące typów, które mogą być używane jako parametr typu. Na przykład dana klasa ogólna może akceptować tylko klasy dziedziczone z określonej klasy lub implementować określony interfejs. Aby uzyskać więcej informacji, zobacz Ograniczenia dotyczące parametrów typu ogólnego (C++/CLI).

Typy referencyjne i typy wartości

Dojścia typy i typy wartości mogą być używane jako argumenty typu. W definicji ogólnej, w której może być używany dowolny typ, składnia jest typu odwołania. Na przykład operator jest używany do uzyskiwania dostępu do elementów członkowskich typu parametru typu, -> czy typ ostatecznie używany jest typem odwołania lub typem wartości. Gdy typ wartości jest używany jako argument typu, środowisko uruchomieniowe generuje kod, który używa typów wartości bezpośrednio bez tworzenia pól typów wartości.

W przypadku używania typu odwołania jako argumentu typu ogólnego użyj składni obsługi. W przypadku używania typu wartości jako argumentu typu ogólnego użyj nazwy typu bezpośrednio.

// generics_overview_2.cpp
// compile with: /clr
generic <typename T>

ref class GenericType {};
ref class ReferenceType {};

value struct ValueType {};

int main() {
    GenericType<ReferenceType^> x;
    GenericType<ValueType> y;
}

Parametry typu

Parametry typu w klasie ogólnej są traktowane jak inne identyfikatory. Jednak ponieważ typ nie jest znany, istnieją ograniczenia dotyczące ich użycia. Na przykład nie można użyć składowych i metod klasy parametrów typu, chyba że parametr typu jest znany do obsługi tych składowych. Oznacza to, że aby uzyskać dostęp do elementu członkowskiego za pośrednictwem parametru typu, należy dodać typ zawierający element członkowski do listy ograniczeń parametru typu.

// generics_overview_3.cpp
// compile with: /clr
interface class I {
   void f1();
   void f2();
};

ref struct R : public I {
   virtual void f1() {}
   virtual void f2() {}
   virtual void f3() {}
};

generic <typename T>
where T : I
void f(T t) {
   t->f1();
   t->f2();
   safe_cast<R^>(t)->f3();
}

int main() {
   f(gcnew R());
}

Te ograniczenia dotyczą również operatorów. Nieobsługiwany parametr typu ogólnego może nie używać == operatorów i != do porównywania dwóch wystąpień parametru typu, jeśli typ nie obsługuje tych operatorów. Te testy są niezbędne dla typów ogólnych, ale nie dla szablonów, ponieważ typy ogólne mogą być wyspecjalizowane w czasie wykonywania z dowolną klasą spełniającą ograniczenia, gdy jest za późno, aby sprawdzić użycie nieprawidłowych elementów członkowskich.

Domyślne wystąpienie parametru typu może zostać utworzone przy użyciu () operatora . Na przykład:

T t = T();

gdzie T jest parametrem typu w klasie ogólnej lub definicji metody, inicjuje zmienną do jej wartości domyślnej. Jeśli jest klasą ref, będzie to wskaźnik o wartości null. Jeśli T T jest klasą wartości, obiekt jest inicjowany do zera. Jest to nazywane domyślnym inicjatorem.

Zobacz też

Typy ogólne