Genel Sınıflar (C++/CLI)
Genel bir sınıf aşağıdaki form kullanılarak bildirilir:
Sözdizimi
[attributes]
generic <class-key type-parameter-identifier(s)>
[constraint-clauses]
[accessibility-modifiers] ref class identifier [modifiers]
[: base-list]
{
class-body
} [declarators] [;]
Açıklamalar
Yukarıdaki söz diziminde aşağıdaki terimler kullanılır:
Öznitelik
(İsteğe bağlı) Ek bildirim bilgileri. Öznitelikler ve öznitelik sınıfları hakkında daha fazla bilgi için bkz. Öznitelikler.
class-key
class
veya typename
type-parameter-identifier(lar),tür parametrelerinin adlarını belirten virgülle ayrılmış tanımlayıcı listesi.
constraint-clauses
Tür parametreleri için kısıtlamaları belirten where yan tümcelerinin listesi (virgülle ayrılmamış). Şu formu alır:
burada type-parameter-identifier : constraint-list ...
constraint-list
class-or-interface[,
...]
erişilebilirlik değiştiricileri
Genel sınıf için erişilebilirlik değiştiricileri. Windows Çalışma Zamanı için izin verilen tek değiştiricidirprivate
. Ortak dil çalışma zamanı için izin verilen değiştiriciler ve public
şeklindedirprivate
.
identifier
Genel sınıfın adı, geçerli herhangi bir C++ tanımlayıcısı.
Değiştirici
(İsteğe bağlı) İzin verilen değiştiriciler korumalı ve soyut içerir.
temel liste
Tek bir temel sınıfı ve uygulanan arabirimleri içeren, tümü virgülle ayrılmış bir liste.
sınıf gövdesi
Alanlar, üye işlevleri vb. içeren sınıfın gövdesi.
Declarators
Bu tür değişkenlerin bildirimleri. Örneğin: ^
identifier[,
...]
Bunlar gibi genel sınıflar bildirebilirsiniz (yerine anahtar sözcüğün class
kullanılabileceğini typename
unutmayın). Bu örnekte, ItemType
KeyType
ve ValueType
türün bulunduğu noktada belirtilen bilinmeyen türlerdir. HashTable<int, int>
, genel türünün yapılı bir türüdür HashTable<KeyType, ValueType>
. Tek bir genel türden bir dizi farklı tür oluşturulabilir. Genel sınıflardan oluşturulmuş türler, diğer başvuru sınıfı türleri gibi ele alınıyor.
// 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>();
}
Hem değer türleri (veya double
gibi int
yerleşik türler ya da kullanıcı tanımlı değer türleri) ve başvuru türleri genel tür bağımsız değişkeni olarak kullanılabilir. Genel tanımın içindeki söz dizimi ne olursa olsun aynıdır. Bozulmadan, bilinmeyen tür bir başvuru türü gibi değerlendirilir. Ancak çalışma zamanı, gerçekten kullanılan türün bir değer türü olup olmadığını belirleyebilir ve üyelere doğrudan erişim için uygun oluşturulan kodun yerini alır. Genel tür bağımsız değişkenleri olarak kullanılan değer türleri kutulanmaz ve bu nedenle kutulamayla ilişkili performans cezasına maruz olmaz. Genel gövdesinde kullanılan söz dizimi yerine ve ->
.
olmalıdırT^
. Tür parametresi için ref new, gcnew kullanımı, tür bağımsız değişkeni bir değer türüyse, çalışma zamanı tarafından basit bir değer türü oluşturma olarak yorumlanır.
Ayrıca tür parametresi için kullanılabilecek türlerde Genel Tür Parametrelerinde (C++/CLI) Kısıtlamalara sahip genel bir sınıf bildirebilirsiniz. Aşağıdaki örnekte için ItemType
kullanılan herhangi bir türün IItem
arabirimi uygulaması gerekir. tür bağımsız değişkeni kısıtlamayı int
karşılamadığından, örneğin, uygulamasını IItem
kullanmaya çalışmak derleme zamanı hatasına neden olur.
// generic_classes_2.cpp
// compile with: /clr /c
interface class IItem {};
generic <class ItemType>
where ItemType : IItem
ref class Stack {};
Aynı ad alanında yer alan genel sınıflar yalnızca sayı veya tür parametrelerinin türleri değiştirilerek aşırı yüklenemez. Ancak, her sınıf farklı bir ad alanında yaşıyorsa, bunlar aşırı yüklenebilir. Örneğin, ve ad alanları A
içinde aşağıdaki iki sınıfı MyClass
ve MyClass<ItemType>
B
değerini göz önünde bulundurun. Bu iki sınıf daha sonra üçüncü bir C ad alanında aşırı yüklenebilir:
// 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
}
};
}
Temel sınıf ve temel arabirimler tür parametreleri olamaz. Ancak temel sınıf, aşağıdaki örnekte olduğu gibi tür parametresini bağımsız değişken olarak içerebilir:
// generic_classes_4.cpp
// compile with: /clr /c
generic <typename ItemType>
interface class IInterface {};
generic <typename ItemType>
ref class MyClass : IInterface<ItemType> {};
Oluşturucular ve yıkıcılar her nesne örneği için bir kez yürütülür (her zamanki gibi); statik oluşturucular, oluşturulan her tür için bir kez yürütülür.
Genel Sınıflardaki Alanlar
Bu bölümde genel sınıflarda örnek ve statik alanların kullanımı gösterilmektedir.
Örnek Değişkenleri
Genel sınıfın örnek değişkenleri, kapsayan sınıftan herhangi bir tür parametresi içeren türler ve değişken başlatıcılara sahip olabilir.
Örnek: Farklı genel sınıflar
Aşağıdaki örnekte, myClass<ItemType> genel sınıfının üç farklı örneği uygun tür bağımsız değişkenleri (int
, double
ve dizesi) kullanılarak oluşturulur.
// 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
Statik Değişkenler
Yeni bir genel tür oluşturulurken, herhangi bir statik değişkenin yeni örnekleri oluşturulur ve bu tür için herhangi bir statik oluşturucu yürütülür.
Statik değişkenler, kapsayan sınıftan herhangi bir tür parametresi kullanabilir.
Örnek: Statik değişkenleri kullanma
Aşağıdaki örnekte, genel bir sınıf içinde statik alanların ve statik oluşturucunun kullanılması gösterilmektedir.
// 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
Genel Sınıflardaki Yöntemler
Genel sınıflardaki yöntemler genel olabilir; genel olmayan yöntemler, sınıf türü parametresi tarafından örtük olarak parametrelendirilir.
Aşağıdaki özel kurallar genel sınıflar içindeki yöntemler için geçerlidir:
Genel sınıflardaki yöntemler tür parametrelerini parametre, dönüş türü veya yerel değişken olarak kullanabilir.
Genel sınıflardaki yöntemler parametre, dönüş türü veya yerel değişken olarak açık veya kapalı yapılı türleri kullanabilir.
Genel Sınıflarda Genel Olmayan Yöntemler
Ek tür parametresi olmayan genel sınıflardaki yöntemler genellikle genel olmayan olarak adlandırılır, ancak bunlar kapsayan genel sınıf tarafından örtük olarak parametrelendirilir.
Genel olmayan bir yöntemin imzası, kapsayan sınıfın doğrudan veya açık oluşturulmuş bir türdeki bir veya daha fazla tür parametresini içerebilir. Örneğin:
void MyMethod(MyClass<ItemType> x) {}
Bu tür yöntemlerin gövdesi de bu tür parametreleri kullanabilir.
Örnek: Genel olmayan yöntem bildirme
Aşağıdaki örnek, ProtectData
genel bir sınıf MyClass<ItemType>
içinde genel olmayan bir yöntem bildirir. yöntemi, açık oluşturulmuş bir türdeki imzasında sınıf türü parametresini ItemType
kullanır.
// 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**
Genel Sınıflarda Genel Yöntemler
Hem genel hem de genel olmayan sınıflarda genel yöntemler bildirebilirsiniz. Örneğin:
Örnek: Genel ve genel olmayan yöntemleri bildirme
// 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) {}
};
Genel olmayan yöntem, sınıfın tür parametresi tarafından parametrelendirildiği için hala geneldir, ancak ek tür parametresi yoktur.
Genel sınıflardaki tüm yöntem türleri statik, örnek ve sanal yöntemler de dahil olmak üzere genel olabilir.
Örnek: Genel yöntemleri bildirme ve kullanma
Aşağıdaki örnekte, genel sınıflar içinde genel yöntemlerin bildirilmesi ve kullanılması gösterilmektedir:
// 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!
Genel Sınıflarda İç İçe Türleri Kullanma
Sıradan sınıflarda olduğu gibi, genel bir sınıf içinde diğer türleri bildirebilirsiniz. İç içe sınıf bildirimi, dış sınıf bildiriminin tür parametreleri tarafından örtük olarak parametreleştirilir. Bu nedenle, her oluşturulmuş dış tür için ayrı bir iç içe sınıf tanımlanır. Örneğin, bildirimde,
// generic_classes_5.cpp
// compile with: /clr /c
generic <typename ItemType>
ref struct Outer {
ref class Inner {};
};
Türü Outer<int>::Inner
, türüyle Outer<double>::Inner
aynı değildir.
Genel sınıflardaki genel yöntemlerde olduğu gibi, iç içe türü için ek tür parametreleri tanımlanabilir. İç ve dış sınıfta aynı tür parametre adlarını kullanırsanız, iç tür parametresi dış tür parametresini gizler.
// 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
};
};
Dış tür parametresine başvurmanın bir yolu olmadığından, derleyici bu durumda bir uyarı üretir.
İç içe yerleştirilmiş genel türler oluşturulduğunda, dış türün tür parametresi, iç türün tür parametresi tarafından örtük olarak parametreleştirilmiş olsa bile, iç türün tür parametresi listesine dahil değildir. Yukarıdaki örnekte, bir yapı türü adı olacaktır Outer<int>::Inner<string>
.
Örnek: Bağlantılı liste oluşturma ve okuma
Aşağıdaki örnek, genel sınıflarda iç içe türleri kullanarak bağlantılı liste oluşturmayı ve okumayı gösterir.
// 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
Genel Sınıflarda Özellikler, Olaylar, Dizin Oluşturucular ve İşleçler
Özellikler, olaylar, dizin oluşturucular ve işleçler, bir sınıfın tür parametresi olduğu durumlar
ItemType
gibi dönüş değerleri, parametreler veya yerel değişkenler olarak kapsayan genel sınıfın tür parametrelerini kullanabilir:public ItemType MyProperty {}
Özellikler, olaylar, dizin oluşturucular ve işleçler parametrelendirilemez.
Örnek: Örnek özelliğini bildirme
Bu örnek, genel bir sınıf içindeki örnek özelliğinin bildirimlerini gösterir.
// 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
Örnek: Olay içeren genel sınıf
Sonraki örnekte olay içeren genel bir sınıf gösterilmektedir.
// 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);
}
Genel Yapılar
Genel yapıları bildirme ve kullanma kuralları, Visual C++ dil başvurusunda belirtilen farklar dışında genel sınıflarla aynıdır.
Örnek: Genel yapıyı bildirme
Aşağıdaki örnek, MyGenStruct
bir alanı olan genel bir yapı bildirir ve bu alana myField
farklı türlerdeki (int
, double
, String^
) değerleri atar.
// 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!