2012-09-28 16 views
1

我在考慮爲我目前正在開發的應用程序的未來插件創建設計合​​適的合同。基本上這個想法是定義一個接口,但我希望應用程序知道當前呈現給系統的插件,並向用戶顯示一個包含它們名稱的插件列表以及一個簡短的描述,這些插件的開發者應該提供,應用程序的用戶不應該能夠輕易改變這個,所以額外的配置文件不是一個選項。我不想爲此使用程序集的文件名的類名稱。此外,我認爲它應該是可訪問的,而無需實例化插件,但也許可以通過反射,例如:assembly.GetType(type).GetProperty("Name").GetValue(null, null).ToString();。當然,我可以提供一些邏輯來檢查是否存在類似if(assembly.GetType(type).GetProperty("Name") != null)的東西,但這也不是一個好主意,因爲如果該屬性不存在,最終用戶將不知道該插件的功能,甚至不知道它的名稱是。 現在,它應該表現得像一個靜態屬性,但靜態不可覆蓋,所以看起來我不能將其聲明爲接口的一部分,也不能將其聲明爲抽象類。也許我錯了,它看起來像一個靜態屬性,我可以通過另一種方法來實現這個功能。所以簡短的問題可能是「如何強制第三方開發者提供關於他的插件的一些元信息」。請指教。設計插件合同

回答

0

要爲您的插件添加一些'元數據',您可以使用屬性。爲插件創建一個自定義屬性,並用反射讀出信息並將其顯示在應用程序中。關於屬性

更多信息:

Creating Custom Attributes (C# and Visual Basic)

Accessing Attributes by Using Reflection

+0

想到了這一點,但是您如何以契約的方式定義期望的屬性,您如何讓開發人員知道應該將什麼屬性應用到他的插件(您期望插件提供給您什麼元數據)?即使你在界面上應用了一個屬性,我也不確定它會如何幫助 - 第三方開發者應該提供這些信息,因此他應該知道應該將哪些屬性應用於他的插件。 – Jyrkka

0

你應該做的基本上是定義你的界面,並期望其他人來實現它們具體的類並給予運行對象你會莫名其妙的事情(你的預定義的方法,配置等)

但有一些機制稱爲依賴注入。這允許您定義界面和入口點,而「系統」負責匹配您的入口點和實施者的混凝土。爲此,有「System.ComponentModel.Composition」命名空間。

我知道有一個叫做「Unity」的框架在做這樣的工作。我猜組合名稱空間有點簡單。您可以檢查某些提示的「ImportAttribute」和「ExportAttribute」類的幫助。

+0

「System.ComponentModel.Composition」(它是Managed Extensibility Framework(MEF)的一部分)和Unity都提供了依賴注入模式的實現。但我不確定讓怪物參與解決小問題是個好主意。試圖找到正確的方法讓第三方開發人員瞭解我將要尋找的內容,而不僅僅是EntryPoint等,還有一些元數據。但是,謝謝。 – Jyrkka

+0

如果你想導入很多接口,依賴注入框架可以讓你滾動tbh。我使用某些.net4的導入/導出屬性。但是如果你的目標是低於v4,那麼你有興趣去引用另一個lib。我傾向於使自己的業務目標始終以服務提供商的身份初始化。如果有任何他們需要的東西,他們會查找他們需要的服務,然後繼續。在這些條款中,您只需要使用Register方法提供服務,該方法僅將插件的服務註冊回提供商。 – zahir

+0

我已經實現了一個簡單的依賴注入,這對於當前情況已經足夠了。此外,我試圖讓代碼儘可能簡單且不言自明,因此,如果程序不利用框架的大部分可能性,我傾向於不使用框架來使用它們。除此之外,我想弄清楚,元數據的規範如何在執行者上執行。試圖學習...... – Jyrkka

1

你可以嘗試用兩個接口:

IAddIn用於在主接口,所有加載項將實施。

IAddInInfo爲是提供外接(名稱,發行商,描述版本等)的元數據中的接口

每個加載應該實現這兩種。一個IAddInInfo實現可以是這樣的:

public class ScannerAddInInfo : IAddInInfo 
{ 
    public string Name { get { return "Scanner"; } } 

    public string Description { get { return "Add-in for acquiring images from a scanner device"; } } 
} 

爲了確保加載項的所有實現都與元數據,可以使IAddIn一個通用的接口,如:

public interface IAddIn<T> where T : IAddInInfo 
{ 
    T Info { get; } 

    //Continue with the rest of the members you would want every add-in to have. 
} 

然後掃描插件實現將是:

public class ScannAddIn : IAddIn<ScannerAddInInfo> 
{ 
    private ScannerAddInInfo _info = new ScannerAddInInfo(); 

    public ScannerAddInInfo Info { get { return _info; } } 

    //Continue with the rest of the IAddIn implementation. 
} 

然後,您可以從一個特殊的加載項文件夾中加載加載項程序集並創建類型實現的實例ng IAddInInfo,並顯示應用程序中發現的加載項的信息。請注意,尚未創建加載項。爲此,您需要添加更多反射來查找實現IAddIn<ScannerAddInInfo>的類型。

爲了使這更簡單,您可以將加載項類型名稱添加到IAddInInfo接口或類似的東西。

此方法的唯一缺點是,即使不包含任何加載項,也必須加載在您的特殊加載項文件夾中找到的所有程序集。

要避免這種情況,您可以嘗試Mono.Cecil。然後,您將不得不做這樣的事情:

AssemblyDefinition ad = AssemblyDefinition.ReadAssembly(assemblyPath); 

foreach (TypeDefinition td in ad.MainModule.GetTypes()) 
{ 
    if (td.BaseType != null && td.BaseType.FullName == "MyNamespace.MyAddInBase") 
    {   
     return true; 
    } 
} 

要加載,您可以使用Assembly.LoadForm的組件和創建加載項的實例和附加的相關信息,該Activator.CreateInstance重載之一。

祝你好運。

+0

這確實是一個有趣的方法,但是......它仍然不能阻止'名稱'和'描述'('版本'等)特性被依賴或基於字段(可以被改變) ,或者一些額外的邏輯。 雖然僅實例化元數據類的可能性很有意思。 – Jyrkka

+0

另請參閱http://mef.codeplex.com/wikipage?title=Exports%20and%20Metadata。它解釋了MEF如何處理元數據。我已經使用包含元數據的惰性導出的MEF。然後你只需檢查元數據,而不是導出的實例,因爲它會被加載。只有當你想加載加載項時,才能訪問導出的值。 –