Ö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 ImportExport 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 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 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 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.

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 IMyAddinolurMyLogger. uygulamasına IMyAddinrağ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 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, 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 Funcgenel 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 yöntemi tek DoSomethingint 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>Taynı 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 inteşleşecektir. türünde intbir 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 IMyAddinherhangi 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 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 özniteliği kullanılarak özniteliğinden ExportExportMetadata 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 NumOnedevralı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 NumThreedevralır. NumThree kullanıldığındanInheritedExport, NumFour sözleşme türüne NumThreesahip 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 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 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ı MyAttributeMyMetadataö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, 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şı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, , NonSharedveya Anydeğerleri Sharedarası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 Anydışarı aktarma. Benzer şekilde, öğesini belirten Shared veya NonShared yalnızca aynı Anyveya öğ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 Anyveya bir oluşturma ilkesi belirtmezse ve varsayılan olarak Anyise, 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 AnyPartOne olduğundan paylaşılı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 ayrı kopyalarını PartFouralır. PartSeven bir Shared oluşturma ilkesi belirtir. oluşturma ilkesiyle SharedPartSeven 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ı IDisposablegerekir. 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 IDisposableve 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ı 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 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 OnImportsSatisfiedve bölümü uygulaması IPartImportsSatisfiedNotificationgerekir.