Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Dalam Managed Extensibility Framework (MEF), model pemrograman adalah metode tertentu untuk menentukan serangkaian objek konseptual tempat MEF beroperasi. Objek konseptual ini mencakup bagian, impor, dan ekspor. MEF menggunakan objek ini, tetapi tidak menentukan bagaimana objek tersebut harus diwakili. Oleh karena itu, berbagai model pemrograman dimungkinkan, termasuk model pemrograman yang disesuaikan.
Model pemrograman default yang digunakan dalam MEF adalah model pemrograman atribut. Dalam model pemrograman berbasis atribut, impor, ekspor, dan objek lainnya didefinisikan dengan atribut yang melengkapi kelas .NET Framework biasa. Topik ini menjelaskan cara menggunakan atribut yang disediakan oleh model pemrograman atribut untuk membuat aplikasi MEF.
Dasar-Dasar Impor dan Ekspor
Ekspor adalah nilai yang disediakan oleh suatu bagian kepada bagian lain dalam kontainer, dan impor adalah persyaratan yang diekspresikan suatu bagian kepada kontainer, yang diisi dari ekspor yang tersedia. Dalam model pemrograman yang diatribusikan, impor dan ekspor dideklarasikan dengan mendekorasi kelas atau anggota dengan atribut Import dan Export. Atribut Export dapat menghiasi kelas, bidang, properti, atau metode, sementara Import atribut dapat menghias parameter bidang, properti, atau konstruktor.
Agar impor dicocokkan dengan ekspor, impor dan ekspor harus memiliki kontrak yang sama. Kontrak terdiri dari string, yang disebut nama kontrak, dan jenis objek yang diekspor atau diimpor, yang disebut jenis kontrak. Hanya jika nama kontrak dan jenis kontrak cocok, baru kemudian ekspor dianggap memenuhi impor tertentu.
Salah satu atau kedua parameter kontrak dapat implisit atau eksplisit. Kode berikut menunjukkan kelas yang mendeklarasikan impor dasar.
Public Class MyClass1
<Import()>
Public Property MyAddin As IMyAddin
End Class
public class MyClass
{
[Import]
public IMyAddin MyAddin { get; set; }
}
Dalam impor ini, Import atribut tidak memiliki jenis kontrak atau parameter nama kontrak yang terlampir. Oleh karena itu, kedua-duanya akan disimpulkan dari properti yang telah didekorasi. Dalam hal ini, jenis kontrak akan menjadi IMyAddin, dan nama kontrak akan menjadi string unik yang dibuat dari jenis kontrak. (Dengan kata lain, nama kontrak hanya akan cocok dengan ekspor yang namanya juga disimpulkan dari jenis IMyAddin.)
Berikut ini menunjukkan ekspor yang cocok dengan impor sebelumnya.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
Dalam ekspor ini, jenis kontrak adalah IMyAddin karena ditentukan sebagai parameter dari atribut Export. Jenis yang diekspor harus sama dengan jenis kontrak, berasal dari jenis kontrak, atau menerapkan jenis kontrak jika merupakan antarmuka. Dalam ekspor ini, jenis MyLogger sebenarnya mengimplementasikan antarmuka IMyAddin. Nama kontrak disimpulkan dari jenis kontrak, yang berarti bahwa ekspor ini akan cocok dengan impor sebelumnya.
Nota
Ekspor dan impor biasanya harus dideklarasikan pada kelas atau anggota publik. Deklarasi lain didukung, tetapi mengekspor atau mengimpor anggota pribadi, terproteksi, atau internal merusak model isolasi untuk bagian tersebut sehingga tidak disarankan.
Jenis kontrak harus sama persis dengan ekspor dan impor agar dianggap cocok. Pertimbangkan ekspor berikut.
<Export()> 'WILL NOT match the previous import!
Public Class MyLogger
Implements IMyAddin
End Class
[Export] //WILL NOT match the previous import!
public class MyLogger : IMyAddin { }
Dalam ekspor ini, jenis kontrak adalah MyLogger bukan IMyAddin. Meskipun MyLogger mengimplementasikan IMyAddin, dan oleh karena itu dapat dilemparkan ke IMyAddin objek, ekspor ini tidak akan cocok dengan impor sebelumnya karena jenis kontrak tidak sama.
Secara umum, tidak perlu menentukan nama kontrak, dan sebagian besar kontrak harus didefinisikan dalam hal jenis kontrak dan metadata. Namun, dalam keadaan tertentu, penting untuk menentukan nama kontrak secara langsung. Kasus yang paling umum adalah ketika suatu kelas mengekspor beberapa nilai yang berbagi jenis umum, seperti tipe primitif. Nama kontrak dapat ditentukan sebagai parameter pertama dari Import atribut atau Export . Kode berikut menunjukkan impor dan ekspor dengan nama kontrak tertentu dari MajorRevision.
Public Class MyExportClass
'This one will match
<Export("MajorRevision")>
Public ReadOnly Property MajorRevision As Integer
Get
Return 4
End Get
End Property
<Export("MinorRevision")>
Public ReadOnly Property MinorRevision As Integer
Get
Return 16
End Get
End Property
End Class
public class MyClass
{
[Import("MajorRevision")]
public int MajorRevision { get; set; }
}
public class MyExportClass
{
[Export("MajorRevision")] //This one will match.
public int MajorRevision = 4;
[Export("MinorRevision")]
public int MinorRevision = 16;
}
Jika jenis kontrak tidak ditentukan, jenis kontrak masih akan disimpulkan dari jenis impor atau ekspor. Namun, bahkan jika nama kontrak ditentukan secara eksplisit, jenis kontrak juga harus cocok persis agar impor dan ekspor dianggap cocok. Misalnya, jika MajorRevision bidang adalah string, jenis kontrak yang disimpulkan tidak akan cocok dan ekspor tidak akan cocok dengan impor, meskipun memiliki nama kontrak yang sama.
Mengimpor dan Mengekspor Metode
Atribut Export ini juga dapat menghiasi metode, dengan cara yang sama seperti kelas, properti, atau fungsi. Ekspor metode harus menentukan jenis kontrak atau nama kontrak, karena jenis tidak dapat disimpulkan. Jenis yang ditentukan dapat berupa delegasi kustom atau jenis generik, seperti Func. Kelas berikut mengekspor metode bernama DoSomething.
Public Class MyAddin
'Explicitly specifying a generic type
<Export(GetType(Func(Of Integer, String)))>
Public Function DoSomething(ByVal TheParam As Integer) As String
Return Nothing 'Function body goes here
End Function
End Class
public class MyAddin
{
//Explicitly specifying a generic type.
[Export(typeof(Func<int, string>))]
public string DoSomething(int TheParam);
}
Di kelas ini, DoSomething metode mengambil satu int parameter dan mengembalikan string. Agar sesuai dengan ekspor ini, bagian impor harus mendeklarasikan anggota yang sesuai. Kelas berikut mengimpor metode DoSomething.
Public Class MyClass1
'Contract name must match!
<Import()>
Public Property MajorRevision As Func(Of Integer, String)
End Class
public class MyClass
{
[Import] //Contract name must match!
public Func<int, string> DoSomething { get; set; }
}
Untuk informasi selengkapnya tentang cara menggunakan Func<T, T> objek, lihat Func<T,TResult>.
Jenis Impor
MEF mendukung beberapa jenis impor, termasuk dinamis, malas, prasyarat, dan opsional.
Impor Dinamis
Dalam beberapa kasus, kelas impor mungkin ingin mencocokkan ekspor dari jenis apa pun yang memiliki nama kontrak tertentu. Dalam skenario ini, kelas dapat mendeklarasikan impor dinamis. Impor berikut cocok dengan ekspor apa pun dengan nama kontrak "TheString".
Public Class MyClass1
<Import("TheString")>
Public Property MyAddin
End Class
public class MyClass
{
[Import("TheString")]
public dynamic MyAddin { get; set; }
}
Ketika jenis kontrak disimpulkan dari dynamic kata kunci, itu akan cocok dengan jenis kontrak apa pun. Dalam hal ini, untuk mencocokkan, impor harus selalu menentukan nama kontrak. (Jika tidak ada nama kontrak yang ditentukan, impor akan dianggap tidak cocok dengan ekspor.) Kedua ekspor berikut akan cocok dengan impor sebelumnya.
<Export("TheString", GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
<Export("TheString")>
Public Class MyToolbar
End Class
[Export("TheString", typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
[Export("TheString")]
public class MyToolbar { }
Jelas, kelas impor harus siap untuk menangani objek jenis arbitrer.
Impor Malas
Dalam beberapa kasus, kelas yang mengimpor mungkin memerlukan referensi tidak langsung ke objek yang diimpor, agar objek tidak langsung diinisialisasi. Dalam skenario ini, kelas dapat menyatakan impor malas dengan menggunakan Lazy<T>jenis kontrak. Properti pengimpor berikut mendeklarasikan impor tertunda.
Public Class MyClass1
<Import()>
Public Property MyAddin As Lazy(Of IMyAddin)
End Class
public class MyClass
{
[Import]
public Lazy<IMyAddin> MyAddin { get; set; }
}
Dari sudut pandang mesin komposisi, jenis kontrak Lazy<T> dianggap identik dengan jenis kontrak T. Oleh karena itu, impor sebelumnya akan cocok dengan ekspor berikut.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
Nama kontrak dan jenis kontrak dapat ditentukan dalam atribut Import untuk impor tertunda, seperti yang dijelaskan sebelumnya di bagian "Impor dan Ekspor Dasar".
Impor Prasyarat
Bagian MEF yang biasanya diekspor dibuat oleh mesin komposisi, sebagai respons terhadap permintaan langsung atau kebutuhan untuk mengisi impor yang sesuai. Secara default, saat membuat bagian, mesin komposisi menggunakan konstruktor tanpa parameter. Untuk membuat mesin menggunakan konstruktor yang berbeda, Anda dapat menandainya dengan ImportingConstructor atribut .
Setiap bagian mungkin hanya memiliki satu konstruktor untuk digunakan oleh mesin komposisi. Tidak menyediakan konstruktor tanpa parameter dan tidak ada ImportingConstructor atribut, atau menyediakan lebih dari satu ImportingConstructor atribut, akan menghasilkan kesalahan.
Untuk mengisi parameter konstruktor yang ditandai dengan atribut ImportingConstructor, semua parameter tersebut secara otomatis diimpor. Ini adalah cara mudah untuk mendeklarasikan impor yang digunakan selama inisialisasi bagian. Kelas berikut menggunakan ImportingConstructor untuk mendeklarasikan impor.
Public Class MyClass1
Private _theAddin As IMyAddin
'Parameterless constructor will NOT be used
'because the ImportingConstructor
'attribute is present.
Public Sub New()
End Sub
'This constructor will be used.
'An import with contract type IMyAddin
'is declared automatically.
<ImportingConstructor()>
Public Sub New(ByVal MyAddin As IMyAddin)
_theAddin = MyAddin
End Sub
End Class
public class MyClass
{
private IMyAddin _theAddin;
//Parameterless constructor will NOT be
//used because the ImportingConstructor
//attribute is present.
public MyClass() { }
//This constructor will be used.
//An import with contract type IMyAddin is
//declared automatically.
[ImportingConstructor]
public MyClass(IMyAddin MyAddin)
{
_theAddin = MyAddin;
}
}
Secara default, ImportingConstructor atribut menggunakan jenis kontrak yang disimpulkan dan nama kontrak untuk semua impor parameter. Dimungkinkan untuk mengambil alih ini dengan mendekorasi parameter dengan Import atribut, yang kemudian dapat menentukan jenis kontrak dan nama kontrak secara eksplisit. Kode berikut menunjukkan konstruktor yang menggunakan sintaksis ini untuk mengimpor kelas turunan alih-alih kelas induk.
<ImportingConstructor()>
Public Sub New(<Import(GetType(IMySubAddin))> ByVal MyAddin As IMyAddin)
End Sub
[ImportingConstructor]
public MyClass([Import(typeof(IMySubAddin))]IMyAddin MyAddin)
{
_theAddin = MyAddin;
}
Secara khusus, Anda harus berhati-hati dengan parameter pengumpulan. Misalnya, jika Anda menentukan ImportingConstructor pada konstruktor dengan parameter jenis IEnumerable<int>, impor akan cocok dengan satu ekspor jenis IEnumerable<int>, bukan sekumpulan ekspor jenis int. Untuk mencocokkan sekumpulan ekspor jenis int, Anda harus menghias parameter dengan ImportMany atribut .
Parameter yang dinyatakan sebagai impor oleh ImportingConstructor atribut juga ditandai sebagai impor prasyarat. MEF biasanya memungkinkan ekspor dan impor untuk membentuk siklus. Misalnya, siklus adalah tempat objek A mengimpor objek B, yang pada gilirannya mengimpor objek A. Dalam keadaan biasa, siklus tidak menjadi masalah, dan kontainer komposisi membangun kedua objek secara normal.
Ketika nilai yang diimpor diperlukan oleh konstruktor bagian, objek tersebut tidak dapat berpartisipasi dalam siklus. Jika objek A mengharuskan objek B dibangun sebelum dapat dibangun sendiri, dan objek B mengimpor objek A, maka siklus tidak akan dapat diselesaikan dan kesalahan komposisi akan terjadi. Oleh karena itu, impor yang dideklarasikan pada parameter konstruktor adalah impor prasyarat, yang semuanya harus diisi sebelum ekspor-ekspor dari objek yang membutuhkannya dapat digunakan.
Impor Opsional
Atribut Import menentukan persyaratan agar bagian berfungsi. Jika impor tidak dapat dipenuhi, komposisi bagian tersebut akan gagal dan bagian tersebut tidak akan tersedia.
Anda dapat menentukan bahwa impor bersifat opsional dengan menggunakan AllowDefault properti . Dalam hal ini, komposisi akan berhasil bahkan jika impor tidak cocok dengan ekspor yang tersedia, dan properti impor akan diatur ke default untuk jenis propertinya (null untuk properti objek, false untuk Booleans, atau nol untuk properti numerik.) Kelas berikut menggunakan impor opsional.
Public Class MyClass1
<Import(AllowDefault:=True)>
Public Property thePlugin As Plugin
'If no matching export is available,
'thePlugin will be set to null.
End Class
public class MyClass
{
[Import(AllowDefault = true)]
public Plugin thePlugin { get; set; }
//If no matching export is available,
//thePlugin will be set to null.
}
Mengimpor Beberapa Objek
Atribut Import hanya akan berhasil disusun ketika cocok dengan satu dan hanya satu ekspor. Kasus lain akan menghasilkan kesalahan komposisi. Untuk mengimpor lebih dari satu ekspor yang cocok dengan kontrak yang sama, gunakan ImportMany atribut . Impor yang ditandai dengan atribut ini selalu opsional. Misalnya, komposisi tidak akan gagal jika tidak ada ekspor yang sesuai. Kelas berikut mengimpor sejumlah ekspor jenis IMyAddin.
Public Class MyClass1
<ImportMany()>
Public Property MyAddin As IEnumerable(Of IMyAddin)
End Class
public class MyClass
{
[ImportMany]
public IEnumerable<IMyAddin> MyAddin { get; set; }
}
Array yang diimpor dapat diakses dengan menggunakan sintaks dan metode biasa IEnumerable<T> . Dimungkinkan juga untuk menggunakan array biasa (IMyAddin[]) sebagai gantinya.
Pola ini bisa sangat penting ketika Anda menggunakannya dalam kombinasi dengan Lazy<T> sintaks. Misalnya, dengan menggunakan ImportMany, , IEnumerable<T>dan Lazy<T>, Anda dapat mengimpor referensi tidak langsung ke sejumlah objek dan hanya membuat instans objek yang menjadi diperlukan. Kelas berikut menunjukkan pola ini.
Public Class MyClass1
<ImportMany()>
Public Property MyAddin As IEnumerable(Of Lazy(Of IMyAddin))
End Class
public class MyClass
{
[ImportMany]
public IEnumerable<Lazy<IMyAddin>> MyAddin { get; set; }
}
Menghindari Penemuan
Dalam beberapa kasus, Anda mungkin ingin menghindari suatu bagian terdeteksi dalam katalog. Misalnya, bagian tersebut mungkin merupakan kelas dasar yang dimaksudkan untuk diwariskan, tetapi tidak digunakan. Ada dua cara untuk mencapai hal ini. Pertama, Anda dapat menggunakan abstract kata kunci pada kelas komponen. Kelas abstrak tidak pernah menyediakan ekspor, meskipun mereka dapat memberikan ekspor yang diwariskan ke kelas yang berasal dari mereka.
Jika kelas tidak dapat dibuat abstrak, Anda dapat menghiasnya dengan PartNotDiscoverable atribut . Bagian yang didekorasi dengan atribut ini tidak akan disertakan dalam katalog apa pun. Contoh berikut menunjukkan pola-pola ini.
DataOne akan diketahui oleh katalog. Karena DataTwo abstrak, itu tidak akan ditemukan. Karena DataThree menggunakan PartNotDiscoverable atribut , atribut tidak akan ditemukan.
<Export()>
Public Class DataOne
'This part will be discovered
'as normal by the catalog.
End Class
<Export()>
Public MustInherit Class DataTwo
'This part will not be discovered
'by the catalog.
End Class
<PartNotDiscoverable()>
<Export()>
Public Class DataThree
'This part will also not be discovered
'by the catalog.
End Class
[Export]
public class DataOne
{
//This part will be discovered
//as normal by the catalog.
}
[Export]
public abstract class DataTwo
{
//This part will not be discovered
//by the catalog.
}
[PartNotDiscoverable]
[Export]
public class DataThree
{
//This part will also not be discovered
//by the catalog.
}
Metadata dan Tampilan Metadata
Ekspor dapat memberikan informasi tambahan tentang diri mereka sendiri yang dikenal sebagai metadata. Metadata dapat digunakan untuk menyampaikan properti objek yang diekspor ke bagian impor. Bagian impor dapat menggunakan data ini untuk memutuskan ekspor mana yang akan digunakan, atau untuk mengumpulkan informasi tentang ekspor tanpa harus membangunnya. Untuk alasan ini, impor harus malas untuk menggunakan metadata.
Untuk menggunakan metadata, Anda biasanya mendeklarasikan antarmuka yang dikenal sebagai tampilan metadata, yang mendeklarasikan metadata apa yang akan tersedia. Antarmuka tampilan metadata hanya boleh memiliki properti, dan properti tersebut harus memiliki get aksesor. Antarmuka berikut adalah contoh tampilan metadata.
Public Interface IPluginMetadata
ReadOnly Property Name As String
<DefaultValue(1)>
ReadOnly Property Version As Integer
End Interface
public interface IPluginMetadata
{
string Name { get; }
[DefaultValue(1)]
int Version { get; }
}
Dimungkinkan juga untuk menggunakan koleksi generik, IDictionary<string, object>, sebagai tampilan metadata, tetapi ini kehilangan manfaat pemeriksaan jenis dan harus dihindari.
Biasanya, semua properti bernama dalam tampilan metadata diperlukan, dan ekspor apa pun yang tidak menyediakannya tidak akan dianggap cocok. Atribut DefaultValue menentukan bahwa properti bersifat opsional. Jika properti tidak disertakan, properti akan diberi nilai default yang ditentukan sebagai parameter .DefaultValue Berikut ini adalah dua kelas berbeda yang dihiasi dengan metadata. Kedua kelas ini akan cocok dengan tampilan metadata sebelumnya.
<Export(GetType(IPlugin))>
<ExportMetadata("Name", "Logger")>
<ExportMetadata("Version", 4)>
Public Class MyLogger
Implements IPlugin
End Class
'Version is not required because of the DefaultValue
<Export(GetType(IPlugin))>
<ExportMetadata("Name", "Disk Writer")>
Public Class DWriter
Implements IPlugin
End Class
[Export(typeof(IPlugin)),
ExportMetadata("Name", "Logger"),
ExportMetadata("Version", 4)]
public class Logger : IPlugin
{
}
[Export(typeof(IPlugin)),
ExportMetadata("Name", "Disk Writer")]
//Version is not required because of the DefaultValue
public class DWriter : IPlugin
{
}
Metadata dinyatakan setelah Export atribut dengan menggunakan ExportMetadata atribut . Setiap bagian metadata terdiri dari pasangan nama/nilai. Bagian nama metadata harus cocok dengan nama properti yang sesuai dalam tampilan metadata, dan nilai akan ditetapkan ke properti tersebut.
Ini adalah pengimpor yang menentukan tampilan metadata apa, jika ada, yang akan digunakan. Impor dengan metadata dinyatakan sebagai impor lambat, dengan antarmuka metadata sebagai parameter jenis kedua ke Lazy<T,T>. Kelas berikut mengimpor bagian sebelumnya dengan metadata.
Public Class Addin
<Import()>
Public Property plugin As Lazy(Of IPlugin, IPluginMetadata)
End Class
public class Addin
{
[Import]
public Lazy<IPlugin, IPluginMetadata> plugin;
}
Dalam banyak kasus, Anda ingin menggabungkan metadata dengan atribut ImportMany, untuk mengurai impor yang tersedia dan memilih serta menginstansiasi hanya satu, atau memfilter koleksi agar sesuai dengan kondisi tertentu. Kelas berikut hanya menginstansiasi objek IPlugin yang memiliki nilai Name "Logger".
Public Class User
<ImportMany()>
Public Property plugins As IEnumerable(Of Lazy(Of IPlugin, IPluginMetadata))
Public Function InstantiateLogger() As IPlugin
Dim logger As IPlugin
logger = Nothing
For Each Plugin As Lazy(Of IPlugin, IPluginMetadata) In plugins
If Plugin.Metadata.Name = "Logger" Then
logger = Plugin.Value
End If
Next
Return logger
End Function
End Class
public class User
{
[ImportMany]
public IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins;
public IPlugin InstantiateLogger()
{
IPlugin logger = null;
foreach (Lazy<IPlugin, IPluginMetadata> plugin in plugins)
{
if (plugin.Metadata.Name == "Logger")
logger = plugin.Value;
}
return logger;
}
}
Pewarisan Impor dan Ekspor
Jika kelas mewarisi dari suatu bagian, kelas tersebut juga dapat menjadi bagian. Impor selalu diwariskan oleh kelas turunan. Oleh karena itu, subkelas dari sebuah bagian akan selalu menjadi bagian, memiliki impor yang sama dengan kelas induknya.
Ekspor yang dideklarasikan dengan menggunakan Export atribut tidak diwariskan oleh subkelas. Namun, bagian dapat mengekspor dirinya sendiri dengan menggunakan atribut InheritedExport. Subkelas bagian akan mewarisi dan memberikan ekspor yang sama, termasuk nama kontrak dan jenis kontrak.
Export Tidak seperti atribut, InheritedExport hanya dapat diterapkan di tingkat kelas, dan bukan di tingkat anggota. Oleh karena itu, ekspor tingkat anggota tidak pernah dapat diwariskan.
Empat kelas berikut menunjukkan prinsip-prinsip pewarisan impor dan ekspor.
NumTwo mewarisi dari NumOne, sehingga NumTwo akan mengimpor IMyData. Ekspor biasa tidak diwariskan, jadi NumTwo tidak akan mengekspor apa pun.
NumFour mewarisi dari NumThree. Karena NumThree menggunakan InheritedExport, NumFour memiliki satu ekspor dengan jenis kontrak NumThree. Ekspor tingkat anggota tidak pernah diwariskan, jadi IMyData tidak diekspor.
<Export()>
Public Class NumOne
<Import()>
Public Property MyData As IMyData
End Class
Public Class NumTwo
Inherits NumOne
'Imports are always inherited, so NumTwo will
'Import IMyData
'Ordinary exports are not inherited, so
'NumTwo will NOT export anything. As a result it
'will not be discovered by the catalog!
End Class
<InheritedExport()>
Public Class NumThree
<Export()>
Public Property MyData As IMyData
'This part provides two exports, one of
'contract type NumThree, and one of
'contract type IMyData.
End Class
Public Class NumFour
Inherits NumThree
'Because NumThree used InheritedExport,
'this part has one export with contract
'type NumThree.
'Member-level exports are never inherited,
'so IMyData is not exported.
End Class
[Export]
public class NumOne
{
[Import]
public IMyData MyData { get; set; }
}
public class NumTwo : NumOne
{
//Imports are always inherited, so NumTwo will
//import IMyData.
//Ordinary exports are not inherited, so
//NumTwo will NOT export anything. As a result it
//will not be discovered by the catalog!
}
[InheritedExport]
public class NumThree
{
[Export]
Public IMyData MyData { get; set; }
//This part provides two exports, one of
//contract type NumThree, and one of
//contract type IMyData.
}
public class NumFour : NumThree
{
//Because NumThree used InheritedExport,
//this part has one export with contract
//type NumThree.
//Member-level exports are never inherited,
//so IMyData is not exported.
}
Jika ada metadata terkait dengan InheritedExport atribut, metadata itu juga akan diwariskan. (Untuk informasi selengkapnya, lihat bagian sebelumnya "Metadata dan Tampilan Metadata".) Metadata yang diwariskan tidak dapat dimodifikasi oleh subkelas. Namun, dengan mendeklarasikan InheritedExport ulang atribut dengan nama kontrak dan jenis kontrak yang sama, tetapi dengan metadata baru, subkelas dapat mengganti metadata yang diwariskan dengan metadata baru. Kelas berikut menunjukkan prinsip ini. Bagian mewarisi MegaLogger dari Logger dan menyertakan InheritedExport atribut . Karena MegaLogger mendeklarasikan ulang metadata baru bernama Status, metadata Nama dan Versi tidak diwarisi dari Logger.
<InheritedExport(GetType(IPlugin))>
<ExportMetadata("Name", "Logger")>
<ExportMetadata("Version", 4)>
Public Class Logger
Implements IPlugin
'Exports with contract type IPlugin
'and metadata "Name" and "Version".
End Class
Public Class SuperLogger
Inherits Logger
'Exports with contract type IPlugin and
'metadata "Name" and "Version", exactly the same
'as the Logger class.
End Class
<InheritedExport(GetType(IPlugin))>
<ExportMetadata("Status", "Green")>
Public Class MegaLogger
Inherits Logger
'Exports with contract type IPlugin and
'metadata "Status" only. Re-declaring
'the attribute replaces all metadata.
End Class
[InheritedExport(typeof(IPlugin)),
ExportMetadata("Name", "Logger"),
ExportMetadata("Version", 4)]
public class Logger : IPlugin
{
//Exports with contract type IPlugin and
//metadata "Name" and "Version".
}
public class SuperLogger : Logger
{
//Exports with contract type IPlugin and
//metadata "Name" and "Version", exactly the same
//as the Logger class.
}
[InheritedExport(typeof(IPlugin)),
ExportMetadata("Status", "Green")]
public class MegaLogger : Logger {
//Exports with contract type IPlugin and
//metadata "Status" only. Re-declaring
//the attribute replaces all metadata.
}
Saat mendeklarasikan atribut InheritedExport ulang untuk mengganti metadata, pastikan bahwa tipe kontrak sama. (Dalam contoh sebelumnya, IPlugin adalah jenis kontrak.) Jika berbeda, alih-alih mengesampingkan, atribut kedua akan membuat ekspor independen kedua dari bagian tersebut. Umumnya, ini berarti Bahwa Anda harus secara eksplisit menentukan jenis kontrak saat Anda mengambil alih atribut, seperti yang InheritedExport ditunjukkan pada contoh sebelumnya.
Karena antarmuka tidak dapat diinstansiasi secara langsung, antarmuka umumnya tidak dapat didekorasi dengan Export atribut atau Import . Namun, sebuah antarmuka di tingkat antarmuka dapat dihiasi dengan atribut InheritedExport, dan ekspor tersebut beserta metadata terkaitnya akan diwariskan oleh kelas-kelas yang menerapkannya. Namun, antarmuka itu sendiri tidak akan tersedia sebagai bagian.
Atribut Ekspor Khusus
Atribut ekspor dasar, Export dan InheritedExport, dapat diperluas untuk menyertakan metadata sebagai properti atribut. Teknik ini berguna untuk menerapkan metadata serupa ke banyak bagian, atau membuat pohon warisan atribut metadata.
Atribut kustom dapat menentukan jenis kontrak, nama kontrak, atau metadata lainnya. Untuk menentukan atribut kustom, kelas yang mewarisi dari ExportAttribute (atau InheritedExportAttribute) harus dihiasi dengan MetadataAttribute atribut . Kelas berikut mendefinisikan atribut kustom.
<MetadataAttribute()>
<AttributeUsage(AttributeTargets.Class, AllowMultiple:=false)>
Public Class MyAttribute
Inherits ExportAttribute
Public Property MyMetadata As String
Public Sub New(ByVal myMetadata As String)
MyBase.New(GetType(IMyAddin))
myMetadata = myMetadata
End Sub
End Class
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MyAttribute : ExportAttribute
{
public MyAttribute(string myMetadata)
: base(typeof(IMyAddin))
{
MyMetadata = myMetadata;
}
public string MyMetadata { get; private set; }
}
Kelas ini mendefinisikan atribut kustom bernama MyAttribute dengan jenis IMyAddin kontrak dan beberapa metadata bernama MyMetadata. Semua properti di kelas yang ditandai dengan MetadataAttribute atribut dianggap sebagai metadata yang ditentukan dalam atribut kustom. Dua deklarasi berikut setara.
<Export(GetType(IMyAddin))>
<ExportMetadata("MyMetadata", "theData")>
Public Property myAddin As MyAddin
<MyAttribute("theData")>
Public Property myAddin As MyAddin
[Export(typeof(IMyAddin)),
ExportMetadata("MyMetadata", "theData")]
public MyAddin myAddin { get; set; }
[MyAttribute("theData")]
public MyAddin myAddin { get; set; }
Dalam deklarasi pertama, jenis kontrak dan metadata ditentukan secara eksplisit. Dalam deklarasi kedua, jenis kontrak dan metadata implisit dalam atribut yang disesuaikan. Terutama dalam kasus di mana sejumlah besar metadata identik harus diterapkan ke banyak bagian (misalnya, penulis atau informasi hak cipta), menggunakan atribut kustom dapat menghemat banyak waktu dan duplikasi. Selanjutnya, pohon warisan atribut kustom dapat dibuat untuk memungkinkan variasi.
Untuk membuat metadata opsional dalam atribut kustom, Anda dapat menggunakan DefaultValue atribut . Ketika atribut ini diterapkan ke properti di kelas atribut kustom, atribut ini menentukan bahwa properti yang didekorasi bersifat opsional dan tidak harus disediakan oleh pengekspor. Jika nilai untuk properti tidak disediakan, nilai default akan ditetapkan untuk jenis propertinya (biasanya null, , falseatau 0.)
Kebijakan Pembuatan
Ketika bagian menentukan impor dan komposisi dilakukan, kontainer komposisi mencoba menemukan ekspor yang cocok. Jika berhasil mencocokkan impor dengan ekspor, anggota yang mengimpor diatur ke instans objek yang diekspor. Dari mana instance ini berasal dikendalikan oleh kebijakan pembuatan dari bagian pengekspor.
Dua kemungkinan kebijakan pembuatan dibagikan dan tidak dibagikan. Bagian yang memiliki kebijakan pembuatan bersama akan dibagikan di antara setiap impor dalam kontainer untuk bagian dengan kontrak tersebut. Ketika mesin penyusun menemukan kecocokan dan harus mengatur properti impor, sistem akan membuat salinan baru dari bagian tersebut hanya jika belum ada; jika sudah ada, sistem akan menyediakan salinan yang sudah ada. Ini berarti bahwa banyak objek mungkin memiliki referensi ke bagian yang sama. Bagian-bagian seperti itu tidak boleh mengandalkan keadaan internal yang dapat diubah dari banyak tempat. Kebijakan ini sesuai untuk bagian statis, bagian yang menyediakan layanan, dan bagian yang mengonsumsi banyak memori atau sumber daya lainnya.
Bagian dengan kebijakan pembuatan yang tidak dibagi bersama akan dibuat setiap kali ditemukan impor yang sesuai untuk ekspor tertentu. Oleh karena itu, salinan baru akan diinstansiasi untuk setiap impor dalam kontainer yang sesuai dengan salah satu kontrak yang diekspor oleh bagian tersebut. Status internal salinan ini tidak akan dibagikan. Kebijakan ini sesuai untuk bagian-bagian di mana setiap impor memerlukan status internalnya sendiri.
Impor dan ekspor dapat menentukan kebijakan pembuatan bagian, dari antara nilai Shared, , NonSharedatau Any. Defaultnya adalah Any untuk impor dan ekspor. Ekspor yang menentukan Shared atau NonShared hanya akan cocok dengan impor yang menentukan hal yang sama, atau yang menentukan Any. Demikian pula, impor yang menentukan Shared atau NonShared hanya akan cocok dengan ekspor yang menentukan hal yang sama, atau yang menentukan Any. Impor dan ekspor dengan kebijakan pembuatan yang tidak kompatibel tidak dianggap cocok, dengan cara yang sama seperti impor dan ekspor yang nama kontrak atau jenis kontraknya tidak cocok. Jika impor dan ekspor menentukan Any, atau tidak menentukan kebijakan pembuatan dan secara default menjadi Any, maka kebijakan pembuatan akan secara default menjadi shared.
Contoh berikut menunjukkan impor dan ekspor yang menentukan kebijakan pembuatan.
PartOne tidak menentukan kebijakan pembuatan, sehingga defaultnya adalah Any.
PartTwo tidak menentukan kebijakan pembuatan, sehingga defaultnya adalah Any. Karena baik impor maupun ekspor secara default ke Any, PartOne akan dibagikan.
PartThree
Shared menentukan kebijakan pembuatan, jadi PartTwo dan PartThree akan berbagi salinan yang sama dari PartOne.
PartFour
NonShared menentukan kebijakan pembuatan, jadi PartFour akan tidak dibagikan dalam PartFive.
PartSix menentukan kebijakan pembuatan NonShared.
PartFive dan PartSix masing-masing akan menerima salinan terpisah dari PartFour.
PartSeven menentukan kebijakan pembuatan Shared. Karena tidak ada PartFour yang diekspor dengan kebijakan pembuatan Shared, impor PartSeven tidak sesuai dengan apa pun dan tidak akan diisi.
<Export()>
Public Class PartOne
'The default creation policy for an export is Any.
End Class
Public Class PartTwo
<Import()>
Public Property partOne As PartOne
'The default creation policy for an import is Any.
'If both policies are Any, the part will be shared.
End Class
Public Class PartThree
<Import(RequiredCreationPolicy:=CreationPolicy.Shared)>
Public Property partOne As PartOne
'The Shared creation policy is explicitly specified.
'PartTwo and PartThree will receive references to the
'SAME copy of PartOne.
End Class
<Export()>
<PartCreationPolicy(CreationPolicy.NonShared)>
Public Class PartFour
'The NonShared creation policy is explicitly specified.
End Class
Public Class PartFive
<Import()>
Public Property partFour As PartFour
'The default creation policy for an import is Any.
'Since the export's creation policy was explicitly
'defined, the creation policy for this property will
'be non-shared.
End Class
Public Class PartSix
<Import(RequiredCreationPolicy:=CreationPolicy.NonShared)>
Public Property partFour As PartFour
'Both import and export specify matching creation
'policies. PartFive and PartSix will each receive
'SEPARATE copies of PartFour, each with its own
'internal state.
End Class
Public Class PartSeven
<Import(RequiredCreationPolicy:=CreationPolicy.Shared)>
Public Property partFour As PartFour
'A creation policy mismatch. Because there is no
'exported PartFour with a creation policy of Shared,
'this import does not match anything and will not be
'filled.
End Class
[Export]
public class PartOne
{
//The default creation policy for an export is Any.
}
public class PartTwo
{
[Import]
public PartOne partOne { get; set; }
//The default creation policy for an import is Any.
//If both policies are Any, the part will be shared.
}
public class PartThree
{
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public PartOne partOne { get; set; }
//The Shared creation policy is explicitly specified.
//PartTwo and PartThree will receive references to the
//SAME copy of PartOne.
}
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PartFour
{
//The NonShared creation policy is explicitly specified.
}
public class PartFive
{
[Import]
public PartFour partFour { get; set; }
//The default creation policy for an import is Any.
//Since the export's creation policy was explicitly
//defined, the creation policy for this property will
//be non-shared.
}
public class PartSix
{
[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
public PartFour partFour { get; set; }
//Both import and export specify matching creation
//policies. PartFive and PartSix will each receive
//SEPARATE copies of PartFour, each with its own
//internal state.
}
public class PartSeven
{
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public PartFour partFour { get; set; }
//A creation policy mismatch. Because there is no
//exported PartFour with a creation policy of Shared,
//this import does not match anything and will not be
//filled.
}
Siklus Hidup dan Pembuangan
Karena bagian dihosting dalam kontainer komposisi, siklus hidupnya bisa lebih kompleks daripada objek biasa. Bagian dapat mengimplementasikan dua antarmuka terkait siklus hidup penting: IDisposable dan IPartImportsSatisfiedNotification.
Bagian yang mengharuskan pekerjaan dilakukan saat dimatikan atau yang perlu merilis sumber daya harus mengimplementasikan IDisposable, seperti biasa untuk objek .NET Framework. Namun, karena kontainer membuat dan mempertahankan referensi ke bagian, hanya kontainer yang memiliki bagian yang harus memanggil Dispose metode di atasnya. Kontainer itu sendiri mengimplementasikan IDisposable, dan sebagai bagian dari pembersihannya dalam Dispose, ia akan memanggil Dispose untuk semua bagian yang dimilikinya. Untuk alasan ini, Anda harus selalu membuang kontainer komposisi ketika kontainer dan semua bagian yang dimilikinya tidak lagi diperlukan.
Untuk kontainer komposisi berumur panjang, konsumsi memori oleh bagian yang memiliki kebijakan pembuatan tidak berbagi dapat menjadi masalah. Bagian yang tidak dibagikan dengan pihak lain ini dapat dibuat berulang kali dan tidak akan dihancurkan sampai kontainer itu sendiri dihancurkan. Untuk menangani hal ini, kontainer menyediakan ReleaseExport metode . Memanggil metode ini pada ekspor yang tidak dibagikan akan menghapus ekspor tersebut dari wadah komposisi dan memproses pembuangannya. Bagian yang hanya digunakan oleh ekspor yang dihapus, serta bagian-bagian lain di sepanjang hierarki, juga dihapus dan dibuang. Dengan cara ini, sumber daya dapat direklamasi kembali tanpa membuang kontainer komposisi itu sendiri.
IPartImportsSatisfiedNotification berisi satu metode bernama OnImportsSatisfied. Metode ini dipanggil oleh kontainer komposisi pada bagian mana pun yang mengimplementasikan antarmuka ketika komposisi telah selesai dan impor bagian siap digunakan. Bagian-bagian dibuat oleh mesin komposisi untuk memenuhi kebutuhan impor bagian lain. Sebelum impor bagian ditetapkan, Anda tidak dapat melakukan inisialisasi apa pun yang bergantung pada atau memanipulasi nilai yang diimpor di konstruktor bagian kecuali nilai tersebut telah ditentukan sebagai prasyarat dengan menggunakan ImportingConstructor atribut . Ini biasanya metode yang disukai, tetapi dalam beberapa kasus, injeksi konstruktor mungkin tidak tersedia. Dalam kasus tersebut, inisialisasi dapat dilakukan di OnImportsSatisfied, dan bagian harus menerapkan IPartImportsSatisfiedNotification.