Related
I know that multiple inheritance in C# is only allowed by using Interfaces and that there are very valid reasons why multiple inheritance can quickly become a real headache. (Working in .NET Framework if that makes any difference to the answers)
However.
In working on various projects accross many classes I find myself returning to the same patterns to handle behaviour.
For example I have an Interface IXMLSavable which requires the functions GetXML() and SetFromXML(XElement e) to be implemented. The way I implement this in every class is, that I write different functions for different versions of the XML (If I changed something in the GetXML() I want to maintain backwards compatibility...). And according to a version-attribute on the root Element I switch case to the right ReadVersionX(XElement e) function so all my data stays consitent.
Another example would be centered around eventing. If for example I want to implement a "stop firing events for the time being"-Lock I would go about thusly:
private bool suppressEvents;
public bool SuppressEvents
{
get { return suppressEvents; }
set
{
bool prevValue=SuppressEvents;
suppressEvents=value;
if(prevValue!=SuppressEvents && !SuppressEvents) TheChangeEvent?.Invoke();
}
}
So I can run multiple operations on the object in question without it giving of a right old firework display of events. Again: This code will be almost unchanged for a lot of classes.
For the XML one I could refactor this to a class that has a Dictionary<int,delegate> ReadFunctions which I could then set in every implementation (I concede that there needs to be a bit of customisation in the "implementing" class) and reduce the amount of bolierplate for every class (the explicit switching on the version attribute) to just filling this dictionary.
The eventing one could go into a class on its own quite readily, I would probably only need to hook up the right event to the invokation, but that could easily be remedied by an abstract function I will have to implement (again: customisation still necessary but much less boilerplate).
Each "now-class-was-interface" on its own would make a splendid base class for any object. I could use functionality down an inheritance tree and customise it by overwriting functionality with new if I would need it.
The problem starts when I want to combine the two now-classes together. Due to the limitation in C# (which, again, is there for a reason) I cannot inherit from both above described classes at the same time. This would only be possible if I have one of these classes inherit from the other. Which would be possible, but would lead to a whole lot of a different headache when I want one functionality but not the other. Or the other functionality and not the one. The way around that would be to create a plethora of permutation classes (so one class for each combination of the functionaities). And while that would solve the problem it would probably be a nightmare to maintain.
So the real question is: Is there a way to correctly plug in different already implemented functionality into a class in an inheritance like manner that allows the addition of multiple distinct functionality packages as opposed to interfaces that cannot by the very nature of themselves provide any concrete implementation.
In many cases you can avoid inheritance with the use of interfaces/default interface methods/extension methods, decorators, or some other pattern.
In your case with xml you could simply change your interface to have one ReadMethod per version, and use a extension method to select the correct one
public interface IXMLReadable{
void ReadVersion1(XElement e);
void ReadVersion2(XElement e);
}
public static class IXMLReadableExtensions {
public static void Read(this IXMLReadable self, XElement e){
// Read version from xml, call ReadVersion1 or ReadVersion2
}
}
default interface methods would do more or less the same thing, with the added advantage of allowing the class to override the Read-method if it wants some other behavior.
However, my preferred solution would be to instead convert your object to a Data Transfer Object (DTO), add any required serialization attributes to this object, and use a library to serialize this. Added fields etc can usually be accommodated by just marking it as optional. Larger changes can usually be done by creating a new DTO class.
One way to solve your event problem could be to move this logic to a separate class
public class SuppressibleEvent
{
private bool suppressEvents;
private bool pendingEvent;
public void Raise()
{
if (!suppressEvents)
{
TheChangeEvent?.Invoke(this, EventArgs.Empty);
}
else
{
pendingEvent = true;
}
}
public event EventHandler TheChangeEvent;
public bool SuppressEvents
{
get => suppressEvents;
set
{
suppressEvents = value;
if (!suppressEvents && pendingEvent)
{
TheChangeEvent?.Invoke(this, EventArgs.Empty);
pendingEvent = false;
}
}
}
}
Optionally you may add a interface, so that only the owner can raise the event, but others can listen and register. You could also add methods/events to your class that just forwards to the actual implementation.
The overall point is that there is usually a better pattern to use than implementation inheritance. Some might require a bit more code, but usually gain a bit of flexibility as a result.
I hope this question is not to closely related to others but others don't seem to fill the gap in knowledge.
This seems to be hot topic to try and understand Events and Delegates and after reading many SO questions and MSDN articles I'm afraid to say I still don't understand. After some years creating great web applications, I found myself getting extremely frustrated in not understanding them. Please can anyone clarify this in common code. So the question is, why would you use events and delegates over calling a method?
Below is some basic code I written at work. Would I be able to leverage events and delegates?
Public Class Email
{
public string To {get;set;}
//Omitted code
public void Send()
{
//Omitted code that sends.
}
}
Public Class SomeClass
{
//Some props
Public void DoWork()
{
//Code that does some magic
//Now Send Email
Email newEmail = new Email();
newEmail.To = "me#me.com";
newEmail.Send();
}
}
This is probably not the best example but is there anyway that the DoWork() method can subscribe to the Email? Would this work? Any help for me to truly understand events and delegates would be greatly appreciated.
Regards,
The biggest reason I have found in real-world programming for the use of events and delegates is easing the task of code maintenance and encouraging code reuse.
When one class calls methods in another class, those classes are "tightly coupled". The more classes you have tightly coupled, the more difficult it becomes to make a change to one of them without having to also change several others. You may as well have written one big class at that point.
Using events instead makes things more "loosely coupled" and makes it much easier to change one class without having to disturb others.
Taking your example above, suppose we had a third class, Logger, that should log when an email is sent. It uses a method, LogEvent(string desc, DateTime time), to write an entry to the log:
public class Logger
{
...
public void LogEvent(string desc, DateTime time)
{
...//some sort of logging happens here
}
}
If we use methods, we need to update your Email class' Send method to instantiate a Logger and call its LogEvent method:
public void Send()
{
//Omitted code that sends.
var logger = new Logger();
logger.LogEvent("Sent message", DateTime.Now);
}
Now Email is tightly coupled to Logger. If we change the signature of that LogEvent method in Logger, we will also have to make changes to Email. Do you see how this can quickly become a nightmare when you are dealing with even a medium-sized project? Furthermore, no one wants to even try to use the LogEvent method because they know that if they need to make any sort of change to it, they will have to start changing other classes, and what should have been an afternoon's worth of work quickly turns into a week. So instead, they write a new method, or a new class, that then becomes tightly coupled to whatever else they are doing, things get bloated, and each programmer starts to get into their own little "ghetto" of their own code. This is very, very bad when you have to come in later and figure out what the hell the program is doing or hunt down a bug.
If you put some events on your Email class instead, you can loosely couple these classes:
Public Class Email
{
public event EventHandler<EventArgs> Sent;
private void OnSent(EventArgs e)
{
if (Sent!= null)
Sent(this, e);
}
public string To {get;set;}
//Omitted code
public void Send()
{
//Omitted code that sends.
OnSent(new EventArgs());//raise the event
}
}
Now you can add an event handler to Logger and subcribe it to the Email.Sent event from just about anywhere in your application and have it do what it needs to do:
public class Logger
{
...
public void Email_OnSent(object sender, EventArgs e)
{
LogEvent("Message Sent", DateTime.Now);
}
public void LogEvent(string desc, DateTime time)
{
...//some sort of logging happens here
}
}
and elsewhere:
var logger = new Logger();
var email = new Email();
email.Sent += logger.Email_OnSent;//subscribe to the event
Now your classes are very loosely coupled, and six months down the road, when you decide that you want your Logger to capture more or different information, or even do something totally different when an email is sent, you can change the LogEvent method or the event handler without having to touch the Email class. Furthermore, other classes can also subscribe to the event without having to alter the Email class, and you can have a whole host of things happen when an email is sent.
Now maintaining your code is much easier, and other people are much more likely to reuse your code, because they know they won't have to go digging through the guts of 20 different classes just to make a change to how something is handled.
BIG EDIT: More about delegates. If you read through here: Curiosity is Bliss: C# Events vs Delegates (I'll keep links to a minimum, I promise), you see how the author gets into the fact that events are basically special types of delegates. They expect a certain method signature (i.e. (object sender, EventArgs e)), and can have more than one method added to them (+=) to be executed when the method is raised. There are other differences as well, but these are the main ones you will notice. So what good is a delegate?
Imagine you wanted to give the client of your Email class some options for how to send the mail. You could define a series of methods for this:
Public Class Email
{
public string To {get;set;}
//Omitted code
public void Send(MailMethod method)
{
switch(method)
{
case MailMethod.Imap:
ViaImap();
break;
case MailMethod.Pop:
ViaPop();
break;
}
}
private void ViaImap() {...}
private void ViaPop() {...}
}
This works well, but if you want to add more options later, you have to edit your class (as well as the MailMethod enum that is assumed here). If you declare a delegate instead, you can defer this sort of decision to the client and make your class much more flexible:
Public Class Email
{
public Email()
{
Method = ViaPop;//declare the default method on instantiation
}
//define the delegate
public delegate void SendMailMethod(string title, string message);
//declare a variable of type SendMailMethod
public SendMailMethod Method;
public string To {get;set;}
//Omitted code
public void Send()
{
//assume title and message strings have been determined already
Method(title, message);
}
public void SetToPop()
{
this.Method = ViaPop;
}
public void SetToImap()
{
this.Method = ViaImap;
}
//You can write some default methods that you forsee being needed
private void ViaImap(string title, string message) {...}
private void ViaPop(string title, string message) {...}
}
Now a client can use your class with its own methods or provide their own method to send mail just about however they choose:
var regularEmail = new Email();
regularEmail.SetToImap();
regularEmail.Send();
var reallySlowEmail = new Email();
reallySlowEmail.Method = ViaSnailMail;
public void ViaSnailMail(string title, string message) {...}
Now your classes are somewhat less tightly coupled and much easier to maintain (and write tests for!). There are certainly other ways to use delegates, and lambdas sort of take things up a notch, but this should suffice for a bare-bones introduction.
Ok, I understand this answer wont strictly speaking be correct, but I'll tell you how I came to understand them.
All functions have a memory address, and some functions are simple get/set for data. It helps to think of all variables as being functions with only two methods - get and set. You're quite comfortable passing variables by reference, which means (simplistically) you are passing a pointer to their memory, which enables some other code to call their get/set methods, implicitly by using "=" and "==".
Now translate that concept to functions and code. Some code and functions have names (like variable names) which you give them. you are used to executing those functions by calling their name; but the name is just a synonym for their memory location (simplistically). By calling the function you are de-referencing its memory address using its name, and then calling the method which lives at that memory address.
As I said, this is all very simplistic and in various ways, incorrect. But it helps me.
So - is it possible to pass the memory address of a function but not call it ? In the same way you pass a reference to a variable without evaluating it ? I.e. what is the equivalent of calling
DoSomeFunction(ref variablePointer)
Well, the reference to a function is called a delegate. But because a function can also take parameters (which a variable cannot) you need to use a calling syntax more elaborate than just ref. You set up the call you want to make into a delegate structure and pass that delegate stucture to the recipient, who can either immediately evaluate (call) that delegate, or store it for later use.
Its the "store for later use" that is the key to understanding event handlers. The special (and somewhat confusing) syntax around event handlers is just another way of setting up a function pointer (delegate) and add it to a list of function pointers that the recipient class can evaluate at some convenient time.
A simple way of looking at event handlers would be;
class myClass
{
public List<delegate> eventHandlers = new List<delegate>();
public void someMethod()
{
//... do some work
//... then call the events
foreach(delegate d in eventHandlers)
{
// we have no idea what the method name is that the delegate
// points to, but we dont need to know - the pointer to the
// function is stored as a delegate, so we just execute the
// delegate, which is a synonym for the function.
d();
}
}
}
public class Program()
{
public static void Main()
{
myClass class1 = new myClass();
// 'longhand' version of setting up a delegate callback
class1.eventHandlers.Add(new delegate(eventHandlerFunction));
// This call will cause the eventHandlerFunction below to be
// called
class1.someMethod();
// 'shorthand' way of setting up a delegate callback
class1.eventHandlers.Add(() => eventHandlerFunction());
}
public static eventHandlerFunction()
{
Console.WriteLine("I have been called");
}
It gets slightly more complicated when you want the caller of the delegate to pass in some values to the function, but otherwise all delegate concepts are the same as the concepts of "ref" variables - they are references to code which will be executed at a later date, and typically you pass them as callbacks to other classes, who will decide when and whether to execute them. In earler languages delegates were pretty much the same as "function pointers" or (in beloved long departed Nantucket Clipper) "Code blocks". Its all much more complex than simply passing around a memory address of a block of code, but if you hang on to that concept, you wont go far wrong.
Hope that helps.
The simplest way of thinking about the use of delegates is to think in terms of when you want to call a method, but you don't not yet know which one (or ones).
Take a control's Click event handler, for example. It uses the EventHandler delegate. The signature is void EventHandler(object sender, EventArgs e);. What this delegate is there for is that when someone clicks the control I want to be able to call zero or more methods that have the EventHandler signature, but I don't know what they are yet. This delegate lets me effectively call unknown future methods.
Another example is LINQ's .Select(...) operator. It has the signature IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector). This method contains a delegate Func<TSource, TResult> selector. What this method does is takes a sequence of values from source and applies an as yet unknown projection to produce a sequence of TResult.
Finally, another good example is Lazy<T>. This object has a constructor with this signature: public Lazy(Func<T> valueFactory). The job of Lazy<T> is to delay the instantiate of T until the first time it is used, but then to retain that value for all future uses. It presumably is a costly instantiation that would be ideal to avoid if we don't need the object, but if we need it more than one we don't want to be hit with the cost. Lazy<T> handles all the thread locking, etc, to make sure that only one instance of T is created. But the value of T returned by Func<T> valueFactory can be anything - the creators of Lazy<T> has no idea what the delegate will be, and nor should they.
This, to me, is the most fudamentally important thing to understand about delegates.
why would you use events and delegates over calling a method?
In the context of the example you posted, if you wanted to send emails asynchronously, you would have to implement a notification mechanism.
See the SmtpClient implementation for an example: https://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.sendcompleted%28v=vs.110%29.aspx
If more of an explanation is required, rather than code examples, I'll try to explain why you would use a Delegate or Event in the example you gave above.
Imagine that you wish to know if the email was sent or not after you called Email.Send().
In the Email Class you would have two events - one for a failed send, and one for a successful send.
When the Email Class sends without an error, it would look to see if there are any subscribers to the 'SuccessfulSend()' event, and if there are, it raises that event. This would then notify the subscribers that wanted to be informed if the send was successful so that they can perform some other task.
So you could have an event handler that is notified of the successful send, and in this handler, you could call another method (DoMoreWork()).
If the Email.Send() failed, you could be notified of this, and call another method that logs the failure for later reference.
With regards to Delegates, if there were three different Email Classes that used different functionality (or servers) to send mail, the client calling the Email.Send() method, could supply the relevant Email Class to use when sending the email.
The Email Class would use the IEmail interface, and the three Email Classes would implement IEmail (To, From, Subject, Body, Attachments, HTMLBody etc.), but could perform the interactions/rules in different ways.
One could require a Subject, another require an Attachment, one could use CDONTS, another use a different protocol.
The client could determine if it needs to use CDONTS depending on where it is installed, or it could be in an area of the app where an attachment is required, or would format the body in HTML.
What this does is to remove the burden of logic from the client and all of the places where these checks and logic should be checked, and move it into the single versions of the relevant Class.
Then the client simply calls the Email.Send() after providing the correct object to use in its constructor (or by using a settable property).
If a fix or change to a particular email object's code is required - it is carried out in one place rather than finding all areas in the client and updating there.
Imagine if your Email Class was used by several different applications...
I have a game with many classes that need to listen to events. But under certain circumstances, those classes are destroyed or disabled. When that happens, I need to remove their listening methods from the events manager delegate table.
I don't want to modify the EventsManager and I would like to each class that adds any events to it to know which events it added.
I'm currently using something like this do add and remove the events in each class:
void AddEventsListeners() {
EventsManager.AddListener<OnClickDown>(OnClickDownHandler);
EventsManager.AddListener<OnClickUp>(OnClickUpHandler);
EventsManager.AddListener<OnClick>(OnClickHandler);
}
void RemoveEventsListeners() {
EventsManager.RemoveListener<OnClickDown>(OnClickDownHandler);
EventsManager.RemoveListener<OnClickUp>(OnClickUpHandler);
EventsManager.RemoveListener<OnClick>(OnClickHandler);
}
Those OnClick are all derived from GameEventBase, and the OnClickHandler are methods declared as
void OnClickDown(OnClickHandler e) {}
to match the delegate that is used in the EventsManager, which is declared as
delegate void EventDelegate<T>(T e) where T : GameEventBase;
I want to be able to fill a special hash table named, say, events, that has keyvalue pairs like
<T, EventDelegate<T>> where T: GameEventBase
That is, I want to be able to do events.add(OnClick, OnClickHandler), where OnClickHandler is declared as
OnClickHandler(OnClick e) {}
And I want adding to fail if OnClickHandler where defined, for example, as
OnClickHandler(OtherGameEventBaseDerivedEvent e) {}
That requirement translates to me wanting type safety in that special dictionary.
One of my attempts involved not a dictionary, but a way to decide which method to call, between the AddListener and RemoveListener
I didn't like it because it introduces a parameter to the method and the code reads really weird with it. It does work, and does reduce the repetition, but is too ugly.
I create a AddOrRemoveAllListeners(AddOrRemove addOrRemove), which I populated with calls to AddOrRemoveListener for each event.
Now all I had to do is AddOrRemoveAllListeners(AddOrRemove.Remove) or AddOrRemoveAllListeners(AddOrRemove.Add), to add or remove my events.
enum AddOrRemove {
Remove,
Add
}
void AddOrRemoveListener<T>(EventsManager.EventDelegate<T> del, AddOrRemove addOrRemove)
where T : GameEventBase {
switch (addOrRemove) {
case AddOrRemove.Remove:
EvMan.RemoveListener<T>(del);
break;
case AddOrRemove.Add:
EvMan.AddListener<T>(del);
break;
}
}
Another attempt involved creating the type
class EventsDictionary<T> : Dictionary<T, EventsManager.EventDelegate<T>> where T : GameEventBase { }
And using it like this:
EventsDictionary<GameEventBase> events = new MyCustomDictionary<GameEventBase>();
void AddEventHandlerPairToEventsDictionary<T>(T e, EventsManager.EventDelegate<T> handler) where T : GameEventBase {
if (!events.ContainsKey(e)) {
events.Add(e, handler);
}
}
But the events.Add(e, handler) fails and forces me to declare the handler as
EventsManager.EventDelegate<GameEventBase>
instead of
EventsManager.EventDelegate<T>
If I do that, I could add keyvalue pairs that don't make sense in that events type, i.e., I lose the event handling type safety.
I want to have such a structure because I don't like all those repetitions. It would be really bad if someone forgot to remove an event in the RemoveEventsListeners().
Having such a dictionary, I could use a foreach loop to add/remove the handlers to the EventsManager, which would be really nice.
As for performance, this is for a game and it needs to have good performance. Those adding/removing of events can happen a lot (sometimes hundreds of times per frame) because a lot of objects are destroyed (can't leave null handlers in the EventsManager) or disabled (need to stop listening to everything until enabled again) all the time. This means reflection and lots of casting/boxing or anything that creates lots of garbage collected objects is out.
I'm, of course, open to suggestions as to other ways to approach this.
Thanks for your assistance!
I'm attaching the relevant parts of the EventsManager being used (The RemoveListener() is analogous to the AddListener). The GameEventBase is just an empty shell. It isn't a .NET event nor uses EventArgs.
public class EventsManager : ManagedBase {
public delegate void EventDelegate<T>(T e) where T : GameEventBase;
private delegate void EventDelegate(GameEventBase e);
private readonly Dictionary<Type, EventDelegate> delegates = new Dictionary<Type, EventDelegate>();
private readonly Dictionary<Delegate, EventDelegate> delegateLookup = new Dictionary<Delegate, EventDelegate>();
public void AddListener<T>(EventDelegate<T> del) where T : GameEventBase {
// Early-out if we've already registered this delegate
if (delegateLookup.ContainsKey(del)) {
return;
}
// Create a new non-generic delegate which calls our generic one.
// This is the delegate we actually invoke.
EventDelegate internalDelegate = (e) => del((T) e);
delegateLookup[del] = internalDelegate;
EventDelegate tempDel;
if (delegates.TryGetValue(typeof (T), out tempDel)) {
delegates[typeof (T)] = tempDel + internalDelegate;
}
else {
delegates[typeof (T)] = internalDelegate;
}
}
public void Raise(GameEventBase e) {
EventDelegate del;
if (delegates.TryGetValue(e.GetType(), out del)) {
del.Invoke(e);
}
}
}
Your problems seem to be solved if you use the EventAggregator pattern.
There is a short description of it by Martin Fowler
Some very good implementations of it already exist, for example in caliburn micro and
Microsoft Prism
The general idea is that you simplify event registration and deregistration and have a single source of events for many objects.
I never had performance issues with it. You simply put a _eventAggregator.Subscribe(this) when you want to start listening to events for an object and Unsubscribe if you want to stop. Whereever you want to fire an event, just publish it, EventAggregator does the routing.
This once again looks like an XY problem. OP seems to want to have a central place to handle event handlers, registration and disposal. The OP has gone down the route of trying to create a pattern that deal with this in a generic way, but has not looked into the state of the art regarding how this problem is typically solved. He has now come up against a problem in his design and is now asking for a solution to THAT problem, rather than the original problem of event handlers.
There are two good solutions to event handler registration lifecycle management that I know of in .net.
Weak Event Handler
You state that "It would be really bad if someone forgot to remove an event in the RemoveEventsListeners()." Yet do not actually mention WHY it is bad. Typically the only reason for this being bad is that the event handler will now keep an object in reference, that should be collected. With weak reference event handlers, the GC will still be able to collect your object, even when it subscribes to an object that is still alive.
Rx.Net
Rx.Net abstracts event registrations into IDisposables, which you can tie to the object's lifetime, assuming of course you want to control the lifetime of the registrations.
However I actually find the IObservable pattern much nicer to work with than event handler pattern, mostly because C# lacks first class support for event handlers (this is not the case with F#).
F#
Most of your problems will have stemmed from the short sighted design of events keyword handling in C# (specifically not making events a first class construct). F# however does support first class events, and thus should be able to support the pattern you are trying to construct.
Thus with this option you should scrap your code base and rewrite it in F#.
*EDIT added tongue in cheek option of rewriting in F#.
Right now I have six classes:
Listener - manages socket connections
World - a collection of entities and tasks
Ticker - coordinates updating the world
MessageProcessor - receives commands from players
Intelligence - governs the behavior of non-player characters
Tasks - tracking and execution of tasks
But they are like spaghetti with reference to each other all over the place... The World is a data model which the MessageProcessor, Intelligence, and Tasks classes modify. The Ticker coordiates those three classes updating the World. The Listener is used by the MessageProcessor for incomming messages, and by the other classes to push updates.
How can I improve this situation?
I gave a related answer not long ago. The subject was on improving the testability of code, for which the general solution is to loosen coupling. The main focus on that previous answer was on decoupling networking related code from the world and it's logic, because networking code is not unit testable and is a pain to mock too.
The solution given there was to use an interface for incoming messages, such that you decouple the MessageProcessor (named Handler in other post) from the network code, and similarly, decouple the UpdateNotifier from the World.
The dashed line is just an indirect reference handled either by an interface or delegate. There exists no direct relation between the World and networking component now, making it testable. This is really just an application of the Model View Adapter pattern.
This doesn't seem dissimilar to the design you've described having, except perhaps you are missing a few interfaces. With this pattern of interface based UpdateNotifiers used to push updates, I essentially reuse the same architecture for handling NPCs, tasks, or anything else which is processed elsewhere. You cherry pick the event notifiers you need for a particular area, and implement a concrete Notifier class for them, such that you have multiple adapters on the same model.
And that really only looks more complicated than it is. The World object has no direct dependencies on anything else, and each other class has at most one direct dependency. You can also isolate the timer from the World, as it probably isn't needed there - but perhaps one of the biggest hurdles is handling synchronization between the different adapters.
Well, I'm not sure I have a full picture of what the issues you are having are, but I have a few possibilities from what you have laid out so far. (I may be actually suggesting some things that are already done since I'm not sure I have enough from the one-liner descriptions to understand fully.
The Model
I would say from what you've described, the main thing that strikes me is that you'll want to start implementing common functionality in a class model; you will want either interfaces or base classes that you can use to derive your high-level objects from.
This way you can handle things consistently with little extra effort. I think the idea of "architectural layers" can be useful as a first cut of how to think about it, (e.g. low-level hardware stuff, socket handling etc., then middle-layers stuff like what kind of things happen in your game, and the details behind how game mechanics work, etc., and high-level stuff like what can the PC or NPCs do, what's the environment doing, etc.. and also the idea that you never want to "jump" layers). However, when it comes down to it the important thing is to just find the right abstractions for your game, and keep everything organized in such a way as you never feel like the bit of code you're working on is doing two completely different kinds of things.
So, first, let's take the fact that it sounds like (naturally) there are a lot of things interacting with world state. For something like this, it's probably advantageous to factor a lot of the 'stuff' out into a single class, and then mostly only have the one class doing that job. Ideally you implement, say, event communication/message passing, in it's own little group, so that there's no need to pollute your higher-level objects with the nitty-gritty of handling stuff.
e.g., you want to focus on what things are doing at a high level in the higher-level objects: in an AI perhaps "begin movement toward a location", "set my haste", "stop movement"; and in an environment subsystem do "start raining", "increase windspeed", "dim lights"; in a user class "fire weapon", "sleep", "cast spell". But I wouldn't want any of my high-level classes to even know about things like "send message to world", or "reset thirst timer", or "receive socket data", or "health cycle tick". (These are all just elucidations, not suggestions. ;D)
Events
For instance, I think it may be valuable to keep one object in charge of dispatching events to the World, that way you no longer have everyone talking to everyone. I would likely just create a set of stuff to handle events in general. So, maybe EventMain and an enumEvents that you use so that each type of event has a special ID. And then use Event as the base class for particular events that need extra functionality. (I have both the ID as well as a derivation model in mind, so that things thing like the Dispatcher which likely only need to know very basic things about the event don't have to also know about the derived classes. For instance, the dispatcher could take an event in and send it out without ever having to know the internals of a derived event. This may or may not turn out to be useful, but it's good to have the options.) You could also have an EventDispatcher that has a queue of events to be send to other subsystems.
You will want something common for recieving and sending events. You could do EventSourcer and EventSinker standalone classes that can be set up in any class that is generating or receiving events. Or, you could instead do IEventSource and IEventSink so that you could implement a common interface on multiple classes, or perhaps a common class EventSourceAndSink that implements both, and which is part of your base class model, so that anything that might need to handle events can just derive from it.
I would probably make ProtocolEncoder and ProtocolDecoder classes. You can always combine them into a single object, but it may be valuable, and shouldn't cause any issues if done adequately, to have them be two separate pieces of code. You could also have a ProtocolHelper class that factors out anything in common between the two. The encoders sole job is to receive messages from the network and turn them into events for your game, which it will then pass on to the EventDispatcher. The decoder class will take events from the dispatcher that need to go out to the network, and it will take the data from them and send it out.
How to Get Where You're Going
Since you do have working code, I would recommend that you just start doing it wherever seems natural. That could be things that are bogging you down, or things you've noticed to be very similar in different places that you could make regular with a class or some other type of abstraction, then pull out the old and put in the new. Once you have figured out a workable first cut of a class model, that should give you ideas based on what you already have and as you go be constantly reconsidering your model, fixing the things that are a problem, lather, rinse, repeat.
It doesn't have to be a lot of work, in fact, some of the most gratifying moments I've had working on code was when I was able to do a neat refactor that left a formerly hideous mess in much better shape, removing a lot of hard-to-understand code, and replacing it with something that's easier to understand, in fewer lines of code, and that opened up a path toward my next addition being a pleasure instead of another "zomg I don't have to touch that code again do I?" moment.
Best of luck, follows is a nominal guide to the things I was talking about; the first bit is more detailed because the main event class is one of the more important concepts, and then I try to just give a nominal overview of the classes and how they interact. I'm sure I could spend even more hours on this, but at this point I'll just say: ask me if you have questions and I'll do what I can to give you a good answer :)
Ideas in Code
Oh, one more thing of note is I didn't deal at all with the complexities added if you have multiple threads; there are things ranging from simple to intricate to manage it all if you do, but that's another exercise. :)
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
// this is internal to the project namespace, say, TimsWorld_o_Hurt
// I'm now resisting calling everything Xxxx_o_Hurt :)
// examples o' hurt
using EventHandlingLibrary;
namespace EventHandlingLibrary
{
// this will provide the base class for all the events, and can
// also have static methods like factory methods, destination
// lookups etc.
// I have the enums set to protected with the intent being that
// specific factory functions should be called by other classes.
// You should change this if it turns out to be too cumbersome.
public class EventOfHurt
{
#region Event Definitions
protected enum EEventType
{
// System Events
SystemInitializing,
SubsystemInitComplete,
FatalErrorNotification,
SubsystemPingReponse,
SubsystemPingRequest,
// Network Events
FrameRateError,
ThroughputData,
ServerTimeout,
ServerPingRequest,
ServerPingResponse,
// User Events
WeaponsFire,
MovementNotification,
FatigueUpdate
// and so forth
}
protected enum ESubsystem
{
System,
Dispatcher,
TickerTimer,
WorldEntity,
WorldTaskManager,
UserMessageProcessor,
NetworkListener,
NetworkTransmitter,
ProtocolEncoder,
ProtocolDecoder,
PlayerCharacter,
NonPlayerCharacter,
EventSink,
EventSource
// and such
}
#endregion
#region Event Information
public Guid EventId { get; protected set; }
public EEventType EventType { get; protected set; }
public ESubsystem SourceSubsystem { get; protected set; }
public ESubsystem DestSubsystem { get; protected set; }
private List<Tuple<EventOfHurt, DateTime>>
myEventReferences;
// the event(s) that triggered it, if any, and when rec'd
public Tuple<EventOfHurt, DateTime>[]
EventReferences
{
get { return myEventReferences.ToArray(); }
}
public DateTime Timestamp { get; private set; }
#endregion
// we'll be using factor methods to create events
// so keep constructors private; no default constructor
private EventOfHurt(
EEventType evt,
ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher
)
{
EventType = evt;
SourceSubsystem = src;
DestSubsystem = dest;
EventId = Guid.NewGuid();
Timestamp = DateTime.UtcNow;
}
// called to create a non-derived event for simple things;
// but keep other classes limited to calling specific factory
// methods
protected static EventOfHurt CreateGeneric(
EEventType evt, ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher,
Tuple<EventOfHurt, DateTime>[] reasons = null
)
{
EventOfHurt RetVal;
if (dest == null)
dest = ESubsystem.Dispatcher;
List<Tuple<EventOfHurt, DateTime>> ReasonList =
new List<Tuple<EventOfHurt,DateTime>>();
if (reasons != null)
ReasonList.AddRange(reasons);
// the initializer after the constructor allows for a
// lot more flexibility than e.g., optional params
RetVal = new EventOfHurt(evt, src) {
myEventReferences = ReasonList
};
return RetVal;
}
// some of the specific methods can just return a generic
// non-derived event
public static EventOfHurt CreateTickerTimerEvent(
EEventType evt, ESubsystem dest
)
{
ESubsystem src = ESubsystem.TickerTimer;
return CreateGeneric(evt, src, dest, null);
}
// some may return actual derived classes
public static EventOfHurt CreatePlayerActionEvent(
EEventType evt, ESubsystem dest,
Tuple<EventOfHurt, DateTime>[] reasons
)
{
PlayerEvent PE = new PlayerActionEvent(42);
return PE;
}
}
// could have some specific info relevant to player
// events in this class, world location, etc.
public class PlayerEvent :
EventOfHurt
{
};
// and even further speciailzation here, weapon used
// speed, etc.
public class PlayerActionEvent :
PlayerEvent
{
public PlayerActionEvent(int ExtraInfo)
{
}
};
}
namespace EntitiesOfHurt
{
public class LatchedBool
{
private bool myValue = false;
public bool Value
{
get { return myValue; }
set {
if (!myValue)
myValue = value;
}
}
}
public class EventOfHurtArgs :
EventArgs
{
public EventOfHurtArgs(EventOfHurt evt)
{
myDispatchedEvent = evt;
}
private EventOfHurt myDispatchedEvent;
public EventOfHurt DispatchedEvent
{
get { return myDispatchedEvent; }
}
}
public class MultiDispatchEventArgs :
EventOfHurtArgs
{
public MultiDispatchEventArgs(EventOfHurt evt) :
base(evt)
{
}
public LatchedBool isHandled;
}
public interface IEventSink
{
// could do this via methods like this, or by attching to the
// events in a source
void MultiDispatchRecieve(object sender, MultiDispatchEventArgs e);
void EventOfHurt(object sender, EventOfHurtArgs e);
// to allow attaching an event source and notifying that
// the events need to be hooked
void AttachEventSource(IEventSource evtSource);
void DetachEventSource(IEventSource evtSource);
}
// you could hook things up in your app so that most requests
// go through the Dispatcher
public interface IEventSource
{
// for IEventSinks to map
event EventHandler<MultiDispatchEventArgs> onMultiDispatchEvent;
event EventHandler<EventOfHurtArgs> onEventOfHurt;
void FireEventOfHurt(EventOfHurt newEvent);
void FireMultiDispatchEvent(EventOfHurt newEvent);
// to allow attaching an event source and notifying that
// the events need to be hooked
void AttachEventSink(IEventSink evtSink);
void DetachEventSink(IEventSink evtSink);
}
// to the extent that it works with your model, I think it likely
// that you'll want to keep the event flow being mainly just
// Dispatcher <---> Others and to minimize except where absolutely
// necessary (e.g., performance) Others <---> Others.
// DON'T FORGET THREAD SAFETY! :)
public class Dispatcher :
IEventSource, IEventSink
{
}
public class ProtocolDecoder :
IEventSource
{
}
public class ProtocolEncoder :
IEventSink
{
}
public class NetworkListener
{
// just have these as members, then you can have the
// functionality of both on the listener, but the
// listener will not send or receive events, it will
// focus on the sockets.
private ProtocolEncoder myEncoder;
private ProtocolDecoder myDecoder;
}
public class TheWorld :
IEventSink, IEventSource
{
}
public class Character
{
}
public class NonPlayerCharacter :
Character,
IEventSource, IEventSink
{
}
public class PlayerCharacter :
Character,
IEventSource, IEventSink
{
}
}
My class with an event:
public class WindowModel
{
public delegate void WindowChangedHandler(object source, WindowTypeEventArgs e);
public event WindowChangedHandler WindowChanged;
public void GotoWindow(WindowType windowType)
{
this.currentWindow = windowType;
this.WindowChanged.Invoke(this, new WindowTypeEventArgs(windowType));
}
}
Derived event class:
public class WindowTypeEventArgs : EventArgs
{
public readonly WindowType windowType;
public WindowTypeEventArgs(WindowType windowType)
{
this.windowType = windowType;
}
}
Some other class that register it to the event:
private void SetupEvents()
{
this.WindowModel.WindowChanged += this.ChangeWindow;
}
private void ChangeWindow(object sender, WindowTypeEventArgs e)
{
//change window
}
What have I gained from following the .Net convention?
It would make more sense to have a contract like this
public delegate void WindowChangedHandler(WindowType windowType);
public event WindowChangedHandler WindowChanged;
Doing it this way, I don't need to create a new class and is easier to understand.
I am not coding a .Net library. This code is only going to be used in this project. I like conventions but am I right when I say that in this example it does not make sense or have i missunderstood something?
Viewed in isolation, yes, you're correct: the .NET conventional syntax is more verbose and less intuitive, but there are advantages:
Future changes to the information passed by your event do not automatically require changes to every consumer of the event. For example, if you wanted to add an additional piece of information to your event--say, a WindowTitle string--you'll have to modify the signature of every single function that gets attached to that event, regardless of whether or not they use it. With the EventArgs approach, you add the property to the arguments and only alter the functions that need to take advantage to the additional information.
Since .NET 2.0 introduced the EventHandler<TEventArgs> delegate type, you no longer need to define your own event delegates manually. In your example, you would type your event as EventHandler<WindowTypeEventArgs> instead of WindowChangedHandler.
The EventArgs approach makes it easy to pass multiple types of information back to the calling function. If you needed to do this in your alternative example (that passes the event parameter directly), you'd still end up creating your own -tuple class to hold the information.
The impact of the first one is made more evident when you look at the actual pattern for .NET events in creating a protected virtual function that actually does the invoking. For example:
public event EventHandler<WindowTypeEventArgs> WindowChanged;
protected virtual void OnWindowChanged(WindowTypeEventArgs e)
{
var evt = WindowChanged;
if(evt != null) evt(this, e);
}
There are a couple of things I'd like to point out here:
Using the pattern of creating this event-invoking method allows you to avoid null checking throughout your code (an event without any functions attached to it will be null and will throw an exception if you try to invoke it)
This pattern also allows classes that inherit from you to control the order of invocation, allowing them to execute their code explicitly either before or after any outside consumers
This is especially important in multithreaded environments. If you just said if(WindowChanged != null) WindowChanged(this, e);, you would actually run the risk of the WindowChanged event becoming null between the time you check it and the time you call it. This isn't important to do in single-threaded scenarios, but is a great defensive habit to form.
I recognise your confusion! I had the same feeling when I first looked at this too.
The big thing to realise is that it doesn't give you much of an advantage programmatically speaking, but it is a convention that is well known in the framework. As such, there are plenty of tools that expect the void EventName(object sender, EventArgs e) signature. Some IoC containers, for example, can use this signature to auto wire events at construction time.
In short, it looks a bit weird, but it's a convention. Stick with it and the bulb will light up eventually!
You can use your delegate. Nobody will force you. It's just a good pattern for events.
If you use standart Sender-EventArgs pattern you'll be able to use the same ChangeWindow handler for other events too.