Obecné třídy (C++/CLI)

Obecná třída je deklarována pomocí následujícího formuláře:

Syntaxe

[attributes]
generic <class-key type-parameter-identifier(s)>
[constraint-clauses]
[accessibility-modifiers] ref class identifier  [modifiers]
[: base-list]
{
class-body
} [declarators] [;]

Poznámky

Ve výše uvedené syntaxi se používají následující termíny:

Atributy
(Volitelné) Další deklarativní informace Další informace o atributech a třídách atributů naleznete v tématu Atributy.

class-key
Buďto class nebo typename

type-parameter-identifier(s), čárkami oddělený seznam identifikátorů určující názvy parametrů typu.

constraint-clauses
Seznam (nikoli čárkami oddělených ) klauzulí where určující omezení parametrů typu. Přebírá formulář:

wheretype-parameter-identifier:constraint-list...

constraint-list
class-or-interface[,...]

modifikátory přístupnosti
Modifikátory přístupnosti pro obecnou třídu Pro prostředí Windows Runtime je jediným povoleným modifikátorem private. Pro modul CLR (Common Language Runtime) jsou private povolené modifikátory a public.

identifikátor
Název obecné třídy, libovolný platný identifikátor jazyka C++.

Modifikátory
(Volitelné) Povolené modifikátory zahrnují zapečetěné a abstraktní.

základní seznam
Seznam obsahující jednu základní třídu a všechna implementovaná rozhraní oddělená čárkami.

class-body
Tělo třídy obsahující pole, členské funkce atd.

deklarátory
Deklarace všech proměnných tohoto typu. Příklad: ^identifikátor[, ...]

Můžete deklarovat obecné třídy, jako jsou tyto (všimněte si, že klíčové slovo class lze použít místo typename). V tomto příkladu ItemTypeKeyType a ValueType jsou neznámé typy, které jsou zadány v bodě, kde typ. HashTable<int, int> je konstruovaný typ obecného typu HashTable<KeyType, ValueType>. Z jednoho obecného typu lze vytvořit řadu různých konstruovaných typů. Konstruované typy vytvořené z obecných tříd se považují za všechny ostatní typy ref třídy.

// generic_classes_1.cpp
// compile with: /clr
using namespace System;
generic <typename ItemType>
ref struct Stack {
   // ItemType may be used as a type here
   void Add(ItemType item) {}
};

generic <typename KeyType, typename ValueType>
ref class HashTable {};

// The keyword class may be used instead of typename:
generic <class ListItem>
ref class List {};

int main() {
   HashTable<int, Decimal>^ g1 = gcnew HashTable<int, Decimal>();
}

Oba typy hodnot (buď předdefinované typy, jako int jsou nebo double, nebo typy hodnot definované uživatelem) a odkazové typy mohou být použity jako obecný typ argument. Syntaxe v rámci obecné definice je stejná bez ohledu na to. Syntakticky se neznámý typ považuje za odkazový typ. Modul runtime však dokáže určit, že pokud je typ skutečně použitý typ hodnoty, a nahradit odpovídající vygenerovaný kód pro přímý přístup k členům. Typy hodnot používané jako argumenty obecného typu nejsou v rámečku a proto netrpí trestem výkonu spojené s boxingem. Syntaxe použitá v těle obecného by měla být T^ a -> ne .. Jakékoli použití ref new, gcnew pro parametr typu bude odpovídajícím způsobem interpretováno modulem runtime jako jednoduché vytvoření typu hodnoty, pokud je argument typu hodnota typ.

Můžete také deklarovat obecnou třídu s omezeními obecných parametrů typu (C++/CLI) u typů, které lze použít pro parametr typu. V následujícím příkladu musí implementovat rozhraní jakýkoli typ použitý pro ItemTypeIItem . Při pokusu o použití int, například který neimplementuje IItem, by došlo k chybě v době kompilace, protože argument typu nevyhovuje omezení.

// generic_classes_2.cpp
// compile with: /clr /c
interface class IItem {};
generic <class ItemType>
where ItemType : IItem
ref class Stack {};

Obecné třídy ve stejném oboru názvů nelze přetížit pouze změnou čísla nebo typů parametrů typu. Pokud ale každá třída žije v jiném oboru názvů, může být přetížena. Představte si například následující dvě třídy MyClass a MyClass<ItemType>v oborech názvů A a B. Dvě třídy pak mohou být přetíženy ve třetím oboru názvů C:

// generic_classes_3.cpp
// compile with: /clr /c
namespace A {
   ref class MyClass {};
}

namespace B {
   generic <typename ItemType>
   ref class MyClass2 { };
}

namespace C {
   using namespace A;
   using namespace B;

   ref class Test {
      static void F() {
         MyClass^ m1 = gcnew MyClass();   // OK
         MyClass2<int>^ m2 = gcnew MyClass2<int>();   // OK
      }
   };
}

Základní třída a základní rozhraní nemohou být parametry typu. Základní třída však může zahrnovat parametr typu jako argument, jako v následujícím případě:

// generic_classes_4.cpp
// compile with: /clr /c
generic <typename ItemType>
interface class IInterface {};

generic <typename ItemType>
ref class MyClass : IInterface<ItemType> {};

Konstruktory a destruktory se pro každou instanci objektu spouští jednou (obvykle); statické konstruktory se pro každý konstruovaný typ spouští jednou.

Pole v obecných třídách

Tato část ukazuje použití instancí a statických polí v obecných třídách.

Proměnné instance

Proměnné instance obecné třídy mohou mít typy a inicializátory proměnných, které obsahují všechny parametry typu z nadřazené třídy.

Příklad: Různé obecné třídy

V následujícím příkladu jsou vytvořeny tři různé instance obecné třídy MyClass<ItemType> pomocí odpovídajících argumentů typu (int, doublea string).

// generics_instance_fields1.cpp
// compile with: /clr
// Instance fields on generic classes
using namespace System;

generic <typename ItemType>
ref class MyClass {
// Field of the type ItemType:
public :
   ItemType field1;
   // Constructor using a parameter of the type ItemType:
   MyClass(ItemType p) {
   field1 = p;
   }
};

int main() {
   // Instantiate an instance with an integer field:
   MyClass<int>^ myObj1 = gcnew MyClass<int>(123);
   Console::WriteLine("Integer field = {0}", myObj1->field1);

   // Instantiate an instance with a double field:
   MyClass<double>^ myObj2 = gcnew MyClass<double>(1.23);
   Console::WriteLine("Double field = {0}", myObj2->field1);

   // Instantiate an instance with a String field:
   MyClass<String^>^ myObj3 = gcnew MyClass<String^>("ABC");
   Console::WriteLine("String field = {0}", myObj3->field1);
   }
Integer field = 123
Double field = 1.23
String field = ABC

Statické proměnné

Při vytváření nového obecného typu se vytvoří nové instance všech statických proměnných a spustí se jakýkoli statický konstruktor pro tento typ.

Statické proměnné můžou používat libovolné parametry typu z nadřazené třídy.

Příklad: Použití statických proměnných

Následující příklad ukazuje použití statických polí a statického konstruktoru v rámci obecné třídy.

// generics_static2.cpp
// compile with: /clr
using namespace System;

interface class ILog {
   void Write(String^ s);
};

ref class DateTimeLog : ILog {
public:
   virtual void Write(String^ s) {
      Console::WriteLine( "{0}\t{1}", DateTime::Now, s);
   }
};

ref class PlainLog : ILog {
public:
   virtual void Write(String^ s) { Console::WriteLine(s); }
};

generic <typename LogType>
where LogType : ILog
ref class G {
   static LogType s_log;

public:
   G(){}
   void SetLog(LogType log) { s_log = log; }
   void F() { s_log->Write("Test1"); }
   static G() { Console::WriteLine("Static constructor called."); }
};

int main() {
   G<PlainLog^>^ g1 = gcnew G<PlainLog^>();
   g1->SetLog(gcnew PlainLog());
   g1->F();

   G<DateTimeLog^>^ g2 = gcnew G<DateTimeLog^>();
   g2->SetLog(gcnew DateTimeLog());

   // prints date
   // g2->F();
}
Static constructor called.
Static constructor called.
Static constructor called.
Test1

Metody v obecných třídách

Metody v obecnýchtřídch Jiné než obecné metody budou implicitně parametrizovány parametrem typu třídy.

Následující zvláštní pravidla platí pro metody v rámci obecných tříd:

  • Metody v obecných třídách mohou používat parametry typu jako parametry, návratové typy nebo místní proměnné.

  • Metody v obecných třídách mohou jako parametry, návratové typy nebo místní proměnné používat otevřené nebo uzavřené konstruované typy.

Jiné než obecné metody v obecných třídách

Metody v obecnýchtřídch

Podpis jiné než obecné metody může obsahovat jeden nebo více parametrů typu ohraničující třídy, a to buď přímo, nebo v otevřeném vytvořeném typu. Příklad:

void MyMethod(MyClass<ItemType> x) {}

Tělo těchto metod může také použít tyto parametry typu.

Příklad: Deklarace ne generické metody

Následující příklad deklaruje ne generickou metodu , ProtectDatauvnitř obecné třídy, MyClass<ItemType>. Metoda používá parametr ItemType typu třídy v jeho podpisu v otevřeném vytvořeném typu.

// generics_non_generic_methods1.cpp
// compile with: /clr
// Non-generic methods within a generic class.
using namespace System;

generic <typename ItemType>
ref class MyClass {
public:
   String^ name;
   ItemType data;

   MyClass(ItemType x) {
      data = x;
   }

   // Non-generic method using the type parameter:
   virtual void ProtectData(MyClass<ItemType>^ x) {
      data = x->data;
   }
};

// ItemType defined as String^
ref class MyMainClass: MyClass<String^> {
public:
   // Passing "123.00" to the constructor:
   MyMainClass(): MyClass<String^>("123.00") {
      name = "Jeff Smith";
   }

   virtual void ProtectData(MyClass<String^>^ x) override {
      x->data = String::Format("${0}**", x->data);
   }

   static void Main() {
      MyMainClass^ x1 = gcnew MyMainClass();

      x1->ProtectData(x1);
      Console::WriteLine("Name: {0}", x1->name);
      Console::WriteLine("Amount: {0}", x1->data);
   }
};

int main() {
   MyMainClass::Main();
}
Name: Jeff Smith
Amount: $123.00**

Obecné metody v obecných třídách

Obecné metody můžete deklarovat v obecných i ne generických třídách. Příklad:

Příklad: Deklarace obecných a ne generických metod

// generics_method2.cpp
// compile with: /clr /c
generic <typename Type1>
ref class G {
public:
   // Generic method having a type parameter
   // from the class, Type1, and its own type
   // parameter, Type2
   generic <typename Type2>
   void Method1(Type1 t1, Type2 t2) { F(t1, t2); }

   // Non-generic method:
   // Can use the class type param, Type1, but not Type2.
   void Method2(Type1 t1) { F(t1, t1); }

   void F(Object^ o1, Object^ o2) {}
};

Ne generická metoda je stále obecná v tom smyslu, že je parametrizován parametrem typu třídy, ale nemá žádné další parametry typu.

Všechny typy metod v obecných třídách mohou být obecné, včetně statických, instancí a virtuálních metod.

Příklad: Deklarace a použití obecných metod

Následující příklad ukazuje deklarování a použití obecných metod v rámci obecných tříd:

// generics_generic_method2.cpp
// compile with: /clr
using namespace System;
generic <class ItemType>
ref class MyClass {
public:
   // Declare a generic method member.
   generic <class Type1>
   String^ MyMethod(ItemType item, Type1 t) {
      return String::Concat(item->ToString(), t->ToString());
   }
};

int main() {
   // Create instances using different types.
   MyClass<int>^ myObj1 = gcnew MyClass<int>();
   MyClass<String^>^ myObj2 = gcnew MyClass<String^>();
   MyClass<String^>^ myObj3 = gcnew MyClass<String^>();

   // Calling MyMethod using two integers.
   Console::WriteLine("MyMethod returned: {0}",
            myObj1->MyMethod<int>(1, 2));

   // Calling MyMethod using an integer and a string.
   Console::WriteLine("MyMethod returned: {0}",
            myObj2->MyMethod<int>("Hello #", 1));

   // Calling MyMethod using two strings.
   Console::WriteLine("MyMethod returned: {0}",
       myObj3->MyMethod<String^>("Hello ", "World!"));

   // generic methods can be called without specifying type arguments
   myObj1->MyMethod<int>(1, 2);
   myObj2->MyMethod<int>("Hello #", 1);
   myObj3->MyMethod<String^>("Hello ", "World!");
}
MyMethod returned: 12
MyMethod returned: Hello #1
MyMethod returned: Hello World!

Použití vnořených typů v obecných třídách

Stejně jako u běžných tříd můžete deklarovat jiné typy uvnitř obecné třídy. Deklarace vnořené třídy je implicitně parametrizována parametry typu deklarace vnější třídy. Proto je pro každý vytvořený vnější typ definována samostatná vnořená třída. Například v deklaraci,

// generic_classes_5.cpp
// compile with: /clr /c
generic <typename ItemType>
ref struct Outer {
   ref class Inner {};
};

Typ Outer<int>::Inner není stejný jako typ Outer<double>::Inner.

Stejně jako u obecných metod v obecných třídách lze pro vnořený typ definovat další parametry typu. Pokud použijete stejné názvy parametrů typu ve vnitřní a vnější třídě, vnitřní typ parametru skryje vnější typ parametr.

// generic_classes_6.cpp
// compile with: /clr /c
generic <typename ItemType>
ref class Outer {
   ItemType outer_item;   // refers to outer ItemType

   generic <typename ItemType>
   ref class Inner {
      ItemType inner_item;   // refers to Inner ItemType
   };
};

Vzhledem k tomu, že neexistuje způsob, jak odkazovat na vnější parametr typu, kompilátor v této situaci vytvoří upozornění.

Při vytváření vnořených obecných typů jsou pojmenovány, parametr typu pro vnější typ není zahrnut do seznamu parametrů typu pro vnitřní typ, i když vnitřní typ je implicitně parametrizován parametrem typu vnějšího typu. V předchozím případě by název vytvořeného typu byl Outer<int>::Inner<string>.

Příklad: Sestavení a čtení propojeného seznamu

Následující příklad ukazuje sestavení a čtení propojeného seznamu pomocí vnořených typů v obecných třídách.

// generics_linked_list.cpp
// compile with: /clr
using namespace System;
generic <class ItemType>
ref class LinkedList {
// The node class:
public:
   ref class Node {
   // The link field:
   public:
      Node^ next;
      // The data field:
      ItemType item;
   } ^first, ^current;
};

ref class ListBuilder {
public:
   void BuildIt(LinkedList<double>^ list) {
      /* Build the list */
      double m[5] = {0.1, 0.2, 0.3, 0.4, 0.5};
      Console::WriteLine("Building the list:");

      for (int n=0; n<=4; n++) {
         // Create a new node:
         list->current = gcnew LinkedList<double>::Node();

         // Assign a value to the data field:
         list->current->item = m[n];

         // Set the link field "next" to be the same as
         // the "first" field:
         list->current->next = list->first;

         // Redirect "first" to the new node:
         list->first = list->current;

         // Display node's data as it builds:
         Console::WriteLine(list->current->item);
      }
   }

   void ReadIt(LinkedList<double>^ list) {
      // Read the list
      // Make "first" the "current" link field:
      list->current = list->first;
      Console::WriteLine("Reading nodes:");

      // Read nodes until current == null:
      while (list->current != nullptr) {
         // Display the node's data field:
         Console::WriteLine(list->current->item);

         // Move to the next node:
         list->current = list->current->next;
      }
   }
};

int main() {
   // Create a list:
   LinkedList<double>^ aList = gcnew LinkedList<double>();

   // Initialize first node:
   aList->first = nullptr;

   // Instantiate the class, build, and read the list:
   ListBuilder^ myListBuilder = gcnew ListBuilder();
   myListBuilder->BuildIt(aList);
   myListBuilder->ReadIt(aList);
}
Building the list:
0.1
0.2
0.3
0.4
0.5
Reading nodes:
0.5
0.4
0.3
0.2
0.1

Vlastnosti, události, indexery a operátory v obecných třídách

  • Vlastnosti, události, indexery a operátory mohou jako návratové hodnoty, parametry nebo místní proměnné použít parametry typu ohraničující obecné třídy, například kdy ItemType je parametr typu třídy:

    public ItemType MyProperty {}
    
  • Vlastnosti, události, indexery a operátory nelze sami parametrizovat.

Příklad: Deklarace vlastnosti instance

Tento příklad ukazuje deklarace vlastnosti instance v rámci obecné třídy.

// generics_generic_properties1.cpp
// compile with: /clr
using namespace System;

generic <typename ItemType>
ref class MyClass {
private:
   property ItemType myField;

public:
   property ItemType MyProperty {
      ItemType get() {
         return myField;
      }
      void set(ItemType value) {
         myField = value;
      }
   }
};

int main() {
   MyClass<String^>^ c = gcnew MyClass<String^>();
   MyClass<int>^ c1 = gcnew MyClass<int>();

   c->MyProperty = "John";
   c1->MyProperty = 234;

   Console::Write("{0}, {1}", c->MyProperty, c1->MyProperty);
}
John, 234

Příklad: Obecná třída s událostí

Následující příklad ukazuje obecnou třídu s událostí.

// generics_generic_with_event.cpp
// compile with: /clr
// Declare a generic class with an event and
// invoke events.
using namespace System;

// declare delegates
generic <typename ItemType>
delegate void ClickEventHandler(ItemType);

// generic class that defines events
generic <typename ItemType>
ref class EventSource {
public:
   // declare the event OnClick
   event ClickEventHandler<ItemType>^ OnClick;
   void FireEvents(ItemType item) {
      // raises events
      OnClick(item);
   }
};

// generic class that defines methods that will called when
// event occurs
generic <typename ItemType>
ref class EventReceiver {
public:
   void OnMyClick(ItemType item) {
   Console::WriteLine("OnClick: {0}", item);
   }
};

int main() {
   EventSource<String^>^ MyEventSourceString =
                   gcnew EventSource<String^>();
   EventSource<int>^ MyEventSourceInt = gcnew EventSource<int>();
   EventReceiver<String^>^ MyEventReceiverString =
                   gcnew EventReceiver<String^>();
   EventReceiver<int>^ MyEventReceiverInt = gcnew EventReceiver<int>();

   // hook handler to event
   MyEventSourceString->OnClick += gcnew ClickEventHandler<String^>(
       MyEventReceiverString, &EventReceiver<String^>::OnMyClick);
   MyEventSourceInt->OnClick += gcnew ClickEventHandler<int>(
             MyEventReceiverInt, &EventReceiver<int>::OnMyClick);

   // invoke events
   MyEventSourceString->FireEvents("Hello");
   MyEventSourceInt->FireEvents(112);

   // unhook handler to event
   MyEventSourceString->OnClick -= gcnew ClickEventHandler<String^>(
        MyEventReceiverString, &EventReceiver<String^>::OnMyClick);
   MyEventSourceInt->OnClick -= gcnew ClickEventHandler<int>(
        MyEventReceiverInt, &EventReceiver<int>::OnMyClick);
}

Obecné struktury

Pravidla pro deklarování a používání obecných struktur jsou stejná jako pravidla pro obecné třídy s výjimkou rozdílů uvedených v odkazu jazyka Visual C++.

Příklad: Deklarace obecné struktury

Následující příklad deklaruje obecnou strukturu , MyGenStructs jedním polem myFielda přiřadí hodnoty různých typů (int, double, String^) k tomuto poli.

// generics_generic_struct1.cpp
// compile with: /clr
using namespace System;

generic <typename ItemType>
ref struct MyGenStruct {
public:
   ItemType myField;

   ItemType AssignValue(ItemType item) {
      myField = item;
      return myField;
   }
};

int main() {
   int myInt = 123;
   MyGenStruct<int>^ myIntObj = gcnew MyGenStruct<int>();
   myIntObj->AssignValue(myInt);
   Console::WriteLine("The field is assigned the integer value: {0}",
            myIntObj->myField);

   double myDouble = 0.123;
   MyGenStruct<double>^ myDoubleObj = gcnew MyGenStruct<double>();
   myDoubleObj->AssignValue(myDouble);
   Console::WriteLine("The field is assigned the double value: {0}",
            myDoubleObj->myField);

   String^ myString = "Hello Generics!";
   MyGenStruct<String^>^ myStringObj = gcnew MyGenStruct<String^>();
   myStringObj->AssignValue(myString);
   Console::WriteLine("The field is assigned the string: {0}",
            myStringObj->myField);
}
The field is assigned the integer value: 123
The field is assigned the double value: 0.123
The field is assigned the string: Hello Generics!

Viz také

Obecné typy