Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Something similar to "using" that will create an object and call a method on it when done, but let me do what I want in between

I'm using Lidgren and for every new type of message I make, I end up writing the same kind of code. I'm creating an instance of NetOutgoingMessage, running various assignment calls on it, then sending it when I'm done. Creation and send are the same, so I want to write a wrapper to do this for me, but it's a sealed class and it's not IDisposable. What I'm doing is something like:

NetOutgoingMessage om = server.CreateMessage();

om.Write(messageType);
om.Write(data1);
om.Write(data2);

server.SendMessage(om, server.Connections, NetDeliveryMethod.UnreliableSequenced, 0);

And I want to do something like:

using(AutoNetOutgoingMessage om(server, messageType, NetDeliveryMethod.UnreliableSequenced))
{
    om.Write(data1);
    om.Write(data2);
}

Obviously I can't do using so is there another common way if implementing functionality like this? I'm not looking for an incredibly complicated solution, as this is just about maintainability for me, so I don't have a problem repeating my code for every message. But I am curious if there's a fun C# trick I don't know about for this.

like image 419
Dan B Avatar asked Sep 05 '15 22:09

Dan B


2 Answers

Yes, you can easily enforce this sort of temporal relationship between some calls by receiving a delegate that contains the custom actions and invoking that delegate exactly when you want.

Here's an example:

void MyMethod(Server server, Action<NetOutgoingMessage> action)
{
    NetOutgoingMessage om = server.CreateMessage();
    action(om);
    server.SendMessage(om, server.Connections, NetDeliveryMethod.UnreliableSequenced, 0);     
}

Call it like this:

MyMethod(server, om => {
    om.Write(data1);
    om.Write(data2);
});

Essentially, this is a form of inversion of control: the practice by which a general piece of code calls into an externally provided, specialized piece of code, at a specified point.

Typically, this is achieved using (often abstract) classes or interfaces - and making virtual calls through them. Delegates and lambdas simply allow you to express the same thing more concisely, while the compiler does the boring work (like declaring the new class, capturing required variables) behind the scenes.

like image 180
Theodoros Chatzigiannakis Avatar answered Oct 23 '22 10:10

Theodoros Chatzigiannakis


Create a wrapper around the NetOutgoingMessage class that implements IDisposable.

public class AutoMessage : IDisposable
{
    private const NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced;

    public NetPeer Peer { get; private set; }

    public List<NetConnection> Recipients { get; private set; } 

    public NetOutgoingMessage Message { get; private set; }

    public AutoMessage(NetPeer peer, List<NetConnection> recipients)
    {
        Peer = peer;
        Recipients = recipients;
        Message = peer.CreateMessage();
    }

    public void Dispose()
    {
        Peer.SendMessage(Message, Recipients, method, 0);
    }
}

You can then use it through using:

var data = 123;
using (var msg = new AutoMessage(Client, Client.Connections))
{
    msg.Message.Write(data);
}

Another option is to create an interface IMessage that has method definitions for Encode and Decode, and allow other classes to implement them. Then create a Send method that will write the message type and run the Encode method. This is what I do with my own applications and it works great.

like image 33
Cyral Avatar answered Oct 23 '22 10:10

Cyral