Toon posts:

[C#] Casten tijdens runtime

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik zit met het volgende probleem:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Queue
{
    public void Send(IMessage message)
    {
        Type t = message.GetType();
        Send((t)message); //dit werkt niet...
    }
    
    private void Send(EmailMessage message)
    {
        //logic for sending emailmessage (heeft de interface IMessage)
    }
    
    private void Send(SmsMessage message)
    {
        //logc for sending smsmessage (heeft de interface IMessage)
    }
}


Hoe cast ik een object tijdens runtime? Heb flink gezocht op google en hier (ook gekeken bij Reflection) maar kan geen antwoord hierop vinden. Kan dit uberhaubt?

Of hebben jullie andere elegantere oplossingen hiervoor?

  • Invisible_man
  • Registratie: Juni 2006
  • Laatst online: 11:27
Kan je niet gewoon met if-statements kijken wat voor type het is en vervolgens de bijbehorende overload aanroepen?

[ Voor 4% gewijzigd door Invisible_man op 14-06-2006 19:54 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 10-12-2025
Het probleem is natuurlijk niet het casten at runtime. Het probleem is dat je de overload resloutie at runtime wil doen. De correcte OO manier om dit te doen is om IMessage zelf een Send method te geven.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 14-02 12:54
Alledrie de functies public maken? Wordt automatisch de goeie overload aangeroepen :)

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Verwijderd

Dat doe je alsvolgt:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Queue 
{ 
    public void Send(IMessage message) 
    { 
    if (message is EmailMessage) Send( (EmailMessage)message );
    if (message is SmsMessage) Send( (SmsMessage)message );
    } 
     
    private void Send(EmailMessage message) 
    { 
        //logic for sending emailmessage (heeft de interface IMessage) 
    } 
     
    private void Send(SmsMessage message) 
    { 
        //logc for sending smsmessage (heeft de interface IMessage) 
    } 
}


Groetjes Robbert

  • whoami
  • Registratie: December 2000
  • Laatst online: 19-02 23:53
Wat MSalters zegt is waar; als je nu een interface IMessage hebt, en je EmailMessage en SmsMessage hebben implementeren die interface, zorg dan dat de Send method een member is van de interface.
Op die manier kan je gebruik maken van polymorphisme:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface IMessage
{
     public void Send();
}

public class SmsMessage : IMessage
{
     public void Send()
     {
           // logic to send sms
     }
}

public class EmailMessage : IMessage
{
      public void Send()
      { 
           // logic to send email
      }
}


code:
1
2
3
4
5
IMessage m = new SmsMessage();
IMessage m2 = new EmailMessage();

m.Send();
m2.Send();


Je queue kan dan message accepteren, en ervoor zorgen dat de Send() method van de messages uitgevoerd wordt:
code:
1
2
3
4
5
6
7
8
public class Queue
{
     public void AcceptMessage( IMessage m )
     {
           // Do other stuff here if necessary
           m.Send();
     }
}

https://fgheysels.github.io/


  • kenneth
  • Registratie: September 2001
  • Niet online

kenneth

achter de duinen

Alsjeblieft niet! :)

Dat betekent dat bij iedere type Message die je ooit gaat toevoegen, je de hele code in de Send-method mag gaan aanpassen. De manier die whoami demonstreert is veel netter en flexibeler.

Look, runners deal in discomfort. After you get past a certain point, that’s all there really is. There is no finesse here.


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Behalve de superieure interface oplossing, kun je ook nog wat knoeien met IConvertable en Convert.ChangeType. Maar ik zou voor de interface oplossing gaan.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Verwijderd

Topicstarter
whoami schreef op woensdag 14 juni 2006 @ 21:35:
Wat MSalters zegt is waar; als je nu een interface IMessage hebt, en je EmailMessage en SmsMessage hebben implementeren die interface, zorg dan dat de Send method een member is van de interface.
Ik heb hier een gemengd gevoel bij, aangezien:
1. de interface nu een Send-methode heeft die niet direct aangeroepen kan worden (heeft namelijk gegevens van de Queue nodig), maar wel in de publieke interface zit.
2. de logica om een IMessage naar een Queue te sturen zit nu in de Message zelf terwijl deze logica, zoals ik het zie, in de Queue hoort te zitten. Verschillende varianten van IMessage gebruiken namlijk dezelfde Send-logica.
3. De IMessage kan ook op andere manieren/queues verstuurd worden. De EmailMessage kan bijvoorbeeld ook worden verstuurd door de SmtpClient.

Aan de andere kant hoef ik alleen IMessage te implementeren bij een nieuwe variant, ipv van code op meedere plekken. Echter van de Queue zijn ook drie varianten met hun eigen logica, die zou ik dan ook in de nieuwe variant van IMessage moeten plaatsen. Komt ook nog eens bij dat niet elke queue alle type IMessage's accepteert (dacht er aan om dan een exception te throw-en)

Misschien is het duidelijk als ik laat zien hoe ik de code wil gaan gebruiken (maar als jullie hierop commentaar hebben, hoor ik dat graag):
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using (Transaction trx = Transaction.Begin())
{
    EmailMessage email = new EmailMessage();
    SmsMessage sms = new SmsMessage();

    int serviceId = 34;
    SimpleQueue q = new SimpleQueue(serviceId); //heeft interface IQueue
    q.Send(email, trx); //stuurt een email naar de queue
    q.Send(sms, trx); //stuurt een sms naar de queue

    tr.Commit();
}

//maar dit zou ook kunnen
EmailMessage email = new EmailMessage();
SmtpClient client = new SmtpClient();
client.Send(email);
Mijn idee was dat de logica voor Send() in de Queue thuishoort. Deze Send-logic wil ik dan waarschijnlijk dmv State-pattern implementeren, aangezien dit het enige is wat variabel is (maar wel gegevens van de desbetreffende Queue nodig heeft).

Ik had het via if-statements voorlopig opgelost (zoals hierboven gepost), maar ik dacht dat het misschien 'eleganter' cq generieker kan.
EfBe schreef op woensdag 14 juni 2006 @ 22:17:
Behalve de superieure interface oplossing, kun je ook nog wat knoeien met IConvertable en Convert.ChangeType. Maar ik zou voor de interface oplossing gaan.
Maar IConvertable convert toch alleen maar (heb hem nog nooit gebruikt)? Kan je IConvertable ook gebruiken om te casten?

[ Voor 5% gewijzigd door Verwijderd op 14-06-2006 23:07 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 19-02 23:53
Als je de send functionaliteit niet in de Message zelf wilt hebben, ga je die functionaliteit dan niet dupliceren in je Queue en in je SmtpClient class ?
Ik snap ook het commentaar op regel 8 niet goed; send een message naar de queue ? Is het niet de Queue die een message moet zenden naar een ontvanger ?

Als je de Send() method van de Message zelf niet zichtbaar wilt hebben, is het dan geen beter idee om ipv een Interface een abstract class te maken die een internal method Send heeft, die abstract is, en je dan dus overrided in de concrete implementaties ?
Doordat die method dan internal is, is ze enkel zichtbaar binnen de assembly.

Welke informatie heeft zo'n Message trouwens nog nodig van de Queue ? En geldt dit ook voor de SmtpClient class ? En heeft een SmsMessage dezelfde info nodig als een MailMessage ?

https://fgheysels.github.io/


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 18:45

.oisyn

Moderator Devschuur®

Demotivational Speaker

En als je het versturen van messages niet in die afgeleide message classes zelf wilt hebben zou je natuurlijk ook het visitor design pattern kunnen overwegen.

C#:
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
interface IMessageVisitor
{
    public void Visit(EmailMessage message);
    public void Visit(SmsMessage message);
}

interface IMessage
{
    // ...
    public void Accept(IMessageVisitor);
}

class SmsMessage : IMessage
{
    // ...
    public void Accept(IMessageVisitor visitor) { visitor.Visit(this); }
}

class EmailMessage : IMessage
{
    // ...
    public void Accept(IMessageVisitor visitor) { visitor.Visit(this); }
}

class Queue : IMessageVisitor
{
    public void Send(IMessage message)
    {
        message.Accept(this);
    }

    public void Visit(SmsMessage message)
    {
        // ...
    }

    public void Visit(EmailMessage message)
    {
        // ...
    }
}

Zo ongeveer, m'n C# is niet zo heel sterk :). Misschien wil je Queue niet publiekelijk van IMessageVisitor laten inheriten, dat is voor buitenstaanders namelijk niet erg interessant (of zij moeten gewoon de Accept op message aanroepen ipv de Send op de Queue, en dan de Queue meegeven). Het nadeel is wel dat je voor elk nieuw message-type weer de interface aan moet passen (en de queue natuurlijk, maar dat moest je toch al).

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
whoami schreef op woensdag 14 juni 2006 @ 23:06:
Als je de send functionaliteit niet in de Message zelf wilt hebben, ga je die functionaliteit dan niet dupliceren in je Queue en in je SmtpClient class ?
Misschien dat het nog niet duidelijk was, maar SmtpClient is een class die in het .NET Framework zit. Ik kan deze gebruiken door dat ik EmailMessage laat extenden van MailMessage (ook in het .NET Framework). Dit voordeeltje is handig, maar op zich niet zo belangrijk (had hem misschien niet als voorbeeld erbij moeten zetten).
whoami schreef op woensdag 14 juni 2006 @ 23:06:
Ik snap ook het commentaar op regel 8 niet goed; send een message naar de queue ? Is het niet de Queue die een message moet zenden naar een ontvanger ?
Nee, Send() stuurt een Message naar de queue. De naam is misschien niet helemaal duidelijk (had ik ook in het begin), maar ik heb de implementatie van MSMQ in .NET aangehouden. Daar heet het ook Send() en ik heb dit doorgetrokken naar mijn code, aangezien het idee hetzelfde is.
whoami schreef op woensdag 14 juni 2006 @ 23:06:
Als je de Send() method van de Message zelf niet zichtbaar wilt hebben, is het dan geen beter idee om ipv een Interface een abstract class te maken die een internal method Send heeft, die abstract is, en je dan dus overrided in de concrete implementaties ?
Doordat die method dan internal is, is ze enkel zichtbaar binnen de assembly.
Dacht ik ook net aan, nadat ik poste :)
whoami schreef op woensdag 14 juni 2006 @ 23:06:
Welke informatie heeft zo'n Message trouwens nog nodig van de Queue ? En geldt dit ook voor de SmtpClient class ? En heeft een SmsMessage dezelfde info nodig als een MailMessage ?
Ik ben een abstractie van ons huidig (verouderd) systeem aan het maken, die veel overeenkomsten vertoond met BizTalk Server cq Service Broker in SQL Server 2005. Misschien kopen we BizTalk Server, maar die is vrij prijzig (migratietraject niet eens meegerekend).

De Message met vooral weten hoe deze zichzelf moet serializen en heeft geen informatie nodig van de queue. De Queue serialized de Message en stopt deze in de Queue met wat extra data (zoals wie gefactureed moet worden hiervoor). Doordat het een veroudert systeem is, zijn er enkele uitzonderingen/hacks voor bepaalde types Messages, waarvoor een aangepaste Send-logica nodig is. Echter voor de meeste Messages is de Send-logica hetzelfde (maar die verschilt per type Queue).

Even in cijfers: Er zijn 3 typen Queues, die hun eigen Send-logica hebben. Per Queue zijn er één tot drie verschillende implementaties van Send() zijn, die afhangen van het type Message. Er zijn nu 12 typen Messages, maar niet alle queues kunnen alle 12 verschillende types verwerken. (Één queue kan bijv. maar 8 types Message aan).

Mijn eerste code-voorbeeld is wilicht niet helemaal duidelijk, maar hopelijk heb ik het nu iets duidelijker kunnen maken. In ieder geval bedankt voor de antwoorden die ik tot nu toe heb gekregen :).

  • Vedett.
  • Registratie: November 2005
  • Laatst online: 19-02 19:21
Ik zou geen x-aantal send methoden in de que zetten. Maar elk type algoritme in een aparte classe die dan bijvoorbeeld een ISendBehaviour interface implementeert.
Volledig volgens het Strategy design pattern.

Op deze manier hoeft zowel IMessage als IQue zich niet bezig te houden met de implementatie van Send, maar zit deze dus apart in ISendBehaviour

link: http://www.dofactory.com/Patterns/PatternStrategy.aspx

[ Voor 9% gewijzigd door Vedett. op 15-06-2006 09:13 ]


Verwijderd

Met reflection lukt het wel.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    class Queue
    {
        public void Send(IMessage message)
        {
            MethodInfo method = 
                this.GetType().GetMethod("Send", new Type[] { message.GetType() });

            method.Invoke(this, new object[] { message });
        }

        private void Send(EmailMessage message)
        {
            //logic for sending emailmessage (heeft de interface IMessage)
        }

        private void Send(SmsMessage message)
        {
            //logc for sending smsmessage (heeft de interface IMessage)
        }
    }

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 10-12-2025
Verwijderd schreef op woensdag 14 juni 2006 @ 22:59:
[...]
Ik heb hier een gemengd gevoel bij, aangezien:
1. de interface nu een Send-methode heeft die niet direct aangeroepen kan worden (heeft namelijk gegevens van de Queue nodig), maar wel in de publieke interface zit.
Dat is niet heel erg. Messages moeten namelijk niet proberen zelf te bepalen waar ze naar toe verzonden worden. De eigenaar van Queue en Message kan de link als enige leggen, en die hoeft de link pas te leggen als de message daadwerkelijk wordt verzonden.
2. de logica om een IMessage naar een Queue te sturen zit nu in de Message zelf terwijl deze logica, zoals ik het zie, in de Queue hoort te zitten. Verschillende varianten van IMessage gebruiken namlijk dezelfde Send-logica.
Nee. Dat heb je namelijk al laten zien, toe je een andere Send liet zien voor EmailMessage, en dat IS-A IMessage.
Aan de andere kant hoef ik alleen IMessage te implementeren bij een nieuwe variant, ipv van code op meedere plekken. Echter van de Queue zijn ook drie varianten met hun eigen logica, die zou ik dan ook in de nieuwe variant van IMessage moeten plaatsen.
Nee, je maakt een IQueue. Elke implementatie van IMessage gebruikt alleen de IQueue interface, en andersom maakt elke implementatir van IQueue alleen gebruik van de IMessage interface. Dat is de enige manier om een O(N*M) complexiteit te reduceren tot complexiteit O(N+M)

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 15:38
Zou toch ook even kijken naar de tip van Vedett.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1