[C#] Plugins en data

Pagina: 1
Acties:

  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Ik zit met het volgende probleem: In mijn applicatie maak ik gebruik van plugins. Een plugin is gewoon een klasse die erft van een klasse Plugin. Bij het opstarten loop ik alle plugins in de plugin-folder af om te kijken welke plugins er aanwezig zijn. Deze worden door middel van reflection bekeken om te kijken welke public variabelen ze hebben. Deze informatie gebruik ik voor het automatisch genereren van een Form waarmee je de variabelen kan beinvloeden en voor (de nog te implementeren) scripting engine.

Aangezien ik bepaalde metadata nodig heb (minimum waarde, maximum waarde bijvoorbeeld) gebruik ik hiervoor geen standaard .Net datatypen, maar zelf gebakken klasses. Hieronder een deel van de huidige implementatie:

code:
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
    public class ARFloat : ARBaseVar, IVarFloat {
        private float min = -500;
        private float max = 500;
        private float inc = 1;
        private float val = 0;
        public bool Loop = false;

        public ARFloat() { }
        public ARFloat(float v) { Value = v; }

        public void SetAttributes(string n, float mi, float ma, float i) {
            name = n;
            min = mi;
            max = ma;
            inc = i;
        }

        public float Value {
            get { return val; }
            set {
                if (value >= min && value <= max)
                    val = value;
                else if (Loop) {
                    if (value < min) {
                        val = max;
                    } else {
                        val = min;
                    }
                }
            }
        }

        public override System.Windows.Forms.UserControl GetControl() {
            return ARVarGuiFactory.GetUserControl(this);
        }

        public float Min { get { return min; } }
        public float Max { get { return max; } }
        public float Inc { get { return inc; } }

        public override string ToXml(bool enclose) {
            return "<ARFloat loop=\"" + Loop + "\">" + val.ToString() + "</ARFloat>\r\n";
            
        }

        public override void FromXml(XmlReader reader) {
            PresetLoader.LoadARVar(this, reader);
        }
    }

    public class ARFloat2 : ARBaseVar, IVarFloat {
        protected ARFloat x;
        protected ARFloat y;

        public ARFloat2() {
            x = new ARFloat();
            y = new ARFloat();
        }

        public ARFloat2(float xx, float yy) {
            x = new ARFloat(xx);
            y = new ARFloat(yy);
        }

        public void Set(float xx, float yy) {
            this.x.Value = xx;
            this.y.Value = yy;
        }

        public virtual void SetAttributes(string n, float mi, float ma, float i) {
            this.name = n;
            x.SetAttributes(name, mi, ma, i);
            y.SetAttributes(name, mi, ma, i);
        }

        public ARFloat X { get { return x; } set { x = value; } }
        public ARFloat Y { get { return y; } set { y = value; } }

        public override System.Windows.Forms.UserControl GetControl() {
            return ARVarGuiFactory.GetUserControl(this);
        }

        public float Min { get { return x.Min; } }
        public float Max { get { return x.Max; } }
        public float Inc { get { return x.Inc; } }

        public override string ToXml(bool enclose) {
            string ret = x.ToXml(false) + y.ToXml(false);
            if (enclose)
                return "<ARFloat2>\r\n" + ret + "</ARFloat2>\r\n";
            else
                return ret;
        }

        public virtual bool Loop {
            set {
                x.Loop = value;
                y.Loop = value;
            }
        }

        public override void FromXml(XmlReader reader) {
            PresetLoader.LoadARVar(this, reader);
        }
    }

    public class ARFloat3 : ARFloat2, IVarFloat {
        protected ARFloat z;
        public ARFloat3()
            : base() {
            z = new ARFloat();
        }
        public ARFloat3(float xx, float yy, float zz)
            : base(xx, yy) {
            z = new ARFloat(zz);
        }

        public void Set(float xx, float yy, float zz) {
            base.Set(xx, yy);
            this.z.Value = zz;
        }

        public override void SetAttributes(string n, float mi, float ma, float i) {
            base.SetAttributes(n, mi, ma, i);
            z.SetAttributes(n, mi, ma, i);
        }

        public ARFloat Z { get { return z; } set { z = value; } }

        public Vector3 ToVector3() {
            return new Vector3(this.x.Value, this.y.Value, this.Z.Value);
        }

        public override System.Windows.Forms.UserControl GetControl() {
            return ARVarGuiFactory.GetUserControl(this);
        }

        public override string ToXml(bool enclose) {
            string ret = base.ToXml(false) + z.ToXml(false);
            if (enclose)
                return "<ARFloat3>\r\n" + ret + "</ARFloat3>\r\n";
            else
                return ret;
        }

        public override void FromXml(XmlReader reader) {
            PresetLoader.LoadARVar(this, reader);
        }

        public override bool Loop {
            set {
                base.Loop = value;
                z.Loop = value;
            }
        }
    }


om vervolgens deze klassen te gebruiken in de plugin gebruik je de volgende code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PluginDing : Plugin {
        //[ARFloatAttribute("Naam in Form", min, max, increment)]
        [ARFloatAttribute("Size", -1000f, 1000f, 0.2f)]
        public ARFloat2 size;
        [ARIntAttribute("Texture", 0, 63, 1)]
        public ARInt Texture;

        public PluginDing() {
             //initialiseer shit...
        }

       public void DoeIets() {
            //doe nuttige shit
       }
}


Op zich werkt dit, maar er zitten wel wat scherpe kantjes aan. Ten eerste heb ik ook nog ARFloat4 en ARFloat5 klassen, daarnaast heb ik ook nog integers. Oftewel ik heb nog 5 klassen waarin ik dmv zoeken en vervangen alle floats door ints vervang. Dat is niet volgens de regel Do Not Repeat Yourself. Ook moet ik dus om nieuwe functionaliteit toe te voegen 10 klassen aanpassen.

Het eerste idee was om generics te gebruiken (dan zou ik iig de helft van het aantal klasses overhouden), maar dat kan omdat het niet mogelijk is om met Generics te rekenen. Het toevoegen van een IArithmetic interface aan het .net framework zou dit oplossen (zie bijvoorbeeld dit artikel) en het schijnt dat in op dit moment hiernaar gekeken wordt voor de 4.0 release. Maar daar ga ik natuurlijk niet op wachten.

Heeft iemand suggesties over hoe ik dit beter zou kunnen implementeren?

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 19:52

Haan

dotnetter

st0p schreef op zondag 01 juni 2008 @ 17:18:
Aangezien ik bepaalde metadata nodig heb (minimum waarde, maximum waarde bijvoorbeeld) gebruik ik hiervoor geen standaard .Net datatypen, maar zelf gebakken klasses.

Heeft iemand suggesties over hoe ik dit beter zou kunnen implementeren?
Zijn hiervoor geen structures uitgevonden?

Kater? Eerst water, de rest komt later


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Je hebt op zich gelijk dat deze klasses misschien beter structs zouden kunnen zijn, maar dat doet toch niets af aan de explosie van verschillende klasses/structs?

  • whoami
  • Registratie: December 2000
  • Laatst online: 22:26
Kan je niet zelf een interface schrijven, waarin je het 'contract' vastlegt waaraan jouw eigen classes moeten voldoen ?
Dan kan je met generics werken, en afdwingen dat het generic type jouw interface moet implementeren.
Haan schreef op zondag 01 juni 2008 @ 17:40:
[...]

Zijn hiervoor geen structures uitgevonden?
:?
Hoe is dit een oplossing voor het probleem ?

https://fgheysels.github.io/


  • Haan
  • Registratie: Februari 2004
  • Laatst online: 19:52

Haan

dotnetter

whoami schreef op zondag 01 juni 2008 @ 17:48:

Hoe is dit een oplossing voor het probleem ?
Ik had niet helemaal goed gelezen wat het werkelijke probleem was, ik zat alleen naar de voorbeeld code te kijken..

Kater? Eerst water, de rest komt later


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Bedoel je iets als:
code:
1
2
3
4
5
6
interface ICalculate<T> {
    T Add(T t1, T t2);
    T Sub(T t1, T t2);
    T Div(T t1, T t2);
    T Mul(T t1, T t2);
}


Nadeel hiervan vind ik zelf dat je de gewone operatoren (+, -, *, /) niet meer kan gebruiken, aangezien ik het gebruik ervan zo transparant mogelijk wil houden. Ik heb ook nog gekeken of ik operator overloading in een interface kon opnemen maar aangezien deze overloads static zijn kan ik ze niet in een interface knallen.

  • whoami
  • Registratie: December 2000
  • Laatst online: 22:26
Dat is idd een (klein) nadeel , maar het zorgt er wellicht wel voor dat je veel code kunt weggooien ....
't Is aan jou om de voor- en nadelen tov elkaar af te wegen ...

https://fgheysels.github.io/


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
yep, ik zit er zelf ook al even over te filosoferen maar waarschijnlijk is het de beste oplossing. Volgens mij zou ik als ik de classes ook nog static operator overloads geef binnen de plugins zelf ook nog gewoon +, -, / en * kunnen gebruiken. Dat betekend dat ik alleen in de generieke klassen zelf gebruik hoef te maken van de interface.

  • compufreak88
  • Registratie: November 2001
  • Laatst online: 02-05 17:51
Wat je kan doen is een basisklasse maken die de interface implementeert, inclusief de operatoroverloads. De rest van de classes erven hiervan dan over. Als je de operatoroverloads dan MustInherrit maakt, dan krijg je hetzelfde idee.

  • whoami
  • Registratie: December 2000
  • Laatst online: 22:26
Volgens mij kan je geen abstract operators definieren.
Indien het wel zou kunnen, dan kan je natuurlijk gewoon een abstract class maken, met daarin abstract operator overloads, en dan kan je die interface meteen weglaten.
Maar, ik ben 99% zeker dat je geen abstract operators kunt definieren ...

https://fgheysels.github.io/


Verwijderd

Kan je niet van Linq's expression tree's gebruik maken.
http://www.google.nl/sear...on+tree&btnG=Zoeken&meta=

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 16-10 10:47
Op zich voegt het volgende niks toe aan de discussie, en zal zeker niet je probleem oplossen (of wel, hangt af van hoe je er tegen aan kijken).
Maar heb je al eens gekeken naar het Managed AddIn framework? Het is geen eenvoudige materie maar misschien verschaft het je met nieuwe inzichten. Wat altijd goed is natuurlijk ;)
Link naar blogpost:
http://www.danielmoth.com/Blog/2008/02/maf-screencasts.html

Overigens is dit geen verkapte flame naar je plugin implementatie. Zie het gewoon als ter informatie.

  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
@Compufreak88: Ik heb het geprobeerd maar de compiler vindt dat idd niet goed...

@Menno_bakker: Ik zie zeker wel voordelen van je methode (en er is een stuk in mijn applicatie waar functioneel programeren denk ik zeker voordeel heeft, los daarvan is het iets waar ik me sowieso wat meer in wil verdiepen), dus ik ga daar zeker nog verder naar kijken, maar ik heb het gevoel dat het in dit geval geen toegevoegde waarde heeft (correct me if i'm wrong)

@Deathraven: Ik ga die screencasts bekijken (gisteravond werkten ze niet) en als je verkapte flames op mijn oplossing hebt, dan hoor ik het graag!

edit:

Ik heb de 1e screencast zojuist bekeken maar ik ben weinig onder de indruk. Er zitten maar 2 dingen in waarvan ik denk "ja, dat biedt voordeel ten opzichte van wat ik nu heb!"
1) Garbage collection als je dingen in een ander AppDomain laad (maar ik laad niks in een ander AppDomain)
2) Een iets striktere scheiding tussen host en plugin, vanwege de 2 interfaces (architectureel wel een klein voordeel maar ik vind de wildgroei aan klassen die dat oplevert (ook al zijn ze gegenereerd) minder fraai)


@whoami: Ik ben aan het kijken naar de oplossing zoals jij hebt hem aangedragen maar ik heb hem nog niet werkend. Als dat gelukt is zal ik hem hier posten.

[ Voor 36% gewijzigd door st0p op 03-06-2008 20:33 ]


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyPlugin : Plugin
{
   [MaxValue(500)]
   [MinValue(0)]
   public int Property { get; set; }
}

of [Bounds(0,500)]

of
public int Property
{
   get {return property; }
   set { if (value < 0 || value > 500) throw new ArgumentException(); property = value;
}

ASSUME makes an ASS out of U and ME


  • whoami
  • Registratie: December 2000
  • Laatst online: 22:26
Euh ..... :? En wat wil je hiermee zeggen H!GHGuy ?

https://fgheysels.github.io/


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Je kan toch gewoon MaxValue en MinValue of Bounds attributen gebruiken ?
Gebruik dan ook meteen het feit dat bepaalde conversies impliciet gebeuren en gebruik meteen het meest geschikte type.

Of gebruik gewoon excepties en vang die op.
Het gaat over een at runtime gegenereerde GUI, dan is een SetXXX functie die die exceptie opvangt en een mooie error returned niet meer dan normaal.

ASSUME makes an ASS out of U and ME


  • DrDelete
  • Registratie: Oktober 2000
  • Laatst online: 21:14
H!GHGuY schreef op dinsdag 03 juni 2008 @ 22:20:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyPlugin : Plugin
{
   [MaxValue(500)]
   [MinValue(0)]
   public int Property { get; set; }
}

of [Bounds(0,500)]

of
public int Property
{
   get {return property; }
   set { if (value < 0 || value > 500) throw new ArgumentException(); property = value;
}
dat laatste zou ik niet zo snel doen : als programmeur verwacht je geen exceptions bij getters en setters, veelal zou ik de waarde van "value" laten controleren door een constructor initializer en alleen getter overhouden (geen setter meer).

  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
@Highguy: Dat is toch de implementatie zoals ik hem nu heb? Ik gebruik al attributes om min/max te bepalen. In mijn geval is een datatype geen plugin, maar stelt de plugin datatypes zoals jij beschrijft beschikbaar.

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 19:30

Sebazzz

3dp

DrDelete schreef op woensdag 04 juni 2008 @ 07:49:
[...]


dat laatste zou ik niet zo snel doen : als programmeur verwacht je geen exceptions bij getters en setters
Waarom niet? Het is iets wat je constant ziet in het .NET framework.

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


  • DrDelete
  • Registratie: Oktober 2000
  • Laatst online: 21:14
Sebazzz schreef op woensdag 04 juni 2008 @ 08:22:
[...]

Waarom niet? Het is iets wat je constant ziet in het .NET framework.
De guideline betreft vooral getters, bij setters zou het wel mogen hoor, maar persoonlijk doe ik dit niet

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

st0p schreef op woensdag 04 juni 2008 @ 08:18:
@Highguy: Dat is toch de implementatie zoals ik hem nu heb? Ik gebruik al attributes om min/max te bepalen. In mijn geval is een datatype geen plugin, maar stelt de plugin datatypes zoals jij beschrijft beschikbaar.
Bespaar jezelf het typwerk door impliciet aangeboden conversies te gebruiken.

C#:
1
2
3
double d = 100.0;
int i = 90;
Debug.Assert(i < d);


bovendien zijn excepties tijdens setters, helemaal niet uit den boze. Zeker niet in een plugin-structuur waarbij je de implementatie en dus "wat mag en wat niet mag" niet kent.
Je interface van je plugin moet enkel beschrijven wie wat mag opgooien (of maw wat je in het host programma opvangt en wat niet)

[ Voor 24% gewijzigd door H!GHGuY op 04-06-2008 13:00 ]

ASSUME makes an ASS out of U and ME

Pagina: 1