[C#] Implementaties dwingen zich te melden.

Pagina: 1
Acties:
  • 171 views sinds 30-01-2008
  • Reageer

  • Fl4sh3r
  • Registratie: Juni 2002
  • Laatst online: 02-10-2023
Al enige tijd stoei ik met het volgende probleem;

Ik ben bezig met een programma om Winamp te bedienen vanaf Cisco IP Telefoons. Dit werkt via een server die XML aanbiedt die de telefoon snapt.

Om structuur aan te brengen heb ik pagina's bedacht, deze extenden WinampPage. Nu wil ik dat van iedere implementatie van WinampPage een instantie zich (bij het opstarten van mn programma) aanmeldt.

Ik heb nu een klasse WinampPageCollection die in de constructor van iedere klasse die een WinampPage is een instantie toevoegt. Dit is echter hardcoded, dus als ik een nieuwe pagina toevoeg en vergeet dit in WinampPageCollection op te nemen werkt het niet.

Concrete vraag
Hoe dwing ik implementaties van WinampPage zich toe te voegen aan de WinampPageCollection?

WinampPage.cs
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;

namespace WinampCisControl2 {
    public abstract class WinampPage {
        private string _handle = string.Empty;

        public string Handle {
            get { return _handle; }
        }

        public WinampPage(string handle) {
            _handle = handle;
        }

        public abstract CisControlReply HandleRequest(CisControlRequest request);
    }
}


WinampPageCollection:Constructor
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
        public WinampPageCollection() {
            try {
                WinampPage wp;

                wp = new WinampMainMenu();
                _pages.Add(wp.Handle, wp);

                wp = new WinampAbout();
                _pages.Add(wp.Handle, wp);

                wp = new WinampPlaylist();
                _pages.Add(wp.Handle, wp);

                wp = new WinampSearch();
                _pages.Add(wp.Handle, wp);

            } catch(Exception ex) {
                Logger.Log("Exception in WinampPageCollection: " + ex.ToString(), this, LogLevel.ERROR);
            }
        }

  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
Waarom pas je dat WinampPageCollection object niet als argument in de constructor van de winamppage, en laat je die pagina zichzelf toevoegen aan de collectie ?

code:
1
2
3
4
5
6
7
abstract class WinampPage
{
    protected WinampPage( WinampPageCollection coll )
    {
          coll.Add (this);
    }
}

Of zoiets.

Een andere manier kan zijn om het creeëren van WinampPages niet rechtstreeks toe te laten, en dat via een factory te doen, waarbij de factory method dan ook zorgt dat de nieuwe page aan een collectie wordt toegevoegd:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class WinampPageFactory
{

    private WinampPageCollection _coll;

    public WinampPageFactory( WinampPageCollection coll )
    {
        _coll = coll;
    }

    public void CreateSomeWinampPage()
    {
         SomeWinampPage page = new SomeWinampPage();
         _coll.Add (page);
         return page;
    }
}

ofzo.

https://fgheysels.github.io/


  • Fl4sh3r
  • Registratie: Juni 2002
  • Laatst online: 02-10-2023
Het probleem is dan nog steeds dat ik ergens de pagina's moet creëren.
Het toevoegen aan de collection vanuit de WinampPage basis klasse is een goed idee, hiermee was ik ook begonnen. Na veel proberen ben ik daar nog niet op terug gekomen.

Ik wil graag dat het voldoende is om WinampPage te extenden, de nieuwe klasse van een HandleRequest te voorzien en klaar. Ik heb me erbij neergelegd dat ik nu in de nieuwe klasse :base("naam") moet doen. Deze naam is een unieke key waarmee ik de pagina terug wil vinden in de collection. Zonder dat ik dus per pagina nog expliciet code moet schrijven om een instatie creëren.

  • NicoD
  • Registratie: Augustus 2004
  • Laatst online: 19:10
Als je in de constructor van de base-class een Guid genereerd, kun je die registreren en heb je voor iedere instantie een mooie sleutel.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Je zou iets van reflection kunnen gebruiken om alle classes die van WinampPage erven op te zoeken. Dit eventueel gecombineerd met een attribute.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Fl4sh3r
  • Registratie: Juni 2002
  • Laatst online: 02-10-2023
Dat unieke id gebruik ik wel als onderdeel van de URL, dus om daar GUIDs te gebruiken vind ik niet zo. Ik vind "/playlist" duidelijker dan "/63812371236ACF1A3CA237867B54E69C".

Weet iemand hoe ik het met de Reflection ongeveer zou kunnen doen? Ik heb hier ook wel even aan gedacht, maar heb geen idee hoe ik dat zou moeten doen. Ik ben totaal niet thuis in de Reflection.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Dat zou iets als het volgende zijn
C#:
1
2
3
4
5
6
7
8
Assembly assembly = Assembly.GetExecutingAssembly();
foreach( Type t in assembly.GetTypes() )
{
    if( t.IsSubclassOf( typeof( WinampPage) ) )
    {
        //Doe wat met t. Bijvoorbeeld een instance maken en aanmelden
    }
}

Als je dit dan bijvoorbeeld in een initializatie functie doet kan je alle types opzoeken in een assembly die extenden van WinampPage.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • NicoD
  • Registratie: Augustus 2004
  • Laatst online: 19:10
Je zou ook nog de handle-property van je abstracte class WinampPage abstract kunnen maken.
De afgeleide class moet die dan implementeren (mede gecontroleerd door de compiler)
En die kun je dan in de constructor van WinampPage uitvragen en registreren.'

Dan krijg je alleen niet van die mooie l33t-naampjes in je url..

  • NicoD
  • Registratie: Augustus 2004
  • Laatst online: 19:10
Bijvoorbeeld:
(Simpele console-app, naam moet handle zijn enz.)
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
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      Afgeleid a = new Afgeleid();
      Afgeleid2 a2 = new Afgeleid2();
      Console.ReadLine();
    }
  }

  public abstract class Basis
  {
    public abstract string naam{get ;}

    public Basis()
    {
      Console.WriteLine("nieuwe " + naam);
    }
  }

  public class Afgeleid : Basis
  {
    public override string naam
    {
      get { return "a1"; }
    } 
  }

  public class Afgeleid2 : Basis
  {
    public override string naam
    {
      get {  return "a2"; }
    }
  }
}

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
NicoD schreef op woensdag 30 augustus 2006 @ 17:46:
Bijvoorbeeld:
(Simpele console-app, naam moet handle zijn enz.)
code:
1
...
Dat heeft niet zo veel met het probleem van de TS te maken. Het gaat de TS er om dat hij niet alle WinampPages hard-coded aan moet maken omdat hij dan voor elke page die hij maakt de code weer moet aanpassen.

Door middel van reflection is het het makkelijkst te realiseren aangezien je dan ook een systeem kan bouwen die runtime extra assembly's kan laden en zo dus ook later nog extra winamppages toe kan voegen.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Feyd-Rautha
  • Registratie: November 2001
  • Laatst online: 02-08-2025
Dit is een voorbeeld van een crosscutting concern. Het toevoegen van die pagina in de collection gebeurt op verschillende plaatsen in uw programma: namelijk in iedere constructor van een WinampPage-implementatie.

Daarom zou het interessant zijn om een Aspect daarvoor te kunnen schrijven. Maar misschien moet je je eerst eens verdiepen in de AOP wereld.
Er bestaan reeds voor vele talen frameworks waarin je aspecten kunt schrijven (Spring, JBoss zijn vb voor Java, met NKalore kun je aspecten in C# schrijven).

In dit aspect zou je dan een nieuwe PageCollection member-variabele kunnen introduceren. Verder kun je dan ervoor zorgen dat je na de aanroep van de constructor het huidige object in die PageCollection kunt stoppen.

Aspecten worden geweaved met je source-programma waardoor de source-code wordt uitgebreid met de code van het aspect. Daardoor hoef je nu maar eenmaal de code te schrijven die de page in de collection toevoegt. Voor iedere constructor van een object van het type WinampPage wordt dan het aspect uitgevoerd.

Ik weet niet in hoeverre er (goede) ondersteuning is voor aspecten in C#, maar dit zou toch een mooie oplossing zijn.

[ Voor 4% gewijzigd door Feyd-Rautha op 30-08-2006 20:33 ]

I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. Where the fear has gone there will be nothing. Only I will remain.


  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
Hoe zou je dit dan concreet in Java doen bv ?

Maar, wat heeft Spring met AOP te maken ? Spring is toch een Inversion of Control container (die je ook voor .NET hebt, in de voor van spring.net).

En wat doet die NKalore precies ? Volgens hetgeen ik er van kan terugvinden, is het een taal die een extensie is op C#, en AOP toelaat ? Hoe vergelijk je dat dan met Spring, dat geen taal is ?
Ik zie ook dat je een AOP framework 'Aspect#' hebt voor .NET.

Die meuk is waarschijnlijk allemaal best interessant, maar veel documentatie is er toch niet over te vinden.

https://fgheysels.github.io/


  • Feyd-Rautha
  • Registratie: November 2001
  • Laatst online: 02-08-2025
Spring biedt ook de mogelijkheid om Aspecten in uw applicatie te definieren, net zoals je dat ook met JBoss en AspectWerkz kan doen. Wat NKalore precies doet weet ik niet want ik heb gewoon eens vlug gegoogled en ik kwam die naam tegen. Maar aangezien het een taal-extensie is, moet het zoiets zijn als AspectJ. Daarin kun je dus werkelijk aspecten in schrijven (net zoals je classes kunt schrijven).
Die frameworks bieden normaal geen nieuwe syntax aan. In Spring kun je aspecten mbhv XML-files definieren en het framework doet dan wel de weaving.

voorbeeld van hoe het in Java zou kunnen gebeuren:

Java:
1
2
3
4
5
public abstract class WinampPage {
  protected WinampPage() {
    // allerlei dingen, behalve die page in de collection steken
  }
}


Java:
1
2
3
4
5
public class AnotherPage extends WinampPage {
  public AnotherPage() {
    // allerlei dingen, behalve die page in de collection steken
  }
}


AspectJ:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
aspect putPagesInCollection {

  // hier gaan we een Collection in de classe WinampPage toevoegen.
  // -> intertype member declaration
  private Collection WinampPage.pageCollection = new Collection();

  // alle executions van de public constructors opvangen van de classe WinampPage en alle subclasses (+). 
  pointcut pageConstruction() : execution(public WinampPage+.new());

  // na de execution van die constructors, gaan we de volgende code uitvoeren
  // via 'thisJoinPoint.getThis' kunnen we het object waarop het aspect van toepassing is opvragen.
  after() pageConstruction() {
    pageCollection.add(thisJoinPoint.getThis());
  }
}  


Ik heb ook eens geprobeerd om dan die nieuw geintroduceerde variabele te gebruiken in de andere klassen van de applicatie en dat werkt dus ook (in AspectJ). Dus die Collection kun je dan nog verder in de applicatie gebruiken (zolang je het aspect weaved tenminste ).

[ Voor 15% gewijzigd door Feyd-Rautha op 30-08-2006 21:50 ]

I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. Where the fear has gone there will be nothing. Only I will remain.


  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
Als ik het goed snap, ga je dus mbhv zo'n aspect je class 'WinampPage' gaan extenden ?
En zo'n pointcut is eigenlijk een soort van 'interceptie' die moet plaatsvinden bij een bepaalde actie ? Hier na het uitvoeren van de constructor , waar je de instance aan de collection gaat toevoegen ?

En wat bedoel je precies met 'het aspect weaven' ?

https://fgheysels.github.io/


  • Feyd-Rautha
  • Registratie: November 2001
  • Laatst online: 02-08-2025
Inderdaad, met aspecten ge je de crosscutting concerns uit je base-code halen en die in een aspect zetten. Je gaat dus een nieuw soort abstractie maken: een aspect.
En zo'n pointcut is eigenlijk een soort van 'interceptie' die moet plaatsvinden bij een bepaalde actie ? Hier na het uitvoeren van de constructor , waar je de instance aan de collection gaat toevoegen ?
Met de pointcut (een verzameling van joinpoints) kun je op een bepaalde plaats in jouw program-flow een interceptie doen (bv: uitvoeren van een method/constructor). Voor, na of rond die plaats kan je dan extra code invullen (een before/after/around advice). Je kan in plaats van code toevoegen aan voorgedefinieerde pointcuts, ook member-variabelen toevoegen. Je kan bijvoorbeeld ook met behulp van een aspect een klasse laten overerven van een andere klasse of een interface laten implementeren.


Een weaver zal je base-code en het aspect in elkaar weven zodat de base-code terug wordt aangevuld met de crosscutting code die in het aspect geschreven is.

[ Voor 52% gewijzigd door Feyd-Rautha op 30-08-2006 22:24 ]

I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. Where the fear has gone there will be nothing. Only I will remain.


  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
Maar, nu nog iets wat ik nog niet goed snap.

In het geval van de Topicstarter is het dus duidelijk dat hij later iets met die collectie wil doen. Hoe kan je die collectie -die dus een member is van je aspect- nu ergens anders gaan gebruiken ?

https://fgheysels.github.io/


  • Feyd-Rautha
  • Registratie: November 2001
  • Laatst online: 02-08-2025
Het aspect voegt die member-variabele toe in een bepaalde klasse.

Ik heb het eens uitgetest in AspectJ en het is mogelijk om in de gewone programmacode dan die member-variabele ook te gebruiken. De IDE heeft wel aan dat die variabele nog niet gedefinieerd is, maar tijdens het weaven wordt die variabele toch in uw klasse gedefinieerd. Het weaven gebeurt voor het runnen, dus is er geen probleem en het programma runt zoals het moet runnen.

In je aspect moet je dan nog een method introduceren (net zoals je member-variabelen kunt introduceren). Die method zou je dus nu ook gewoon in je base-code moeten kunnen gebruiken.

code:
1
2
3
public Collection WinampPage.getPageCollection() {
  return pageCollection;
}


Wanneer je dat aspect niet zou gebruiken/weaven, dan krijg je natuurlijk wel een compile-error aangezien je dan wel naar variabelen/methods refereert die niet in de klasse zitten.

[ Voor 24% gewijzigd door Feyd-Rautha op 30-08-2006 22:32 ]

I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. Where the fear has gone there will be nothing. Only I will remain.


  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
Nu, AOP zal zeker z'n nut hebben, maar ik vraag me toch af of het in dit concrete geval van de topicstarter niet handiger en makkelijker is om gewoon de collection als argument in de constructor te passen, en het object zelf in de abstract class aan de collection toe te voegen.

https://fgheysels.github.io/


  • NicoD
  • Registratie: Augustus 2004
  • Laatst online: 19:10
Wat ik wilde aantonen is dat de waarde van de abstracte property wel beschikbaar is in de constructor van de abstracte class.
De compiler dwingt je dus voor iedere implementatie een naam of handle te verzinnen.

Je zou ook de collection een static member kunnen maken van de abstracte class, dan hoef je 'm maar één keer te zetten en hoef je 'm dus niet iedere keer aan de constructor mee te geven.

  • whoami
  • Registratie: December 2000
  • Laatst online: 20:35
NicoD schreef op donderdag 31 augustus 2006 @ 10:31:
Wat ik wilde aantonen is dat de waarde van de abstracte property wel beschikbaar is in de constructor van de abstracte class.
Da's toch logisch ? :?
Net zoals het mogelijk is om in je abstracte class een abstracte method aan te roepen. Da's nu eenmaal het nut van abstracte classes.
Je zou ook de collection een static member kunnen maken van de abstracte class, dan hoef je 'm maar één keer te zetten en hoef je 'm dus niet iedere keer aan de constructor mee te geven.
Mja, dit is wel beperkend natuurlijk.
Wat als bepaalde pages in een andere collectie moeten komen ?

statics moet je imho zo veel mogelijk vermijden, tenzij het echt nodig is natuurlijk.

[ Voor 9% gewijzigd door whoami op 31-08-2006 11:03 ]

https://fgheysels.github.io/


  • NicoD
  • Registratie: Augustus 2004
  • Laatst online: 19:10
Is wel logisch, maar het is altijd fijn om het ook even in het echt te zien gebeuren.
Je hebt idd gelijk dat als je meerdere collections aanmaakt een static member definieren niet echt handig is.
Static members vermijden is m.i. vaak verstandig maar soms zijn ze wel ontzettend handig.
Pagina: 1