Share via


Pantulan (C++/CLI)

Refleksi memungkinkan jenis data yang diketahui diperiksa saat runtime. Pantulan memungkinkan enumerasi jenis data dalam rakitan tertentu, dan anggota kelas atau jenis nilai tertentu dapat ditemukan. Ini berlaku terlepas dari apakah jenis tersebut diketahui atau dirujuk pada waktu kompilasi. Hal ini menjadikan refleksi fitur yang berguna untuk alat manajemen pengembangan dan kode.

Perhatikan bahwa nama rakitan yang disediakan adalah nama yang kuat (lihat Membuat dan Menggunakan Rakitan Dengan Nama Kuat), yang mencakup versi perakitan, budaya, dan informasi penandatanganan. Perhatikan juga bahwa nama namespace layanan tempat jenis data ditentukan dapat diambil, bersama dengan nama kelas dasar.

Cara paling umum untuk mengakses fitur refleksi adalah melalui GetType metode . Metode ini disediakan oleh System.Object, dari mana semua kelas yang dikumpulkan sampah berasal.

Catatan

Refleksi pada .exe yang dibangun dengan pengkompilasi Microsoft C++ hanya diperbolehkan jika .exe dibangun dengan opsi kompilator /clr:pure atau /clr:safe . Opsi pengkompilasi /clr:pure dan /clr:safe tidak digunakan lagi di Visual Studio 2015 dan tidak tersedia di Visual Studio 2017. Lihat /clr (Kompilasi Runtime Bahasa Umum) untuk informasi selengkapnya.

Untuk informasi selengkapnya, lihat System.Reflection

Contoh: GetType

Metode mengembalikan GetType penunjuk ke Type objek kelas, yang menjelaskan jenis saat objek didasarkan. (Objek jenis tidak berisi informasi khusus instans apa pun.) Salah satu item tersebut adalah nama lengkap jenis, yang dapat ditampilkan sebagai berikut:

Perhatikan bahwa nama jenis menyertakan cakupan lengkap di mana jenis ditentukan, termasuk namespace layanan, dan bahwa nama tersebut ditampilkan dalam sintaks .NET, dengan titik sebagai operator resolusi cakupan.

// vcpp_reflection.cpp
// compile with: /clr
using namespace System;
int main() {
   String ^ s = "sample string";
   Console::WriteLine("full type name of '{0}' is '{1}'", s, s->GetType());
}
full type name of 'sample string' is 'System.String'

Contoh: jenis nilai kotak

Jenis nilai juga dapat digunakan dengan GetType fungsi, tetapi harus dikotak terlebih dahulu.

// vcpp_reflection_2.cpp
// compile with: /clr
using namespace System;
int main() {
   Int32 i = 100;
   Object ^ o = i;
   Console::WriteLine("type of i = '{0}'", o->GetType());
}
type of i = 'System.Int32'

Contoh: typeid

Seperti metode , GetTypeoperator typeid mengembalikan penunjuk ke objek Jenis , sehingga kode ini menunjukkan nama jenis System.Int32. Menampilkan nama jenis adalah fitur refleksi yang paling dasar, tetapi teknik yang berpotensi lebih berguna adalah memeriksa atau menemukan nilai yang valid untuk jenis enumerasi. Ini dapat dilakukan dengan menggunakan fungsi Enum::GetNames statis, yang mengembalikan array string, masing-masing berisi nilai enumerasi dalam bentuk teks. Sampel berikut mengambil array string yang menjelaskan nilai enumerasi nilai untuk enum Opsi (CLR) dan menampilkannya dalam perulangan.

Jika opsi keempat ditambahkan ke enumerasi Opsi , kode ini akan melaporkan opsi baru tanpa kompilasi ulang, bahkan jika enumerasi ditentukan dalam rakitan terpisah.

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

enum class Options {   // not a native enum
   Option1, Option2, Option3
};

int main() {
   array<String^>^ names = Enum::GetNames(Options::typeid);

   Console::WriteLine("there are {0} options in enum '{1}'",
               names->Length, Options::typeid);

   for (int i = 0 ; i < names->Length ; i++)
      Console::WriteLine("{0}: {1}", i, names[i]);

   Options o = Options::Option2;
   Console::WriteLine("value of 'o' is {0}", o);
}
there are 3 options in enum 'Options'
0: Option1
1: Option2
2: Option3
value of 'o' is Option2

Contoh: Anggota dan properti GetType

Objek GetType mendukung sejumlah anggota dan properti yang dapat digunakan untuk memeriksa jenis. Kode ini mengambil dan menampilkan beberapa informasi ini:

// vcpp_reflection_4.cpp
// compile with: /clr
using namespace System;
int main() {
   Console::WriteLine("type information for 'String':");
   Type ^ t = String::typeid;

   String ^ assemblyName = t->Assembly->FullName;
   Console::WriteLine("assembly name: {0}", assemblyName);

   String ^ nameSpace = t->Namespace;
   Console::WriteLine("namespace: {0}", nameSpace);

   String ^ baseType = t->BaseType->FullName;
   Console::WriteLine("base type: {0}", baseType);

   bool isArray = t->IsArray;
   Console::WriteLine("is array: {0}", isArray);

   bool isClass = t->IsClass;
   Console::WriteLine("is class: {0}", isClass);
}
type information for 'String':
assembly name: mscorlib, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
namespace: System
base type: System.Object
is array: False
is class: True

Contoh: enumerasi jenis

Refleksi juga memungkinkan enumerasi jenis dalam rakitan dan anggota dalam kelas. Untuk menunjukkan fitur ini, tentukan kelas sederhana:

// vcpp_reflection_5.cpp
// compile with: /clr /LD
using namespace System;
public ref class TestClass {
   int m_i;
public:
   TestClass() {}
   void SimpleTestMember1() {}
   String ^ SimpleMember2(String ^ s) { return s; }
   int TestMember(int i) { return i; }
   property int Member {
      int get() { return m_i; }
      void set(int i) { m_i = i; }
   }
};

Contoh: inspeksi rakitan

Jika kode di atas dikompilasi ke dalam DLL yang disebut vcpp_reflection_6.dll, Anda kemudian dapat menggunakan pantulan untuk memeriksa konten rakitan ini. Ini melibatkan penggunaan fungsi API refleksi statis xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType untuk memuat rakitan. Fungsi ini mengembalikan alamat objek Assembly yang kemudian dapat dikueri tentang modul dan jenis di dalamnya.

Setelah sistem pantulan berhasil memuat rakitan, array objek Jenis diambil dengan Assembly.GetTypes fungsi . Setiap elemen array berisi informasi tentang jenis yang berbeda, meskipun dalam hal ini, hanya satu kelas yang ditentukan. Dengan menggunakan perulangan, setiap Jenis dalam array ini dikueri tentang anggota jenis menggunakan fungsi Type::GetMembers . Fungsi ini mengembalikan array objek MethodInfo , setiap objek yang berisi informasi tentang fungsi anggota, anggota data, atau properti dalam jenis .

Perhatikan bahwa daftar metode mencakup fungsi yang secara eksplisit didefinisikan dalam TestClass dan fungsi yang secara implisit diwarisi dari kelas System::Object . Sebagai bagian dari yang dijelaskan dalam .NET daripada dalam sintaks Visual C++, properti muncul sebagai anggota data yang mendasar yang diakses oleh fungsi get/set. Fungsi get/set muncul dalam daftar ini sebagai metode reguler. Refleksi didukung melalui runtime bahasa umum, bukan oleh pengkompilasi Microsoft C++.

Meskipun Anda menggunakan kode ini untuk memeriksa rakitan yang Anda tentukan, Anda juga dapat menggunakan kode ini untuk memeriksa rakitan .NET. Misalnya, jika Anda mengubah TestAssembly menjadi mscorlib, maka Anda akan melihat daftar setiap jenis dan metode yang ditentukan dalam mscorlib.dll.

// vcpp_reflection_6.cpp
// compile with: /clr
using namespace System;
using namespace System::IO;
using namespace System::Reflection;
int main() {
   Assembly ^ a = nullptr;
   try {
      // load assembly -- do not use file extension
      // will look for .dll extension first
      // then .exe with the filename
      a = Assembly::Load("vcpp_reflection_5");
   }
   catch (FileNotFoundException ^ e) {
      Console::WriteLine(e->Message);
      return -1;
   }

   Console::WriteLine("assembly info:");
   Console::WriteLine(a->FullName);
   array<Type^>^ typeArray = a->GetTypes();

   Console::WriteLine("type info ({0} types):", typeArray->Length);

   int totalTypes = 0;
   int totalMembers = 0;
   for (int i = 0 ; i < typeArray->Length ; i++) {
      // retrieve array of member descriptions
      array<MemberInfo^>^ member = typeArray[i]->GetMembers();

      Console::WriteLine("  members of {0} ({1} members):",
      typeArray[i]->FullName, member->Length);
      for (int j = 0 ; j < member->Length ; j++) {
         Console::Write("       ({0})",
         member[j]->MemberType.ToString() );
         Console::Write("{0}  ", member[j]);
         Console::WriteLine("");
         totalMembers++;
      }
      totalTypes++;
   }
   Console::WriteLine("{0} total types, {1} total members",
   totalTypes, totalMembers);
}

Cara: Menerapkan Arsitektur Komponen Plug-In menggunakan Pantulan

Contoh kode berikut menunjukkan penggunaan pantulan untuk mengimplementasikan arsitektur "plug-in" sederhana. Daftar pertama adalah aplikasi, dan yang kedua adalah plug-in. Aplikasi ini adalah beberapa formulir dokumen yang mengisi dirinya sendiri menggunakan kelas berbasis formulir apa pun yang ditemukan di DLL plug-in yang disediakan sebagai argumen baris perintah.

Aplikasi mencoba memuat rakitan yang disediakan menggunakan System.Reflection.Assembly.Load metode . Jika berhasil, jenis di dalam rakitan dijumlahkan menggunakan System.Reflection.Assembly.GetTypes metode . Setiap jenis kemudian diperiksa kompatibilitasnya menggunakan metode .System.Type.IsAssignableFrom Dalam contoh ini, kelas yang ditemukan dalam rakitan yang disediakan harus berasal dari Form kelas untuk memenuhi syarat sebagai plug-in.

Kelas yang kompatibel kemudian dibuat dengan System.Activator.CreateInstance metode , yang menerima Type sebagai argumen dan mengembalikan pointer ke instans baru. Setiap instans baru kemudian dilampirkan ke formulir dan ditampilkan.

Perhatikan bahwa Load metode tidak menerima nama rakitan yang menyertakan ekstensi file. Fungsi utama dalam aplikasi memangkas ekstensi yang disediakan, sehingga contoh kode berikut berfungsi dalam kedua kasus.

Contoh aplikasi

Kode berikut mendefinisikan aplikasi yang menerima plug-in. Nama rakitan harus disediakan sebagai argumen pertama. Rakitan ini harus berisi setidaknya satu jenis turunan publik Form .

// plugin_application.cpp
// compile with: /clr /c
#using <system.dll>
#using <system.drawing.dll>
#using <system.windows.forms.dll>

using namespace System;
using namespace System::Windows::Forms;
using namespace System::Reflection;

ref class PluggableForm : public Form  {
public:
   PluggableForm() {}
   PluggableForm(Assembly^ plugAssembly) {
      Text = "plug-in example";
      Size = Drawing::Size(400, 400);
      IsMdiContainer = true;

      array<Type^>^ types = plugAssembly->GetTypes( );
      Type^ formType = Form::typeid;

      for (int i = 0 ; i < types->Length ; i++) {
         if (formType->IsAssignableFrom(types[i])) {
            // Create an instance given the type description.
            Form^ f = dynamic_cast<Form^> (Activator::CreateInstance(types[i]));
            if (f) {
               f->Text = types[i]->ToString();
               f->MdiParent = this;
               f->Show();
            }
         }
      }
   }
};

int main() {
   Assembly^ a = Assembly::LoadFrom("plugin_application.exe");
   Application::Run(gcnew PluggableForm(a));
}

Contoh plug-in

Kode berikut mendefinisikan tiga kelas yang berasal dari Form. Ketika nama nama rakitan yang dihasilkan diteruskan ke yang dapat dieksekusi dalam daftar sebelumnya, masing-masing dari ketiga kelas ini akan ditemukan dan dibuat, terlepas dari kenyataan bahwa mereka semua tidak diketahui oleh aplikasi hosting pada waktu kompilasi.

// plugin_assembly.cpp
// compile with: /clr /LD
#using <system.dll>
#using <system.drawing.dll>
#using <system.windows.forms.dll>

using namespace System;
using namespace System::Windows::Forms;
using namespace System::Reflection;
using namespace System::Drawing;

public ref class BlueForm : public Form {
public:
   BlueForm() {
      BackColor = Color::Blue;
   }
};

public ref class CircleForm : public Form {
protected:
   virtual void OnPaint(PaintEventArgs^ args) override {
      args->Graphics->FillEllipse(Brushes::Green, ClientRectangle);
   }
};

public ref class StarburstForm : public Form {
public:
   StarburstForm(){
      BackColor = Color::Black;
   }
protected:
   virtual void OnPaint(PaintEventArgs^ args) override {
      Pen^ p = gcnew Pen(Color::Red, 2);
      Random^ r = gcnew Random( );
      Int32 w = ClientSize.Width;
      Int32 h = ClientSize.Height;
      for (int i=0; i<100; i++) {
         float x1 = w / 2;
         float y1 = h / 2;
         float x2 = r->Next(w);
         float y2 = r->Next(h);
         args->Graphics->DrawLine(p, x1, y1, x2, y2);
      }
   }
};

Cara: Menghitung Jenis Data di Rakitan menggunakan Pantulan

Kode berikut menunjukkan enumerasi jenis publik dan anggota menggunakan System.Reflection.

Mengingat nama rakitan, baik di direktori lokal atau di GAC, kode di bawah ini mencoba membuka rakitan dan mengambil deskripsi. Jika berhasil, setiap jenis ditampilkan dengan anggota publiknya.

Perhatikan bahwa System.Reflection.Assembly.Load tidak ada ekstensi file yang digunakan. Oleh karena itu, menggunakan "mscorlib.dll" sebagai argumen baris perintah akan gagal, sementara hanya menggunakan "mscorlib" akan menghasilkan tampilan jenis .NET Framework. Jika tidak ada nama rakitan yang disediakan, kode akan mendeteksi dan melaporkan jenis dalam rakitan saat ini (EXE yang dihasilkan dari kode ini).

Contoh

// self_reflection.cpp
// compile with: /clr
using namespace System;
using namespace System::Reflection;
using namespace System::Collections;

public ref class ExampleType {
public:
   ExampleType() {}
   void Func() {}
};

int main() {
   String^ delimStr = " ";
   array<Char>^ delimiter = delimStr->ToCharArray( );
   array<String^>^ args = Environment::CommandLine->Split( delimiter );

// replace "self_reflection.exe" with an assembly from either the local
// directory or the GAC
   Assembly^ a = Assembly::LoadFrom("self_reflection.exe");
   Console::WriteLine(a);

   int count = 0;
   array<Type^>^ types = a->GetTypes();
   IEnumerator^ typeIter = types->GetEnumerator();

   while ( typeIter->MoveNext() ) {
      Type^ t = dynamic_cast<Type^>(typeIter->Current);
      Console::WriteLine("   {0}", t->ToString());

      array<MemberInfo^>^ members = t->GetMembers();
      IEnumerator^ memberIter = members->GetEnumerator();
      while ( memberIter->MoveNext() ) {
         MemberInfo^ mi = dynamic_cast<MemberInfo^>(memberIter->Current);
         Console::Write("      {0}", mi->ToString( ) );
         if (mi->MemberType == MemberTypes::Constructor)
            Console::Write("   (constructor)");

         Console::WriteLine();
      }
      count++;
   }
   Console::WriteLine("{0} types found", count);
}

Baca juga