Öznitelikli programlama modeline genel bakış (MEF)
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, içeri aktarmalar ve dışarı aktarmalar 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 ve öznitelikleriyle sınıflar veya üyeler Import
Export
süslenerek 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şmesi belirli bir içeri aktarmayı gerçekleştirmek için dikkate alınan bir dışarı aktarma ise.
Sözleşme parametrelerinden biri veya her ikisi örtük veya açık olabilir. Aşağıdaki kod, temel içeri aktarmayı bildiren 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çeri aktarmada özniteliğin Import
bir sözleşme türü veya sözleşme adı parametresi eklenmemiştir. Bu nedenle, her ikisi de dekore edilmiş özellikten çıkarılacaktır. Bu durumda, sözleşme türü olur IMyAddin
ve 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 dışarı aktarmalarla eşleşecektir.)
Aşağıda, önceki içeri aktarmayla 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ünün IMyAddin
özniteliğinin Export
parametresi olarak belirtildiği içindir. 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 IMyAddin
uygular. 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.
Not
Dışarı aktarmalar ve içeri aktarmalar 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 dışarı aktarmayı 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ışarı aktarmada, sözleşme türü yerine IMyAddin
olurMyLogger
. uygulamasına IMyAddin
rağmen MyLogger
ve bu nedenle bir IMyAddin
nesneye yayınlanabilir, ancak bu dışarı aktarma işlemi önceki içeri aktarmayla eşleşmez çünkü sözleşme türleri aynı değildir.
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 Export
özniteliğinin ilk parametresi Import
olarak belirtilebilir. Aşağıdaki kod, belirtilen sözleşme adına sahip bir içeri aktarma ve dışarı aktarma işlemini MajorRevision
gö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, yine de içeri veya dışarı aktarma türünden çıkarı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, alan bir dizeyse MajorRevision
, çıkarılan sözleşme türleri eşleşmez ve aynı sözleşme adına sahip olmasına rağmen dışarı aktarma içeri aktarmayla eşleşmez.
Yöntemi İçeri ve Dışarı Aktarma
Export
özniteliği bir yöntemi de sınıf, özellik veya işlevle aynı şekilde süsleyebilir. Yöntem dışarı aktarmaları bir sözleşme türü veya sözleşme adı belirtmelidir, tür çıkarılamaz. Belirtilen tür özel bir temsilci veya gibi Func
genel bir tür olabilir. Aşağıdaki sınıf adlı DoSomething
bir 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 yöntemi tek DoSomething
int
bir parametre alır ve döndürür string
. Bu dışarı aktarmayı eşleştirmek için içeri aktarma bölümünün uygun bir üye bildirmesi gerekir. Aşağıdaki sınıf yöntemini içeri aktarır 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; }
}
Nesnesinin Func<T, T>
kullanımı hakkında daha fazla bilgi için bkz Func<T,TResult>. .
İçeri Aktarma 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
Bazı durumlarda, içeri aktarma sınıfı belirli bir sözleşme adına sahip herhangi bir türdeki dışarı aktarmaları eşleştirmek isteyebilir. Bu senaryoda, sınıfı dinamik içeri aktarma bildirebilir. Aşağıdaki içeri aktarma işlemi, "TheString" sözleşme adıyla tüm dışarı aktarmalarla 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ürde bir nesneyle ilgilenmeye hazır olması gerekir.
Gecikmeli İçeri Aktarmalar
Bazı durumlarda, içeri aktarma sınıfı içeri aktarılan nesneye dolaylı başvuru gerektirebilir, böylece nesne hemen örneklenmez. Bu senaryoda sınıfı, bir sözleşme türü Lazy<T>
kullanarak gecikmeli içeri aktarma 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; }
}
Birleştirme altyapısı açısından bakıldığında, bir sözleşme türü sözleşme türüyle Lazy<T>
T
aynı kabul edilir. Bu nedenle, önceki içeri aktarma aşağıdaki dışarı aktarmayla eşleşer.
<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 gecikmeli içeri aktarma özniteliğinde belirtilebilir Import
.
Önkoşul İçeri Aktarmaları
Dışarı aktarılan MEF parçaları genellikle doğrudan bir isteğe veya eşleşen içeri aktarmayı doldurma gereksinimine yanıt olarak oluşturma altyapısı tarafından oluşturulur. Varsayılan olarak, bir parça oluştururken, oluşturma altyapısı parametresiz oluşturucuyu kullanır. Altyapının farklı bir oluşturucu kullanmasını sağlamak için özniteliğiyle ImportingConstructor
işaretleyebilirsiniz.
Her parça, bileşim altyapısı tarafından kullanılmak üzere yalnızca bir oluşturucuya sahip olabilir. Parametresiz oluşturucu ve öznitelik sağlama ImportingConstructor
veya birden ImportingConstructor
fazla öznitelik sağlama, 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 sırasında kullanılan içeri aktarmaları bildirmenin kullanışlı bir yoludur. Aşağıdaki sınıf, içeri aktarma bildirmek için kullanır ImportingConstructor
.
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ünde bir parametresi IEnumerable<int>
olan bir oluşturucuda belirtirsenizImportingConstructor
, içeri aktarma türündeki bir dışarı aktarma kümesi yerine türünde IEnumerable<int>
tek bir dışarı aktarmayla int
eşleşecektir. türünde int
bir dışarı aktarma kümesini eşleştirmek için parametresini özniteliğiyle ImportMany
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
isteğe bağlı olduğunu belirtebilirsiniz. Bu durumda, içeri aktarma kullanılabilir dışarı aktarmalarla eşleşmese bile oluşturma başarılı olur ve içeri aktarma özelliği özellik türü için varsayılan değere (null
nesne özellikleri için, false
Boole'lar için veya sayısal özellikler için sıfır) ayarlanı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ı oluşturur. Aynı sözleşmeyle eşleşen birden fazla dışarı aktarmayı içeri aktarmak için özniteliğini ImportMany
kullanın. Bu öznitelikle işaretlenmiş içeri aktarma işlemleri her zaman isteğe bağlıdır. Örneğin, eşleşen dışarı aktarma yoksa oluşturma başarısız olmaz. Aşağıdaki sınıf türünde IMyAddin
herhangi bir sayıda dışarı aktarmayı 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, , IEnumerable<T>
ve Lazy<T>
kullanarakImportMany
, herhangi bir sayıda nesneye dolaylı başvuruları içeri aktarabilir ve yalnızca gerekli olanları örneği 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; }
}
Bulmadan 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ışarı aktarma sağlamaz, ancak bu sınıflardan türetilen sınıflara devralınan dışarı aktarmalar sağlayabilirler.
Sınıf soyut yapılamazsa, özniteliğiyle PartNotDiscoverable
süsleyebilirsiniz. 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 bulunur. Soyut DataTwo
olduğundan, bulunmayacak. DataThree
özniteliği kullanıldığındanPartNotDiscoverable
, bulunmayacak.
<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 yalnızca özelliklere sahip olmalı ve bu özelliklerin erişimcileri olmalıdır get
. 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 DefaultValue
olarak 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 özniteliği kullanılarak özniteliğinden Export
ExportMetadata
sonra 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.
Varsa, hangi meta veri görünümünün kullanılacağını belirten 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;
}
Çoğu durumda, kullanılabilir içeri aktarmalar arasında ayrıştırmak ve yalnızca bir tane seçip örnek oluşturmak veya belirli bir koşulla eşleşecek şekilde bir koleksiyonu filtrelemek için meta verileri özniteliğiyle ImportMany
birleştirmek istersiniz. Aşağıdaki sınıf yalnızca IPlugin
"Logger" değerine sahip nesnelerin örneğini Name
oluşturur.
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;
}
}
Devralmayı İçeri ve Dışarı Aktarma
Bir sınıf bir bölümden devralıyorsa, bu sınıf da bir parça haline gelebilir. İçeri aktarmalar her zaman alt sınıflar tarafından devralınır. Bu nedenle, bir bölümün alt sınıfı, üst sınıfıyla aynı içeri aktarma işlemleriyle her zaman bir parça olacaktı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ı dışarı aktarmayı devralır ve sağlar. Ö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 içeri ve dışarı aktarma devralma ilkelerini gösterir. NumTwo
öğesinden NumOne
devralır, bu nedenle NumTwo
içeri aktarır IMyData
. Sıradan dışarı aktarmalar devralınmadığından hiçbir NumTwo
şey dışarı aktarılmaz. NumFour
öğesinden NumThree
devralır. NumThree
kullanıldığındanInheritedExport
, NumFour
sözleşme türüne NumThree
sahip bir dışarı aktarma işlemi vardır. Üye düzeyinde dışarı aktarmalar hiçbir zaman devralınmadığından dışarı IMyData
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, özniteliği aynı sözleşme adı ve sözleşme türüne sahip ancak yeni meta verilerle yeniden bildirerek InheritedExport
, alt sınıf devralınan meta verileri yeni meta verilerle değiştirebilir. Aşağıdaki sınıf bu ilkeyi gösterir. bölümü MegaLogger
öğesinden Logger
devralır ve özniteliğini InheritedExport
içerir. Status adlı yeni meta verileri yeniden bildirdiğinden MegaLogger
, Ad ve Sürüm meta verilerini öğesinden Logger
devralmaz.
<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 veya Import
öznitelikleriyle Export
süslenemez. Ancak, arabirim, arabirim düzeyinde bir InheritedExport
öznitelikle donatılabilir ve ilişkili meta verilerle birlikte dışarı aktaran tüm uygulayan sınıflar tarafından devralınır. Ancak, arabirimin kendisi bir parçası olarak kullanılamaz.
Özel Dışarı Aktarma Ö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 InheritedExportAttribute
) öğesinden ExportAttribute
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, sözleşme türü IMyAddin
ve adlı bazı meta veriler ile adlı MyAttribute
MyMetadata
ö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üslü özelliğin isteğe bağlı olduğunu ve bir veren tarafından sağlanması gerekmediğini belirtir. Özellik için bir değer sağlanmazsa, özellik türü (genellikle null
, false
veya 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şılır ve paylaşılamaz. 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ç 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 içeri aktarmanın kendi iç durumunu gerektirdiği bölümler için uygundur.
Hem içeri aktarma hem de dışarı aktarma, , NonShared
veya Any
değerleri Shared
arasından bir bölümün oluşturma ilkesini belirtebilir. Varsayılan değer Any
hem içeri aktarmalar hem de dışarı aktarmalar içindir. öğesini belirten Shared
veya NonShared
yalnızca aynı veya öğesini belirten bir içeri aktarmayla eşleşecek bir Any
dışarı aktarma. Benzer şekilde, öğesini belirten Shared
veya NonShared
yalnızca aynı Any
veya öğesini belirten bir dışarı aktarmayla eşleşecek bir içeri aktarma. 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. hem içeri hem de dışarı aktarma işlemi belirtirse Any
veya bir oluşturma ilkesi belirtmezse ve 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
. hem içeri hem de dışarı aktarma varsayılan olarak Any
PartOne
olduğundan paylaşılır. PartThree
bir Shared
oluşturma ilkesi belirtir, bu nedenle PartTwo
ve PartThree
aynı kopyasını PartOne
paylaşır. PartFour
bir NonShared
oluşturma ilkesi belirtir, bu nedenle PartFour
içinde PartFive
paylaşılmayacaktır. PartSix
bir NonShared
oluşturma ilkesi belirtir. PartFive
ve PartSix
her biri ayrı kopyalarını PartFour
alır. PartSeven
bir Shared
oluşturma ilkesi belirtir. oluşturma ilkesiyle Shared
PartSeven
dışarı aktarılmadığındanPartFour
, içeri aktarma hiçbir şeyle eşleşmiyor ve doldurulmayacak.
<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ümlerin .NET Framework nesneleri için her zamanki gibi uygulaması IDisposable
gerekir. Ancak, kapsayıcı parçalar için başvurular oluşturduğundan ve koruduğundan, yalnızca bir parçaya sahip olan kapsayıcı üzerinde yöntemini çağırmalıdır Dispose
. Kapsayıcının kendisi uygular IDisposable
ve içindeki Dispose
temizleme işleminin bir parçası olarak sahip olduğu tüm parçaları çağırır Dispose
. Bu nedenle, bileşim kapsayıcısını her zaman atmalısınız ve sahip olduğu parçalar artık gerekli olmadığında.
Uzun süreli birleştirme kapsayıcıları için, paylaşılmayan oluşturma ilkesine sahip bölümlere göre bellek tüketimi sorun haline gelebilir. Bu paylaşılmayan parçalar birden çok kez oluşturulabilir ve kapsayıcının kendisi atılana kadar atılmaz. Bununla başa çıkmak için kapsayıcı yöntemini ReleaseExport
sağlar. Bu yöntemin paylaşılmayan bir dışarı aktarmada çağrılması, bu dışarı aktarmayı oluşturma kapsayıcısından kaldırır ve atılır. Yalnızca kaldırılan dışarı aktarma ve ağaçta olduğu gibi kullanılan parçalar da kaldırılır ve atılır. Bu şekilde, kaynaklar oluşturma kapsayıcısının kendisini yok etmeden geri kazanılabilir.
IPartImportsSatisfiedNotification
adlı OnImportsSatisfied
bir 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 aktarmalarını doldurmak için bileşim altyapısı 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 ekleme kullanılamayabilir. Bu gibi durumlarda, içinde başlatma gerçekleştirilebilir OnImportsSatisfied
ve bölümü uygulaması IPartImportsSatisfiedNotification
gerekir.