Sdílet prostřednictvím


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

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

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

Poznámky

Nad syntaxe jsou použity následující termíny:

  • attributes (volitelné)
    Další deklarativní informace.Další informace o atributy a třídy atributu viz atributy.

  • class-key
    Buď class nebotypename

  • Typ-parametr-identifikační kódy GMO,
    Čárkami oddělený seznam identifikátorů zadání názvů parametrů typu.

  • klauzule CONSTRAINT
    Seznam (ne oddělený čárkami) kde ustanovení určující omezení pro parametry typu.Má podobu:

    where type-parameter-identifier : constraint-list ...

  • omezení seznamu
    class-or-interface, ...

  • Modifikátory dostupnosti
    Modifikátory dostupnosti pro obecné třídy.Pro prostředí Windows Runtime, povoleny Modifikátor je pouze private.Společný jazykový modul runtime, jsou povolené modifikátory private a public.

  • identifier
    Název obecná třída jakýkoli platný identifikátor jazyka C++.

  • Modifikátory (volitelné)
    Mohou zahrnovat modifikátory sealed a abstraktní.

  • Přehled základních
    Seznam, který obsahuje jeden základní třídy a všechny implementováno rozhraní, všechny oddělených čárkami.

  • Tělo třídy
    Tělo třídy obsahující pole členské funkce, např.

  • declarators
    Deklarace proměnných tohoto typu.For example: ^identifier, ...]

Můžete deklarovat jako jsou tyto obecné třídy (Všimněte si, že klíčové slovo třídy mohou být použity namísto typename).V tomto příkladu ItemType, KeyType a ValueType jsou neznámé typy, které jsou uvedeny v bodě, kde typ.HashTable<int, int>je vyrobeno obecný typ typ HashTable<KeyType, ValueType>.Počet různých typů konstrukce lze zkonstruovat z jednoho obecného typu.Konstruované typy, které jsou zhotoveny z obecné třídy jsou zpracovány stejně jako jakýkoli jiný typ ref class.

// 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ď vestavěné typy, jako int nebo double, nebo typy uživatelem definovaných hodnot) a referenční typy lze použít jako obecný typ argumentů.V rámci obecné definice syntaxe je stejná bez ohledu na to.Neznámý typ syntakticky, je považována, jako by to byl typ odkazu.Modul runtime je však možné určit, zda-li skutečně použitý typ je typ hodnoty a nahradit odpovídající generovaný kód pro přímý přístup k členům.Použít jako obecný typ argumentů typů hodnot nejsou zabaleny a tak nebude trpět penalizace přidružené k zabalení.Syntaxe používané v rámci obecného textu by měla být T ^ a '–>'místo od'.'.Jakékoli použití ref new, gcnew (rozšíření komponent C++) typu parametru vhodně považován modulem runtime za vytvoření jednoduchého typu hodnoty je-li argument typ je typ hodnoty.

Můžete také deklarovat třídu obecný s Omezení parametrů obecných typů (C++/CLI) na typy, které lze použít pro parametr typu.V následujícím příkladu použít kterýkoli typ pro ItemType musí implementovat IItem rozhraní.Při pokusu o použití int, například, který neimplementuje IItem, vyprodukuje chybu v době kompilace, protože argument typ nesplňuje 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ů nesmí být přetížené pouze změnou číslo nebo typy parametrů typu.Však pokud každé třídy je umístěn v různých názvů, jejich může být přetížený.Zvažte například následující dvě třídy MyClass a MyClass<ItemType>, v oboru názvů A a B.Dvě třídy lze potom přetížení v oboru třetí 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í nemůže mít parametry typu.Základní třídy však může zahrnovat typ parametru 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 jsou prováděny jednou pro každou instanci objektu (jako obvykle); statické konstruktory jsou prováděny jednou pro každý typ konstrukce.

Pole Obecné třídy

Tato část ukazuje použití statické pole v obecné třídy a instance.

Proměnné instance

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

Příklad

V následujícím příkladu tři různé instance obecné třídy MyClass<ItemType>, jsou vytvořeny pomocí vhodných argumentů (int, dvojité, a řetězec).

// 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);
   }
  

Následující příklad ukazuje použití statické pole a statický konstruktor 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();
}
  

Následující příklad deklaruje metodu obecný, ProtectData, uvnitř obecná třída MyClass<ItemType>.Metoda používá parametr typu třídy ItemType v podpisu v otevřené konstruovaný typ.

// 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();
}
  
// 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) {}
};

Neobecnou metodu je stále obecná v tom smyslu je parametrizována parametr typu třídy, že nemá žádné další typy parametrů.

Všechny typy metod v obecné třídy mohou být obecné, včetně statického, instance a virtuální metody.

Následující příklad ukazuje deklaraci a používání generických metod v rámci obecné třídy:

// 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!");
}
  
// 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);
}
  

Tento příklad ukazuje prohlášení vlastnost 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);
}
  

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);
}

Následující příklad deklaruje obecná struktura MyGenStruct, s jedním polem, myFielda přiřadí hodnoty různých typů (int, dvojité, String ^) do tohoto pole.

// 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);
}
  

Statické proměnné

Při vytváření nového obecného typu jsou vytvořeny nové instance všech statických proměnných a libovolný statický konstruktor pro tento typ je provedena.

Statické proměnné lze použít všechny parametry typu z nadřazené třídy.

Metody v obecné třídy

Metody v obecné třídy mohou být obecné. Obecný metody budou implicitně parametrizovanou parametr typu třídy.

Následující zvláštní pravidla platí pro metody v rámci obecné třídy:

  • Metody v obecné třídy můžete použít parametry typu jako lokální proměnné, parametry a návratové typy.

  • Metody v obecné třídy můžete použít otevřené nebo zavřené konstruované typy jako lokální proměnné, parametry a návratové typy.

Obecný metody v obecné třídy

V obecné třídy, které mají žádné další typy parametrů metody jsou obvykle označovány jako obecný i když jsou implicitně parametrizovanou ohraničující obecná třída.

Podpis neobecnou metodu může obsahovat jeden nebo více parametrů typu nadřazené třídy, buď přímo, nebo v otevřené konstruovaný typ.Příklad:

void MyMethod(MyClass<ItemType> x) {}

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

Obecné metody v obecné třídy

Můžete deklarovat v obecných a neobecných tříd obecné metody.Příklad:

Vnořené typy v obecné třídy

Stejně jako běžné třídy, můžete deklarovat jiné typy uvnitř obecná třída.Vnořené třídy prohlášení implicitně parametrizovanou parametry typu deklarace třídy vnější.Odlišná vnořená třída je tedy definována pro každý typ konstrukce vnější.Například v prohlášení,

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

Typ vnější<int>:: vnitřní není stejný jako typ vnější<dvojité>:: vnitřní.

Stejně jako u obecné metody v obecné třídy lze definovat další typové parametry u vnořeného typu.Pokud používáte stejné názvy parametrů typu ve třídě vnitřní a vnější, vnitřní typ parametru bude skrýt vnější typ parametru.

// 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
   };
};

Protože neexistuje žádný způsob, jak odkazovat na vnější typ parametru, kompilátor bude vyrábět upozornění v této situaci.

Pokud jsou pojmenovány konstruované vnořených obecné typy, typový parametr pro vnější typ není součástí seznamu Typ parametru pro vnitřní typ, přestože parametr typu vnějšího typu implicitně parametrizovanou vnitřní typ.Ve výše uvedeném případě bude název konstruovaný typ vnější<int>:: vnitřní<řetězec>.

Následující příklad ukazuje vytvoření a čtení propojeného seznamu pomocí vnořené typy v obecné třídy.

Vlastnosti, události, indexování a operátory v obecné třídy

  • Vlastnosti, události, indexování a operátory můžete použít parametry typu ohraničující třídy obecný jako vrácené hodnoty, parametry nebo lokální proměnné, například při ItemType je parametr typu třídy:

    public ItemType MyProperty {}
    
  • Vlastnosti, události, indexování a hospodářské subjekty nemohou samy být parametrické.

Obecné struktury

Pravidla pro deklarování a použití obecné struktury jsou stejné jako u obecné třídy, s výjimkou rozdílů uvedených v v reference jazyka Visual C++.

Viz také

Další zdroje

Obecné typy (rozšíření komponent C++)