[C#] Plugin systeem

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Antrax
  • Registratie: April 2012
  • Laatst online: 11:23
Ik ben niet echt een specialist in C#. Ik weet de beginselen, normaal programmeer ik alleen PHP maar ik wilde iets anders proberen. Ik probeer momenteel een systeem te ontwikkelen waarin plugins de GUI en functionaliteiten van de applicatie maken. Ik heb een soort draft gemaakt maar ik heb totaal geen idee of dit de juiste methode is en de juiste weg.

Tot nu toe heb ik deze code; hopelijk kan iemand mij in de juiste richting sturen. Er komen veel foutmeldingen in voor en ik denk dat iPlugin totaal niet iets is waar ik naar op zoek ben. Google vertelt mij weinig en vertelt mij alleen veel over console apps ipv. gui apps.

Structuur:
  • App - Nu alleen lege form en lege program.cs (hier wordt de gui en de plugins geladen)
  • App.Core (de plugin core en voor language strings etc.)
  • Example Plugin
C#: App.Core/Classes/IPlugin.cs
1
2
3
4
5
6
7
8
9
10
11
12
namespace App.Core
{
    public interface IPlugin
    {
        string Name { get; }
        string Version { get; }

        bool OnLoad();
        void OnConfigure();
        object DataExchange(object objData);
    }
}
C#: App.Core/Classes/PluginCollection.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;
using System.Collections;

namespace App.Core
{
    public class PluginCollection : CollectionBase
    {
        Collection iPluginCollection; // The type or namespace name 'Collection' could not be found (are you missing a using directive or an assembly reference?)
        
        private IPlugin Plugin;
        
        public virtual void Add(IPlugin Plugin)
        {
            innerlist.Add(Plugin); // The name 'innerlist' does not exist in the current context
        }

        public virtual void AddRange(IPlugin[] Plugin)
        {
            innerlist.AddRange(Plugin); // The name 'innerlist' does not exist in the current context
        }

        public IPlugin Item(Int32 Index)
        {
            return innerlist.Item[Index]; // The name 'innerlist' does not exist in the current context
        }

        public IPlugin Item(string Name) // Shit, this is not working
        {
            IPlugin plug;

            foreach (plug in innerlist)
            {
                if ((plug.Name.ToLower().CompareTo(Name.ToLower()) == 0))
                {
                    return plug;
                }
            }
        }

        public void Remove(IPlugin Plugin)
        {
            innerlist.Remove(Plugin); // The name 'innerlist' does not exist in the current context
        }

        public PluginCollection()
        {
            iPluginCollection = new Collection(); // The type or namespace name 'Collection' could not be found (are you missing a using directive or an assembly reference?)
        }

        protected override void Finalize()  // Intend to declare a destructor? Is this even needed?
        {
            iPluginCollection = null;
            base.Finalize(); // IDE Suggests not to call this due to garbage  because it gets called by the destructor? I have pain in my head
        }
    }
}
C#: App.Core/Classes/PluginLocator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using System;
using System.Reflection;

namespace App.Core
{
    [Serializable()]

    public class PluginLocator
    {
        public PluginCollection FindPlugins(string path)
        {
            if (!System.IO.Directory.Exists(path))
            {
                return default(PluginCollection);
            }

            PluginCollection results = new PluginCollection();
            string[] files = System.IO.Directory.GetFiles(path, "*.*");
            Type iface = default(Type);
            Type t = default(Type);
            string file = "";

            foreach (string _file in files)
            {
                file = _file;

                try
                {
                    Assembly asm = Assembly.LoadFrom(file);

                    foreach (Type loop_t in asm.GetTypes())
                    {
                        t = loop_t;

                        foreach (Type loop_v_iface in t.GetInterfaces())
                        {
                            iface = loop_v_iface;

                            if (typeof(IPlugin) == iface)
                            {
                                IPlugin plug = (IPlugin)(Activator.CreateInstance(t));

                                if (plug.OnLoad())
                                {
                                    results.Add(plug);
                                }
                            }
                        }
                    }
                }
                catch (Exception)
                {
                }
            }

            return results;
        }
    }
}
C#: ExamplePlugin/ExamplePlugin.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using App.Core;

namespace App.Plugins
{
    public class ExamplePlugin : IPlugin
    {
        public string Name
        {
            get
            {
                return "Example Plugin";
            }
        }

        public string Version
        {
            get
            {
                return "0.0.1";
            }
        }

        public bool OnLoad()
        {
            return true;
        }

        public void OnConfigure()
        {
            //
        }

        public object DataExchange(object objData)
        {
            return null;
        }
    }
}

.Gertjan.: Ik ben een zelfstandige alcoholist, dus ik bepaal zelf wel wanneer ik aan het bier ga!


Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

Voor plugins zou ik MEF gebruiken, dat lost vrij veel problemen voor je op: MSDN: Managed Extensibility Framework (MEF)

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Ik zou eens naar MEF kijken i.p.v. een NIH oplossing bouwen ;)

@HMS: Voorkruipert :( :P

[ Voor 6% gewijzigd door RobIII op 28-10-2015 12:42 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • DexterDee
  • Registratie: November 2004
  • Laatst online: 13:01

DexterDee

I doubt, therefore I might be

Het is misschien verstandig om je eerst conceptueel in te lezen in hoe plugins hun werk doen. De exacte structuur van een plugin manager en een individuele plugin is belangrijk, maar is van ondergeschikt belang bij de methode waarop de plugin in je core systeem inhaakt.

Technisch gezien heb je heel veel integratiemogelijkheden. Wil je bijvoorbeeld een bepaalde core functionaliteit overschrijven? Of wil je het resultaat van een core functie augmenteren / verrijken?

Mogelijke scenario's waarop een plugin zou kunnen integreren:
  • Via observers welke bepaald gedrag observeren en op basis hiervan events afvuren
  • Via Hooks, die PRE- en POST acties kunnen uitvoeren op bepaalde events
  • Via het subclassen van core classes, om zo core functionaliteit te overschrijven
En dan moet je nog iets bedenken als meerdere plugins op hetzelfde punt willen inhaken. ga je dat toestaan? Zo ja, dan is de volgorde van belang. En bij subclassing zul je dan meerdere overervingen gaan krijgen die allemaal nog compatible moeten zijn met elkaar. Al met al niet eenvoudig.

Design patterns die je kunnen helpen zijn bijvoorbeeld Inversion of Control en dependency injection. Hiermee wordt subclassen een stuk eenvoudiger.

Zoals hierboven ook al genoemd is, is MEF een prima startpunt die al allerlei uitdagingen voor je oplost.

Klik hier om mij een DM te sturen • 3245 WP op ZW


Acties:
  • 0 Henk 'm!

  • Antrax
  • Registratie: April 2012
  • Laatst online: 11:23
Dank je wel allemaal voor jullie suggesties. Ik ga kijken naar MEF.
I get your message :P ;)

.Gertjan.: Ik ben een zelfstandige alcoholist, dus ik bepaal zelf wel wanneer ik aan het bier ga!


Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 19-09 10:19
MEF is eigenlijk al weer ouderwets. Je kunt eigenlijk elke dependency injector gebruikend je wil. Microsoft heeft tegenwoordig Unity. Op mijn werk gebruiken we Ninject voor plug ins.

Om in het kort een 'brids eye view' te geven.

In een algemene assembly definieer je een interface voor functionaliteit die de plugin kan/moet ondersteunen. In de plugin assembly maak je een class die deze plugin implementeert en maak je een module waarin je met je favoriete dependency injector een binding maakt. Tijdens het opstarten van je applicatie lijkt je je dependency injector een module catalog maken en kies je welke modules te laten. Nu weet je dependency injector welke concrete mogelijkheden er zijn voor de IPlugin interface en deze kunnen dan weer in andere delen van je applicatie gebruikt worden.

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
roy-t schreef op woensdag 28 oktober 2015 @ 13:24:
MEF is eigenlijk al weer ouderwets. Je kunt eigenlijk elke dependency injector gebruikend je wil. Microsoft heeft tegenwoordig Unity.
Andersom. Unity was er eerst; is enkel bedoeld als DI container en lifetime manager en is ondertussen overgedragen aan de community, d.w.z. afgestoten en een sterfhuisconstructie in geduwd.

MEF kwam later en is specifiek bedoeld voor plugins: N-ary resolution & hydration, auto-discovery, runtime rebuilds van de composition tree, etc.

[ Voor 11% gewijzigd door R4gnax op 28-10-2015 20:28 ]


Acties:
  • 0 Henk 'm!

  • HurpDerp
  • Registratie: Oktober 2013
  • Laatst online: 18-11-2024
Om je even op weg te helpen met wat foutmeldingen die je krijgt:

-"Collection iPluginCollection;" // The type or namespace name 'Collection' could not be found (are you missing a using directive or an assembly reference?))
Ik neem aan dat je hierbij de class Collection<T> bedoeld, welke in de using System.Collections.ObjectModel; zit. (T wordt gebruikt om aan te duiden dat het een generic is). Probeer dat eens te om te schrijven naar "private Collection<IPlugin> iPluginCollection;" met de benoemde using erbij.

- innerlist.Add(Plugin); // The name 'innerlist' does not exist in the current context
In de base class zit een InnerList (let op de hoofdletters), dit wordt dus InnerList.Add(plugin);

-public IPlugin Item(Int32 Index))
Je hoeft hierbij niet de grootte van de integer te specifieren, public IPlugin Item(int index) volstaat ook.

- je foreach loop
waarom "IPlugin plug;"? Je gebruikt deze verder niet.
"foreach (plug in innerlist)" Hierbij vergeet je het type te definieeren van plug, daarnaast is de naam plug hetzelfde als de plug erboven. Probeer dit eens te herschrijven naar "foreach(var plugin in InnerList)". (var kan je gebruiken in foreach loops, omdat op compiletijd het onderliggende type van var bekend is).
"if ((plug.Name.ToLower().CompareTo(Name.ToLower()) == 0))" Waarom niet .Equals() gebruiken? dus "if(plug.Name.Equals(Name, StringComparison.InvariantCultureIgnoreCase)" (de StringComparison.InvariantCultureIgnoreCase geeft aan dat de vergelijking niet case-sensitive moet zijn, onafhankelijk van de culture)
-Advanced, eventueel zou je het ook als mooie one-liner op kunnen schrijven dmv Linq. ie: "return InnerList.Cast<IPlugin>().FirstOrDefault(x => x.Name.Equals(Name, StringComparison.InvariantCultureIgnoreCase));"

voor de overzichtelijkheid in codeblokje hieronder herschreven:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
public IPlugin Item(string Name) // Shit, this is not working
{
    foreach (var plug in InnerList)
    {
        if (plug.Name.Equals(Name, StringComparison.InvariantCultureIgnoreCase))
        {
            return plug;
        }
    }

    // de one-liner met linq
    // return InnerList.Cast<IPlugin>().FirstOrDefault(x => x.Name.Equals(Name, StringComparison.InvariantCultureIgnoreCase));
}


- De constructor:
probeer dit aan te passen naar de type Collection<T> dus: iPluginCollection = new Collection<IPlugin>();

- De Finalize method
over het algemeen hoef je deze niet te implementeren, evenals de Dispose() method. Deze zijn voornamelijk bedoeld om unmanaged resources en WaitHandles af te handelen.

Zoals anderen al benoemd hebben zijn er zat Frameworks om te gebruiken, maar hopelijk begrijp je hierdoor iets meer wat er fout is atm ;)
Verder hieronder nog wat leesvoer welke wat dieper ingaat op de zojuist benoemde materie:

Generic Collections
var keyword
String.Equals en overloads hiervan
Foreach loop in C#
Dispose en Finalize
Getting started with LINQ

Succes met je applicatie, en happy programming! ;)

[ Voor 1% gewijzigd door HurpDerp op 29-10-2015 14:37 . Reden: tekst edits ]

Pagina: 1