泛型类 (C++/CLI)

使用以下形式,泛型类声明:

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

备注

在上面的语法中,使用了以下术语:

  • attributes(可选)
    附加描述性信息。 有关特性和特性类的更多信息,请参见属性。

  • 类键
    class 或 typename

  • 类型参数标识符
    逗号分隔指定类型参数的名称列表标识符。

  • 约束子句
    列表 (未逗号分隔) 指定绑定的 where 子句为类型参数。 采用形式:

    where类型参数标识符:约束列表...

  • 约束列表
    类或接口[, ]

  • 可访问性修饰符
    泛型类的可访问性修饰符。 对于 Windows 运行时,唯一允许的修饰符是 private。 对于公共语言运行时,允许修饰符是 private 和 public。

  • 标识符
    泛型类,任何有效的 C++ 标识符的名称。

  • (可选)
    允许修饰符包括 sealed 和 abstract

  • 基础列表
    逗号包含该基类以及任何实现的接口的列表,所有分隔。

  • 类主体
    类体中,包含字段,成员函数等。

  • 声明
    此类型的任何变量的声明。 例如:^标识符[, …]

可以声明泛型类类似以下 (请注意关键字 class 可以使用而不是 typename)。 在此示例中,ItemType、 KeyType 和 ValueType 是指定该点该类型的未知类型。 HashTable<int, int> 是泛型类型 HashTable<KeyType, ValueType>的构造类型。 许多不同的构造类型可以从单一泛型类型构造。 从泛型类构造的构造类型象任何其他 ref 类类型。

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

两个值类型 (内置类型 (如 int 或 double或用户定义的值类型) 和引用类型能用作泛型类型参数。 在泛型定义中的语法不相同。 语法上来说,未知类型视为,就好像它是引用类型。 但是,运行时可以确定,如果实际使用的类型是值类型和无需直接访问替换为相应的生成代码对成员。 为泛型类型使用的值类型参数不能包,所以不会陷入性能降低与装箱。 在体使用的语法泛型中应是 T^ 和 “->" 而非 “.”。 ,如果类型参数是值类型,对 ref new、gcnew(C++ 组件扩展) 类型的参数的所有使用将在运行时正确解释为值类型的简单创建。

还可以声明与 泛型类型参数的约束 (C++/CLI) 可为类型参数使用的类型的泛型类。 在下面的示例中 ItemType 的任何类型必须实现 IItem 接口。 尝试使用 int,例如,不实现 IItem,将会导致编译时错误,因为类型参数不满足约束。

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

在同一命名空间中的泛型类无法通过只更改数字或类型参数的类型重载。 但是,因此,如果每个类在不同命名空间中,可以重载它们。 例如,考虑以下两类, MyClass 和 MyClass<ItemType>,在命名空间 A 和 B。 两个类在第三个命名空间 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
      }
   };
}

基类和基接口不能是类型参数。 但是,基类包括该类型参数作为参数,如以下情况:

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

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

构造函数和析构函数为每个对象实例执行一次 (和平常一样);静态构造函数为每个构造类型执行一次。

在泛型类的字段

本节演示如何使用实例和静态字段在泛型类。

skef48fy.collapse_all(zh-cn,VS.110).gif实例变量

泛型类的实例变量可能具有由封闭类的任何类型参数的类型和变量的初始值设定项。

示例

通过使用适当的类型参数 (int、 doublestring),如下面的示例中,泛型类, MyClass <其中 ItemType> 的三个不同实例,创建。

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

使用静态字段和静态构造函数在泛型类中,如下面的示例所示。

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

下面的示例声明一个非泛型方法, ProtectData,在泛型类中, MyClass<ItemType>。 方法在其在打开构造类型的签名使用类类型参数 ItemType 。

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

该非泛型方法是泛型来讲,它由类的类型参数进行参数化,但是,它不具有不同的类型参数。

方案中的所有类型在泛型类的可能为泛型,包括静态和实例,虚方法。

下面的示例演示声明和使用泛型类中的泛型方法:

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

此示例演示实例属性的说明在泛型类中。

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

下一个示例演示了事件的泛型类。

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

下面的示例声明一个泛型结构, MyGenStruct,与一个字段, myField,并分配不同的类型 (int、 doubleString^) 的值更改为此字段。

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

静态变量

在一个新的泛型类型的创建,所有静态变量的新实例后,并且该类型的任何静态构造函数中执行。

静态变量可以使用封闭类的任何类型参数。

在泛型类的方法

在泛型类的方法可以为泛型这些权限;非泛型方法将由类类型参数隐式参数化。

以下特殊规则适用于泛型类中的方法:

  • 在泛型类的方法可以使用类型参数作为参数,返回类型或局部变量。

  • 在泛型类的方法可以使用开放式或作为参数的封闭式构造类型,则返回类型或局部变量。

skef48fy.collapse_all(zh-cn,VS.110).gif在泛型类的非泛型方法

在没有其他类型参数的泛型类的方法通常称为非泛型,尽管它们由封闭泛型类隐式参数化。

非泛型方法的签名可以直接包含一个或多个在打开构造类型的封闭类的类型参数,或。 例如:

void MyMethod(MyClass<ItemType> x) {}

主体这样的方法还可以使用这些类型参数。

在泛型类的泛型方法

可以声明在泛型和非泛型类的泛型方法。 例如:

使用嵌套在泛型类

按普通类,可以为该泛型类中的其他类型。 嵌套类声明由外部类声明的类型参数隐式参数化。 因此,清单的嵌套类为每个构造的外部类型定义。 例如,在声明,

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

类型外部 <int> :: 内部函数不与该类型外部 <二进制文件> 相同:: 内部。

与泛型类的泛型方法,附加类型参数可以为嵌套类型定义。 如果在内部和外部类使用同一类型参数名,该内部类型参数将隐藏该外部类型参数。

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

因为无法引用该外部类型参数,在这种情况下编译器将生成警告。

当构造的嵌套泛型类型名为时,该外部类型的类型参数在类型不包括为内部类型参数,因此,即使该内部类型由外部类型的类型参数隐式参数化。 上述情况,构造类型的名称为 Outer<int>:: Inner<string>。

下面的示例演示生成,并读取连接表使用嵌套在泛型类。

属性、事件、索引器和运算符泛型类的

  • ,当 ItemType 是类时,的类型参数属性、事件、索引器和运算符可以使用封闭泛型类的类型参数返回值,参数或局部变量,如:

    public ItemType MyProperty {}
    
  • 属性、事件、索引器和运算符不能其参数化。

泛型结构

规则用于声明和使用泛型结构相同的与泛型类的,但在 Visual C++ 语言注意的差异引用。

请参见

其他资源

泛型(C++ 组件扩展)