C # eredita da una classe in una DLL diversa

5

Ho bisogno di creare un'applicazione che deve essere altamente modulare e che possa essere facilmente ampliata con nuove funzionalità.

Ho ideato un progetto in cui ho una finestra principale e un elenco di azioni implementate utilizzando un modello di strategia.

Vorrei implementare le classi / interfacce di base in una DLL e avere la possibilità di caricare azioni da DLL che vengono caricate dinamicamente all'avvio dell'applicazione. In questo modo la finestra principale può avviare azioni senza dover ricompilare o ridistribuire una nuova versione. Devo solo (ri) distribuire nuove DLL che posso aggiornare dinamicamente in fase di runtime. Ciò dovrebbe consentire un aggiornamento modulare molto semplice da una fonte online centrale. Le DLL 'action' ereditano tutte la loro struttura dal codice definito nella DLL che definisce la struttura principale del modello di strategia e la sua fabbrica astratta.

Vorrei sapere se C # /.Net consentirà una tale costruzione.

Vorrei anche sapere se questa costruzione presenta grossi problemi in termini di design.

    
posta Onno 21.08.2014 - 18:45
fonte

2 risposte

6

Certo, C # lo supporterà. Dai un'occhiata a questo esempio che utilizza System.Collections.Generic.Dictionary come classe base .

public class MyDictionary : Dictionary<string, int> { }

public class MyMain
{
    public MyMain()
    {
        MyDictionary dictionary = new MyDictionary();
        dictionary.Add("hello", 1);
    }
}

Ci sono alcuni potenziali problemi. Qualsiasi classe ereditaria sarà soggetta a modifiche all'interno della classe base. Quindi non vuoi usare questo approccio con una classe base che probabilmente cambierà frequentemente.

La più grande sfida che ho visto è quando la classe base cambia le firme delle funzioni. Ciò distrugge quasi tutte le classi ereditanti.

    
risposta data 21.08.2014 - 19:09
fonte
4

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
        }
    }
}
    
risposta data 21.08.2014 - 19:20
fonte

Leggi altre domande sui tag