Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Yönetilen Genişletilebilirlik Çerçevesi'nde (MEF), programlama modeli , MEF'nin üzerinde çalıştığı kavramsal nesne kümesini tanımlamaya yönelik belirli bir yöntemdir. Bu kavramsal nesneler parçalar, ithalat ve ihracat içerir. MEF bu nesneleri kullanır, ancak bunların nasıl temsil edilmesi gerektiğini belirtmez. Bu nedenle, özelleştirilmiş programlama modelleri de dahil olmak üzere çok çeşitli programlama modelleri mümkündür.
MEF'te kullanılan varsayılan programlama modeli , öznitelikli programlama modelidir. Öznitelikli programlama modeli bölümlerinde içeri aktarmalar, dışarı aktarmalar ve diğer nesneler sıradan .NET Framework sınıflarını süsleyen özniteliklerle tanımlanır. Bu konuda, bir MEF uygulaması oluşturmak için öznitelikli programlama modeli tarafından sağlanan özniteliklerin nasıl kullanılacağı açıklanmaktadır.
temel bilgileri içeri ve dışarı aktarma
Dışarı aktarma, bir parçanın kapsayıcıdaki diğer bölümlere sağladığı bir değerdir ve içeri aktarma, bir bölümün kullanılabilir dışarı aktarmalardan doldurulması için kapsayıcıya ifade eden bir gereksinimdir. Öznitelikli programlama modelinde, içeri ve dışarı aktarma işlemleri, sınıflar veya üyeler Import ve Export öznitelik olarak işaretlenerek bildirilir.
Export özniteliği bir sınıfı, alanı, özelliği veya yöntemi süsleyebilirkenImport, öznitelik bir alanı, özelliği veya oluşturucu parametresini süsleyebilir.
İçeri aktarma işleminin dışarı aktarmayla eşleşmesi için içeri ve dışarı aktarma işleminin aynı sözleşmeye sahip olması gerekir. Sözleşme, sözleşme adı olarak adlandırılan bir dizeden ve dışarı veya içeri aktarılan nesnenin türünden ( sözleşme türü olarak adlandırılır) oluşur. Yalnızca hem sözleşme adı hem de sözleşme türü eşleştiğinde bir dışa aktarma, belirli bir içe aktarmayı gerçekleştirmiş kabul edilir.
Sözleşme parametrelerinden biri veya her ikisi örtük veya açık olabilir. Aşağıdaki kod, temel bir import işlemini deklare eden bir sınıfı gösterir.
Public Class MyClass1
<Import()>
Public Property MyAddin As IMyAddin
End Class
public class MyClass
{
[Import]
public IMyAddin MyAddin { get; set; }
}
Bu içe aktarmada, Import özniteliğine ne bir sözleşme türü ne de bir sözleşme adı parametresi eklenmiştir. Bu nedenle, her ikisi de dekore edilmiş özellikten çıkarılacaktır. Bu durumda, sözleşme türü olur IMyAddinve sözleşme adı, sözleşme türünden oluşturulan benzersiz bir dize olur. Başka bir deyişle, sözleşme adı, yalnızca adları da türünden IMyAddin çıkarılan ihracatlar ile eşleşecektir.
Aşağıda, önceki ithalatla eşleşen bir dışarı aktarma gösterilmektedir.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
Bu dışarı aktarmada, sözleşme türü IMyAddin özniteliğinin Export parametresi olarak belirtildiği için. Dışarı aktarılan tür, sözleşme türüyle aynı olmalı, sözleşme türünden türetilmelidir veya bir arabirimse sözleşme türünü uygulamalıdır. Bu dışarı aktarmada gerçek tür MyLogger arabirimini IMyAddinuygular. Sözleşme adı sözleşme türünden çıkarılır ve bu da bu dışarı aktarma işleminin önceki içeri aktarmayla eşleşeceği anlamına gelir.
Uyarı
İhracatlar ve ithalatlar genellikle genel sınıflarda veya üyelerde bildirilmelidir. Diğer bildirimler desteklenir, ancak özel, korumalı veya iç üyeyi dışarı veya içeri aktarmak parça için yalıtım modelini bozar ve bu nedenle önerilmez.
Dışarı aktarma ve içeri aktarmanın eşleşme olarak kabul edilmesi için sözleşme türü tam olarak eşleşmelidir. Aşağıdaki ihracatı göz önünde bulundurun.
<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 { }
Bu dışa aktarmada, sözleşme türü MyLogger yerine IMyAddin.
MyLogger
IMyAddin uygulamasını gerçekleştirdiği için bir IMyAddin nesneye dönüştürülebilir, ancak bu ihraç, sözleşme türleri aynı olmadığı için önceki ithalatla eşleşmeyecektir.
Genel olarak, sözleşme adını belirtmek gerekli değildir ve çoğu sözleşme, sözleşme türü ve meta veriler açısından tanımlanmalıdır. Ancak, belirli koşullar altında, sözleşme adını doğrudan belirtmek önemlidir. En yaygın durum, bir sınıfın temel öğeler gibi ortak bir türü paylaşan çeşitli değerleri dışarı aktarmasıdır. Sözleşme adı, veya Import özniteliğinin ilk parametresi Export olarak belirtilebilir. Aşağıdaki kod, belirtilen sözleşme adına sahip bir içeri aktarma ve dışarı aktarma işlemini MajorRevisiongösterir.
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;
}
Sözleşme türü belirtilmezse, ithalat veya ihracat türüne göre yine de çıkarım yapılır. Ancak, sözleşme adı açıkça belirtilse bile, içeri ve dışarı aktarmanın eşleşme olarak kabul edilmesi için sözleşme türünün de tam olarak eşleşmesi gerekir. Örneğin, MajorRevision alan bir dizeyse, tahmin edilen sözleşme türleri eşleşmez ve aynı sözleşme adı olmasına rağmen dışa aktarma içe aktarmayla eşleşmez.
Metodu İthal Etme ve İhraç Etme
Export özniteliği bir yöntemi de sınıf, özellik veya işlevle aynı şekilde süsleyebilir. Yöntem dışa aktarımları, türü çıkarılamadığından bir sözleşme türü veya sözleşme adı belirtmelidir. Belirtilen tür özel bir temsilci veya Func gibi genel bir tür olabilir. Aşağıdaki sınıf adlı DoSomethingbir yöntemi dışarı aktarır.
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);
}
Bu sınıfta, DoSomething yöntemi tek bir int parametre alır ve bir string döndürür. Bu dışarı aktarmayı eşleştirmek için ithal eden tarafın uygun bir üye bildirmesi gerekir. Aşağıdaki sınıf DoSomething yöntemini içeri aktarır.
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; }
}
Nesnesinin Func<T, T> kullanımı hakkında daha fazla bilgi için bkz Func<T,TResult>. .
İthalat Türleri
MEF dinamik, gecikmeli, önkoşul ve isteğe bağlı dahil olmak üzere çeşitli içeri aktarma türlerini destekler.
Dinamik İçeri Aktarmalar
İthal eden sınıf, bazı durumlarda belirli bir sözleşme adına sahip herhangi bir türdeki dışarı aktarmaları karşılamak isteyebilir. Bu senaryoda, sınıf dinamik içeri aktarımı ilan edebilir. Aşağıdaki içeri aktarma işlemi, "TheString" sözleşme adını taşıyan herhangi bir dışarı aktarma ile eşleşir.
Public Class MyClass1
<Import("TheString")>
Public Property MyAddin
End Class
public class MyClass
{
[Import("TheString")]
public dynamic MyAddin { get; set; }
}
Sözleşme türü anahtar sözcüğünden dynamic çıkarıldığında, herhangi bir sözleşme türüyle eşleşecektir. Bu durumda, içeri aktarma işlemi her zaman eşleşecek bir sözleşme adı belirtmelidir. (Herhangi bir sözleşme adı belirtilmezse, içeri aktarma işlemi dışarı aktarmayla eşleşmeyecek şekilde değerlendirilir.) Aşağıdaki dışarı aktarmaların her ikisi de önceki içeri aktarmayla eşleşer.
<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 { }
Açıkçası, içeri aktaran sınıfın rastgele türdeki bir nesneyle başa çıkmaya hazır olması gerekir.
Gecikmeli İçeri Aktarmalar
Bazı durumlarda, ithalat sınıfı içeri aktarılan nesneye dolaylı bir başvuru gerektirebilir, böylece nesne hemen oluşturulmaz. Bu senaryoda, sınıf türünde bir sözleşme kullanarak Lazy<T> bildirebilir. Aşağıdaki içeri aktarma özelliği gecikmeli bir içeri aktarma bildirir.
Public Class MyClass1
<Import()>
Public Property MyAddin As Lazy(Of IMyAddin)
End Class
public class MyClass
{
[Import]
public Lazy<IMyAddin> MyAddin { get; set; }
}
Bileşim motoru açısından bakıldığında, Lazy<T> türü bir sözleşme, T türü sözleşmeyle aynı kabul edilir. Bu nedenle, önceki ithalat aşağıdaki ihracatla eşleşir.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
Sözleşme adı ve sözleşme türü, daha önce "Temel İçeri ve Dışarı Aktarmalar" bölümünde açıklandığı gibi Import özniteliğinde tembel yükleme için belirtilebilir.
Önkoşul İçeri Aktarmaları
Dışarı aktarılan MEF parçaları, genellikle kompozisyon motoru tarafından, bir doğrudan isteğe veya eşleşen bir içeri aktarmayı tamamlama ihtiyacına yanıt olarak oluşturulur. Varsayılan olarak, bir parça oluştururken, oluşturma altyapısı parametresiz oluşturucuyu kullanır. Motorun farklı bir kurucu kullanmasını sağlamak için onu ImportingConstructor özniteliğiyle işaretleyebilirsiniz.
Her parça, bileşim altyapısı tarafından kullanılmak üzere yalnızca bir oluşturucuya sahip olabilir. Parametresiz bir oluşturucu ve öznitelik sağlamamak ya da birden fazla ImportingConstructor özniteliği sağlamak bir hata oluşturur.
özniteliğiyle ImportingConstructor işaretlenmiş bir oluşturucunun parametrelerini doldurmak için, bu parametrelerin tümü otomatik olarak içeri aktarma olarak bildirilir. Bu, parça başlatma işlemi sırasında kullanılan içeri aktarım bildirimlerini yapmanın kullanışlı bir yoludur. Aşağıdaki sınıf, bir içeri aktarma bildirimini ImportingConstructor kullanarak yapar.
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;
}
}
Varsayılan olarak, ImportingConstructor özniteliği tüm parametre içeri aktarma işlemleri için çıkarımlı sözleşme türlerini ve sözleşme adlarını kullanır. Daha sonra sözleşme türünü ve sözleşme adını açıkça tanımlayabilen özniteliklerle Import parametreleri süsleyerek bunu geçersiz kılmak mümkündür. Aşağıdaki kod, üst sınıf yerine türetilmiş bir sınıfı içeri aktarmak için bu söz dizimini kullanan bir oluşturucuyu gösterir.
<ImportingConstructor()>
Public Sub New(<Import(GetType(IMySubAddin))> ByVal MyAddin As IMyAddin)
End Sub
[ImportingConstructor]
public MyClass([Import(typeof(IMySubAddin))]IMyAddin MyAddin)
{
_theAddin = MyAddin;
}
Özellikle, koleksiyon parametrelerine dikkat etmelisiniz. Örneğin, türü ImportingConstructor olan bir parametreyi içeren bir oluşturucuda IEnumerable<int> belirtirseniz, içeri aktarma, türü IEnumerable<int> olan bir dışarı aktarma kümesi yerine türü int olan tek bir dışarı aktarmayla eşleşecektir.
int türünde bir dışarı aktarma kümesini uyum sağlamak için, parametreyi ImportMany özniteliğiyle süslemeniz gerekir.
özniteliği tarafından ImportingConstructor içeri aktarma olarak bildirilen parametreler de önkoşul içeri aktarmaları olarak işaretlenir. MEF normalde dışarı ve içeri aktarmaların bir döngü oluşturmasına izin verir. Örneğin, bir döngü, A nesnesinin B nesnesini içeri aktardığı ve A nesnesini içeri aktardığı yerdir. Normal koşullarda bir döngü sorun değildir ve oluşturma kapsayıcısı her iki nesneyi de normal şekilde oluşturur.
Bir bölümün oluşturucusunda içeri aktarılan bir değer gerektiğinde, bu nesne bir döngüye katılamaz. A nesnesi, B nesnesinin kendisi oluşturulabilmesi için önce oluşturulabilmesini gerektiriyorsa ve B nesnesi A nesnesini içeri aktarıyorsa, döngü çözülemez ve bir oluşturma hatası oluşur. Bu nedenle, oluşturucu parametrelerinde bildirilen içeri aktarmalar önkoşul içeri aktarmalarıdır ve bunları gerektiren nesneden yapılan dışarı aktarmalardan herhangi birinin kullanılabilmesi için bunların tümünün doldurulması gerekir.
İsteğe Bağlı İçeri Aktarmalar
özniteliği, Import parçanın çalışması için bir gereksinim belirtir. İçeri aktarma gerçekleştirilemezse, bu bölümün bileşimi başarısız olur ve bölüm kullanılamaz.
özelliğini kullanarak içeri aktarmanın AllowDefault olduğunu belirtebilirsiniz. Bu durumda, içeri aktarma herhangi bir kullanılabilir dışarı aktarmayla eşleşmese bile bileşim başarılı olur ve içeri aktarma özelliği, özellik türü için varsayılan değerlere ayarlanır: nesne özellikleri için null, Boole'lar için false, veya sayısal özellikler için sıfır. Aşağıdaki sınıf, isteğe bağlı bir içeri aktarma kullanır.
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.
}
Birden Çok Nesneyi İçeri Aktarma
Import Özniteliği yalnızca bir ve yalnızca bir dışarı aktarmayla eşleştiğinde başarıyla oluşturulacaktır. Diğer durumlar bir oluşturma hatasına neden olur. Aynı sözleşmeyle eşleşen birden fazla dışa aktarmayı içeri aktarmak için ImportMany özniteliği kullanın. Bu öznitelikle işaretlenmiş içeri aktarma işlemleri her zaman isteğe bağlıdır. Örneğin, uyumlu dışarı aktarmalar mevcut değilse bile bileşim başarısız olmaz. Aşağıdaki sınıf IMyAddin türünde herhangi bir sayıda veri aktarımını içeri aktarır.
Public Class MyClass1
<ImportMany()>
Public Property MyAddin As IEnumerable(Of IMyAddin)
End Class
public class MyClass
{
[ImportMany]
public IEnumerable<IMyAddin> MyAddin { get; set; }
}
İçeri aktarılan diziye normal IEnumerable<T> söz dizimi ve yöntemleri kullanılarak erişilebilir. Bunun yerine sıradan bir dizi (IMyAddin[]) kullanmak da mümkündür.
Bu deseni söz dizimi ile Lazy<T> birlikte kullandığınızda çok önemli olabilir. Örneğin, ImportMany, IEnumerable<T> ve Lazy<T> kullanarak, herhangi bir sayıda nesneye dolaylı başvuruları içeri aktarabilir ve yalnızca gerekli olanların örneklerini oluşturabilirsiniz. Aşağıdaki sınıf bu düzeni gösterir.
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; }
}
Keşfedilmekten Kaçınma
Bazı durumlarda, bir bölümün kataloğun parçası olarak bulunmasını engellemek isteyebilirsiniz. Örneğin, parça devralınması amaçlanan ancak kullanılmayan bir temel sınıf olabilir. Bunu yapmanın iki yolu vardır. İlk olarak, part sınıfında anahtar sözcüğünü abstract kullanabilirsiniz. Soyut sınıflar hiçbir zaman dışa aktarımlar sağlamaz, ancak bu sınıflardan türetilen sınıflara devredilen dışa aktarımlar sağlayabilirler.
Sınıf soyut yapılamazsa, PartNotDiscoverable özniteliğiyle işaretleyebilirsiniz. Bu öznitelikle dekore edilmiş bir bölüm hiçbir katalogda yer almayacaktır. Aşağıdaki örnekte bu desenler gösterilmektedir.
DataOne katalog tarafından keşfedilecektir. Soyut DataTwo olduğundan keşfedilmeyecek.
DataThree özniteliği kullanıldığı için, PartNotDiscoverable keşfedilmeyecek.
<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.
}
Meta Veri ve Meta Veri Görünümleri
Dışarı aktarmalar, kendileri hakkında meta veri olarak bilinen ek bilgiler sağlayabilir. Dışarı aktarılan nesnenin özelliklerini içeri aktarma bölümüne iletmek için meta veriler kullanılabilir. İçeri aktarma bölümü, hangi dışarı aktarmaların kullanılacağına karar vermek veya bir dışarı aktarma hakkında oluşturmak zorunda kalmadan bilgi toplamak için bu verileri kullanabilir. Bu nedenle, içeri aktarma işlemi meta verileri kullanmak için gecikmeli olmalıdır.
Meta verileri kullanmak için genellikle meta veri görünümü olarak bilinen ve hangi meta verilerin kullanılabilir olacağını bildiren bir arabirim bildirirsiniz. Meta veri görünümü arabirimi sadece özelliklere sahip olmalıdır ve her bir özelliğin get erişimcileri olmalıdır. Aşağıdaki arabirim örnek bir meta veri görünümüdür.
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; }
}
Meta veri görünümü olarak genel bir koleksiyon IDictionary<string, object>kullanmak da mümkündür, ancak bu tür denetiminin avantajlarını kaybeder ve bundan kaçınılmalıdır.
Normalde, meta veri görünümünde adlı tüm özellikler gereklidir ve bunları sağlamayan dışarı aktarmalar eşleşme olarak kabul edilmez.
DefaultValue özniteliği bir özelliğin isteğe bağlı olduğunu belirtir. Özelliği dahil değilse, parametresi DefaultValueolarak belirtilen varsayılan değer atanır. Aşağıda meta verilerle dekore edilmiş iki farklı sınıf vardır. Bu sınıfların her ikisi de önceki meta veri görünümüyle eşleşti.
<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
{
}
Meta veriler, Export özniteliğinden sonra ExportMetadata özniteliği kullanılarak ifade edilir. Her meta veri parçası bir ad/değer çifti oluşur. Meta verilerin ad bölümü, meta veri görünümündeki uygun özelliğin adıyla eşleşmelidir ve değer bu özelliğe atanır.
Hangi meta veri görünümü kullanılacaksa, bunu belirleyen içeri aktarıcıdır. Meta verilerle içeri aktarma işlemi gecikmeli içeri aktarma olarak bildirilir ve meta veri arabirimi için ikinci tür parametresi Lazy<T,T>olarak bildirilir. Aşağıdaki sınıf meta verilerle önceki bölümü içeri aktarır.
Public Class Addin
<Import()>
Public Property plugin As Lazy(Of IPlugin, IPluginMetadata)
End Class
public class Addin
{
[Import]
public Lazy<IPlugin, IPluginMetadata> plugin;
}
Birçok durumda, kullanılabilir içeri aktarmaları arasından yalnızca birini seçip örnek oluşturmak veya belirli bir koşulu karşılayacak şekilde bir koleksiyonu filtrelemek için meta verileri ImportMany özniteliği ile birleştirmek istersiniz. Aşağıdaki sınıf, yalnızca IPlugin nesnelerden Name değeri "Logger" olanları örnekler.
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;
}
}
Miras İthalatı ve İhracatı
Bir sınıf bir bölümden devralıyorsa, bu sınıf da bir parça haline gelebilir. Importlar her zaman alt sınıflar tarafından devralınır. Bu nedenle, bir bölümün alt sınıfı, ebeveyn sınıfıyla aynı aktarılacak içerikle her durumda parça olarak kalacaktır.
özniteliği kullanılarak Export bildirilen dışarı aktarmalar alt sınıflar tarafından devralınmıyor. Ancak, bir parça özniteliğini InheritedExport kullanarak kendisini dışarı aktarabilir. Bölümün alt sınıfları, sözleşme adı ve sözleşme türü de dahil olmak üzere aynı ihracatı devralır ve sunar. Özniteliğin Export aksine, InheritedExport üye düzeyinde değil, yalnızca sınıf düzeyinde uygulanabilir. Bu nedenle, üye düzeyinde dışarı aktarmalar hiçbir zaman devralınamıyor.
Aşağıdaki dört sınıf, ithalat ve ihracat kalıtım ilkelerini göstermektedir.
NumTwo öğesinden NumOnedevralır, bu nedenle NumTwo içeri aktarır IMyData. Sıradan ihraçlar devralınmadığından NumTwo hiçbir şey ihraç etmez.
NumFour öğesinden NumThreedevralır.
NumThree kullanıldığındanInheritedExport, NumFour sözleşme türüne NumThree sahip bir ihracat vardır. Üye düzeyindeki dışa aktarmalar hiçbir zaman devralınmaz, bu yüzden IMyData dışa aktarılmaz.
<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.
}
Bir InheritedExport öznitelikle ilişkilendirilmiş meta veriler varsa, bu meta veriler de devralınır. (Daha fazla bilgi için önceki "Meta Veri ve Meta Veri Görünümleri" bölümüne bakın.) Devralınan meta veriler alt sınıf tarafından değiştirilemez. Ancak, aynı sözleşme adı ve sözleşme türüne sahip InheritedExport özelliğini yeni meta verilerle yeniden bildirdiğinde, alt sınıf devralınan meta verileri yeni meta verilerle değiştirebilir. Aşağıdaki sınıf bu ilkeyi gösterir.
MegaLogger bölümü Logger öğesinden devralır ve InheritedExport özniteliğini içerir. Status adlı yeni meta verileri yeniden bildirdiğinden MegaLogger , Ad ve Sürüm meta verilerini öğesinden Loggerdevralmaz.
<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.
}
Meta verileri geçersiz kılmak için özniteliğini yeniden bildirirken InheritedExport , sözleşme türlerinin aynı olduğundan emin olun. (Önceki örnekte sözleşme IPlugin türü verilmiştir.) Bunlar farklıysa, geçersiz kılma yerine ikinci öznitelik, bölümden bağımsız bir dışarı aktarma oluşturur. Bu genellikle, önceki örnekte gösterildiği gibi bir InheritedExport özniteliği geçersiz kılarken sözleşme türünü açıkça belirtmeniz gerekeceği anlamına gelir.
Arabirimler doğrudan örneklenemediğinden, genellikle Export veya Import öznitelikleriyle dekor yapılamaz. Ancak bir arabirim, arabirim düzeyinde bir InheritedExport öznitelikle donatılabilir ve bu öznitelik, ilişkili meta verilerle birlikte tüm uygulayan sınıflar tarafından devralınır. Ancak, arabirimin kendisi bir parça olarak mevcut olmayacaktır.
Özel Dışa Aktarım Öznitelikleri
Temel dışarı aktarma öznitelikleri Export ve InheritedExport, meta verileri öznitelik özellikleri olarak içerecek şekilde genişletilebilir. Bu teknik, birçok bölüme benzer meta veriler uygulamak veya meta veri özniteliklerinin devralma ağacını oluşturmak için kullanışlıdır.
Özel öznitelik sözleşme türünü, sözleşme adını veya diğer meta verileri belirtebilir. Özel bir öznitelik tanımlamak için , (veya ExportAttribute) öğesinden InheritedExportAttribute devralan bir sınıfın MetadataAttribute özniteliğiyle donatılması gerekir. Aşağıdaki sınıf özel bir öznitelik tanımlar.
<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; }
}
Bu sınıf, MyAttribute adında, sözleşme türü IMyAddin olan ve MyMetadata adında bazı meta veriler içeren özel bir öznitelik tanımlar. özniteliğiyle işaretlenmiş bir sınıftaki MetadataAttribute tüm özellikler, özel öznitelikte tanımlanan meta veriler olarak kabul edilir. Aşağıdaki iki bildirim eşdeğerdir.
<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; }
İlk bildirimde, sözleşme türü ve meta veriler açıkça tanımlanır. İkinci bildirimde, sözleşme türü ve meta veriler özelleştirilmiş öznitelikte örtük olarak yer alır. Özellikle çok fazla parçaya (örneğin, yazar veya telif hakkı bilgileri) büyük miktarda özdeş meta verilerin uygulanması gerektiğinde, özel bir öznitelik kullanmak çok zaman ve yineleme tasarrufu sağlayabilir. Ayrıca, çeşitlemelere izin vermek için özel özniteliklerin devralma ağaçları oluşturulabilir.
Özel öznitelikte isteğe bağlı meta veriler oluşturmak için özniteliğini DefaultValue kullanabilirsiniz. Bu öznitelik özel öznitelik sınıfındaki bir özelliğe uygulandığında, süslenmiş özelliğin isteğe bağlı olduğunu ve bir dışa aktarıcı tarafından sağlanması gerekmediğini belirtir. Özellik için bir değer sağlanmazsa, özellik türü (genellikle null, falseveya 0) için varsayılan değer atanır.
Oluşturma İlkeleri
Bir parça içeri aktarmayı belirttiğinde ve oluşturma işlemi gerçekleştirildiğinde, oluşturma kapsayıcısı eşleşen bir dışarı aktarma bulmaya çalışır. İçeri aktarma işlemi bir dışarı aktarma işlemiyle başarıyla eşleşirse, içeri aktarılan üye dışarı aktarılan nesnenin bir örneğine ayarlanır. Bu örneğin geldiği yer, dışarı aktarma bölümünün oluşturma ilkesi tarafından denetleniyor.
İki olası oluşturma ilkesi paylaşılan ve paylaşılmayan. Paylaşılan oluşturma ilkesine sahip bir bölüm, bu sözleşmeye sahip bir parça için kapsayıcıdaki her içeri aktarma arasında paylaşılır. Oluşturma altyapısı bir eşleşme bulduğunda ve içeri aktarma özelliği ayarlaması gerektiğinde, yalnızca bir tane yoksa bölümün yeni bir kopyasını oluşturur; aksi takdirde, mevcut kopyayı sağlar. Bu, birçok nesnenin aynı bölüme başvurulara sahip olabileceği anlamına gelir. Bu tür parçalar, birçok yerden değiştirilebilen içsel duruma güvenmemelidir. Bu ilke statik parçalar, hizmet sağlayan parçalar ve çok fazla bellek veya başka kaynak kullanan bölümler için uygundur.
Dışarı aktarmalarından biri için eşleşen bir içeri aktarma bulunduğunda, paylaşılmayanların oluşturma ilkesine sahip bir bölüm oluşturulur. Bu nedenle, kapsayıcıdaki parçanın dışarı aktarılan sözleşmelerinden biriyle eşleşen her içeri aktarma işlemi için yeni bir kopya örneği oluşturulur. Bu kopyaların iç durumu paylaşılmaz. Bu ilke, her bir dışa aktarmanın kendi iç durumunu gerektirdiği bölümler için uygundur.
Hem içeri aktarma hem dışarı aktarma, bir bölümün oluşturma ilkesini Shared, NonShared, veya Any değerlerinden biri arasından belirtebilir. Varsayılan değer Any hem içeri aktarmalar hem de dışarı aktarmalar içindir.
Shared veya NonShared belirten bir dışarı aktarma, yalnızca aynı özelliği belirten veya Any belirten bir içeri aktarmayla eşleşecektir. Benzer şekilde, Shared veya NonShared öğesini belirten bir içeri aktarma yalnızca aynı öğeyi belirten veya Any öğesini belirten bir dışarı aktarmayla eşleşecektir. Uyumsuz oluşturma ilkelerine sahip içeri ve dışarı aktarma işlemleri, sözleşme adı veya sözleşme türü eşleşmeyen içeri ve dışarı aktarma işlemleriyle aynı şekilde eşleşme olarak kabul edilmez. İçeri ve dışarı aktarma işlemleri her ikisi de Any belirtiyorsa veya oluşturma ilkesi belirtmeyip varsayılan olarak Any ise, oluşturma ilkesi varsayılan olarak paylaşılan olur.
Aşağıdaki örnekte oluşturma ilkelerini belirten hem içeri hem de dışarı aktarma işlemleri gösterilmektedir.
PartOne bir oluşturma ilkesi belirtmediğinden varsayılan değer olur Any.
PartTwo bir oluşturma ilkesi belirtmediğinden varsayılan değer olur Any. Import ve dışa aktarma varsayılan olarak Any olduğu için, PartOne paylaşılacaktır.
PartThree bir Shared oluşturma ilkesi belirtir, bu nedenle PartTwo ve PartThree aynı kopyasını PartOnepaylaşır.
PartFour bir NonShared oluşturma ilkesi belirtir, bu nedenle PartFour içinde PartFivepaylaşılmayacaktır.
PartSix bir NonShared oluşturma ilkesi belirtir.
PartFive ve PartSix her biri PartFour'nin ayrı kopyalarını alacak.
PartSeven bir Shared oluşturma ilkesi belirtir.
PartFour öğesi Shared oluşturma ilkesine sahip bir dışa aktarma olmadığından, PartSeven içeri aktarma hiçbir şeyle eşleşmez ve doldurulmayacaktır.
<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.
}
Yaşam Döngüsü ve Kullanımdan Kaldırma
Parçalar bileşim kapsayıcısında barındırıldığından, yaşam döngüleri sıradan nesnelere göre daha karmaşık olabilir. Parçalar yaşam döngüsüyle ilgili iki önemli arabirimi uygulayabilir: IDisposable ve IPartImportsSatisfiedNotification.
Kapatma sırasında çalışmanın gerçekleştirilmesini gerektiren veya kaynakları serbest bırakması gereken bölümler, .NET Framework nesneleri için her zamanki gibi IDisposable uygulamalıdır. Ancak, kapsayıcı parçalara referanslar oluşturduğu ve koruduğu için, yalnızca parçasını bulunduran kapsayıcı Dispose yöntemini çağırmalıdır. Kapsayıcı, kendisi IDisposable işlevini uygular ve Dispose temizleme işleminin bir parçası olarak sahip olduğu tüm parçalar üzerinde Dispose çağırır. Bu nedenle, bileşim kapsayıcısını ve ona ait parçaları artık gerekli olmadıklarında her zaman atmalısınız.
Uzun ömürlü birleştirme kapsayıcıları için, paylaşılmayan oluşturma ilkesine sahip parçaların bellek tüketimi sorun olabilir. Bu paylaşılmayan parçalar birden çok kez oluşturulabilir ve kapsayıcının kendisi çöpü atılana kadar bu parçalar atılmayacaktır. Bununla başa çıkmak için kapsayıcı ReleaseExport yöntemini sunar. Paylaşılmayan bir dışarı aktarmada bu yöntemin çağrılması, bu dışarı aktarmayı bileşim kapsayıcıdan kaldırır ve yok eder. Yalnızca kaldırılan dışa aktarma tarafından kullanılan ve bu hiyerarşi içinde yer alan diğer parçalar da çıkarılır ve imha edilir. Bu şekilde, kompozisyon kabını yok etmeye gerek kalmadan kaynaklar geri kazanılabilir.
IPartImportsSatisfiedNotification adlı OnImportsSatisfiedbir yöntem içerir. Bu yöntem, birleştirme tamamlandığında ve bölümün içeri aktarmaları kullanıma hazır olduğunda arabirimi uygulayan tüm parçalar üzerinde oluşturma kapsayıcısı tarafından çağrılır. Parçalar, diğer parçaların içeri aktarımlarını doldurmak için bileşim motoru tarafından oluşturulur. Bir bölümün içeri aktarmaları ayarlanmadan önce, bu değerler özniteliği kullanılarak ImportingConstructor önkoşul olarak belirtilmediği sürece bölüm oluşturucusunda içeri aktarılan değerleri kullanan veya işleyen herhangi bir başlatma gerçekleştiremezsiniz. Bu normalde tercih edilen yöntemdir, ancak bazı durumlarda oluşturucu enjeksiyonu bulunmayabilir.
OnImportsSatisfied içinde başlatma gerçekleştirilebilir ve IPartImportsSatisfiedNotification bölümünü uygulamak gerekir.