Quello che stai descrivendo è un'architettura aggiuntiva o plug-in. In C # è facile caricare i tipi dalle DLL in modo dinamico. Di solito si dovrebbe avere una libreria di classi (DLL) separata contenente solo le dichiarazioni dell'interfaccia a cui farà riferimento l'applicazione principale e i componenti aggiuntivi. Ovviamente si può anche ereditare dalle classi base, ma si dovrebbe preferibilmente programmare contro interfacce (dove ogni classe base implementa le interfacce). Questo ti dà la possibilità di ereditare da un'implementazione esistente o di fornire un'implementazione completamente nuova e indipendente.
In C # non fa differenza se una classe eredita da una classe nello stesso assembly o una classe in un altro assembly. Tipi e membri devono tuttavia essere public
, per essere accessibili da un altro assembly (EXE, DLL). I tipi e i membri dichiarati come internal
sono visibili solo all'interno del proprio assembly.
Nel tuo progetto devi aggiungere un riferimento all'altro assemblaggio o progetto per poter usare i suoi tipi pubblici. Nella soluzione explorer, fare clic con il pulsante destro del mouse sul progetto e selezionare "Aggiungi riferimento ..." Nella scheda "Sfoglia" selezionare un'altra DLL o EXE. Se stai facendo riferimento a un progetto all'interno della stessa soluzione, seleziona l'altro progetto dalla scheda "Progetti". In alternativa, è possibile "Copia come riferimento del progetto" nel menu di scelta rapida del progetto e nell'altro progetto "Incolla riferimento" nel menu di scelta rapida della cartella Progetto / Riferimenti. I riferimenti del progetto cambiano automaticamente il riferimento tra Debug / Release mentre si modifica la configurazione. Se fai riferimento direttamente a una DLL, di solito fai riferimento alla versione "Release".
È una buona idea avere un'interfaccia IAddIn
che deve essere implementata da tutti i componenti aggiuntivi. Puoi inserire ciò che vuoi in questa interfaccia (nome del componente aggiuntivo, metodo di inizializzazione, ecc.). Ulteriori interfacce che possono essere implementate opzionalmente sono una buona idea. Gli esempi sono interfacce che consentono ai componenti aggiuntivi di fornire voci di menu, di utilizzare le impostazioni dell'utente, di fornire controlli utente che verranno presentati in schede dall'applicazione principale, ecc.
public interface IAddIn
{
// Allows you to list the loaded add-ins in an "About" dialog.
string Name { get; }
string Version { get; set; } // Set by the add-in loader.
}
public interface IMenuItemProvider
{
IEnumerable<ToolStripMenuItem> MenuItems { get; }
}
public interface IContent
{
string Name { get; }
Control Content { get; } // Usually a UserControl containing other controls
}
public interface IContentProvider
{
IEnumerable<IContent> Contents { get; }
}
Questo è il caricatore del componente aggiuntivo che sto utilizzando in una delle mie applicazioni. Analizza tutte le DLL trovate in una directory per le classi che implementano l'interfaccia IAddIn
e restituisce gli oggetti aggiuntivi istanziati in un elenco.
public class AddInLoader
{
public IList<IAddIn> Load(string folder)
{
var addIns = new List<IAddIn>();
string[] files = Directory.GetFiles(folder, "*.dll");
foreach (string file in files) {
addIns.AddRange(LoadFromAssembly(file));
}
return addIns;
}
private static IEnumerable<IAddIn> LoadFromAssembly(string fileName)
{
Assembly asm = Assembly.LoadFrom(fileName);
string addInInterfaceName = typeof(IAddIn).FullName;
foreach (Type type in asm.GetExportedTypes()) {
Type interfaceType = type.GetInterface(addInInterfaceName);
if (interfaceType != null && (type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract) {
IAddIn addIn = (IAddIn)Activator.CreateInstance(type);
addIn.Version = asm.GetName().Version.ToString();
yield return addIn;
}
}
}
}
L'applicazione principale può quindi caricare i componenti aggiuntivi e verificare se implementano interfacce e agire di conseguenza.
var loader = new AddInLoader();
IList<IAddIn> addIns = loader.Load(myAddInFolder);
foreach (IAddIn addIn in addIns) {
var menuProvider = addIn as IMenuItemProvider;
if (menuProvider != null) {
foreach (ToolStripMenuItem menuItem in menuProvider.MenuItems) {
//TODO: Add menu item to application menu
}
}
var contentProvider = addIn as IContentProvider;
if (contentProvider != null) {
foreach (IContent content in contentProvider.Contents) {
//TODO: Add content to new tab pages
}
}
}