delegate or reflection? - c#

I have a class with a method in which a string will be passed. That method will do some things to that string and it then passes the string to a certain object which can do other things with the string.
So it basically looks like this:
class Main
{
public Main()
{
strClass str = new strClass(this);
}
public function handler ( )
{
console.log("No string is passed yet, but this method is called from receiveData()");
}
}
class strClass
{
object handler;
public strClass ( handler )
{
// save the object
this.handler = handler;
}
public receiveData ( string str )
{
// This method does some stuff with the string
// And it then passes it on to the supplied object (handler) which will do
// the rest of the processing
// I'm calling the "handler" method in the object which got passed in the
// constructor
Type thisType = this.handler.GetType();
MethodInfo theMethod = thisType.GetMethod("handler");
theMethod.Invoke(this.handler, null);
}
}
Now this code works good, with the reflection stuff. But i was wondering, shouldn't this be possible (and maybe even better?) with delegates?? If so, how can i implement this by using a delegate instead?

Couldn't you use interfaces instead:
interface IStringHandler {
void HandleString(string s);
}
class strClass
{
IStringHandler handler = null;
public strClass(IStringHandler handler)
{
this.handler = handler;
}
public void ReceiveData(string s)
{
handler.HandleString(s);
}
}
class Main : IStringHandler
{
// Your code
}

A delegate is a better option here.
class Main
{
public Main()
{
StrClass str = new StrClass(this.Handler);
}
public void Handler ( )
{
//called from recieve data
}
}
class StrClass
{
readonly Action _handler;
public StrClass ( Action callback)
{
// save the object
this._handler = callback;
}
public void receiveData( string str )
{
this._handler();
}
}

You can do it with an Action like this:
class Main
{
public Main()
{
strClass str = new strClass(newString =>
{
console.log("This string I got back: " + newString);
});
}
}
class strClass
{
Action<string> callback;
public strClass (Action<string> callback)
{
// save the action
this.callback = callback;
}
public receiveData ( string str )
{
// Do something with the string
callback(str);
}
}

Even nicer than using delegates whould be using the
Chain of Responsibility design pattern, which does exactly what you need :).

Firstly, if you must call an unknown method by name, use dynamic - it is heavily optimised for this (although still not a great idea):
((dynamic)handler).handler(); // but please don't use this! see below
However, I would instead look at either an Action<string> (or maybe Func<string,string>), or an interface with a known method on it.

Basically, you want to change how your StrClass object react to data begin received. Sounds like events to me.
something like this, where you have handling methods both in the Main and in a generic HandlerObject:
class StrClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = null;
public void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
private string receivedString;
public string ReceivedString
{
get;
set
{
string oldStr = receivedString;
receivedString = value;
PropertyChanged(receivedString, new PropertyChangedEventArgs("ReceivedString"));
}
}
public void receiveData(string str)
{
//event fires here
ReceivedString = str;
}
}
class HandlerObject
{
public void HandlerMethod1(string s)
{
//magic
}
public void HandlerMethod2(string s)
{
//different kind of magic
}
}
class Program
{
static void HandlerMethod3(string s)
{
//another kind of magic!
}
static void Main(string[] args)
{
StrClass class1 = new StrClass();
StrClass class2 = new StrClass();
StrClass class3 = new StrClass();
HandlerObject handler = new HandlerObject();
class1.PropertyChanged += (s, e) => { handler.HandlerMethod1(s.ToString()); };
class2.PropertyChanged += (s, e) => { handler.HandlerMethod2(s.ToString()); };
class3.PropertyChanged += (s, e) => { HandlerMethod3(s.ToString()); };
}
}

Related

Assigning event to subscribe with new

I'm making QuestSystem in unity.
what i want to do is assigning to my questData an event so it can know when the quest objective has been completed.
Lets say there is a class named A and action called a.
and i want Class B, Action b want to have reference to A.a
So if i do
b = A.a;,
b+= someAction;, it actually does a+=someAction;
but when if i do that. It will just simply b+=someAction and A.a will remain null
what should i do to perform what i want?
here are some tags. (i don't know what the answer would be. so..)
# event
# subscribing event
# assigning event
# referencing event
# action
# delegate
====== Edited =======
here is my code.
QuestData.cs
public class QuestData
{
public string questName;
public string qusetID;
public string questDescription;
public SceneType questSceneType;
private string isActivePrefsKey { get { return $"QuestKey{qusetID}"; } }
public bool isActive {
get {
return Convert.ToBoolean (PlayerPrefs.GetInt (isActivePrefsKey));
}
set { PlayerPrefs.SetInt (isActivePrefsKey, Convert.ToInt16 (value)); }
}
public QuestObjective questObjective;
public QuestReward questReward;
public void Activate ()
{
if (AppController.CurrentScene == questSceneType) {
questObjective.ActivateObjective ();
}
}
}
QuestObjective.cs
public class QuestObjective
{
// TODO rename all
public int goalObjectiveCount;
public int currentObjectiveCount;
public Action questAction;
public void OnConditionMatch ()
{
Debug.Log ("OnConditionMatch");
currentObjectiveCount += 1;
}
public void ActivateObjective ()
{
questAction += OnConditionMatch;
}
}
QuestManager.cs
public class QuestManager : MonoBehaviour
{
List<QuestData> questDatas;
void Awake ()
{
PrepareQuestDatas ();
ActivateActiveQuests ();
}
void ActivateActiveQuests ()
{
var activeQuests = GetActiveQuests ();
foreach (var activeQuest in activeQuests) {
activeQuest.Activate ();
}
}
List<QuestData> GetActiveQuests ()
{
// for debuging
return questDatas;
// real code
return questDatas.Where (q => q.isActive == true).ToList ();
}
public void PrepareQuestDatas ()
{
questDatas = new List<QuestData> {
new QuestData {
questName = "Foot Print",
questDescription = "win the game for first time",
questSceneType = SceneType.Main,
questObjective = new QuestObjective {
goalObjectiveCount = 1,
questAction = GamePlayController.instance.endGameCon.onWinGame
},
questReward = new QuestCoinReward{
rewardAmount = 100,
},
}
};
}
}
One potential solution is to create a new set of EventArgs, like this:
public class QuestCompletedEventArgs : System.EventArgs
{
public QuestObjective FinishedObjective { get; }
public QuestCompletedEventArgs(QuestObjective objectiveIn) {
this.FinishedObjective = objectiveIn;
}
}
(probably in a different file)
... and use it like this:
First, create an event delegate:
public delegate void QuestObjectiveCompleteHandler(object sender, QuestCompletedEventArgs e);
Instantiate the event delegate:
public event QuestObjectiveCompletedHandler CompletedObjective;
Define the method that will do something when the objective is completed:
public void ObjectiveCompleted(object sender, QuestCompletedEventArgs e)
{
// do something
}
Assign that method to the event:
this.CompletedObjective += this.ObjectiveCompleted;
From here, you can make the FinishedObjective object within the QuestCompletedEventArgs a List<QuestObjective>, and FinishedObjective.add(objectiveIn) whenever appropriate.
You should also be able to make the event handling method act differently when a certain amount of objectives have been completed, or whatever you want to do with that information.
Of course, you can also add multiple different methods to respond to this event by adding more this.CompletedObjective += this.methodName; lines, as long as the signature of the new method(s) carry that same signature.
Reading into your example, I have written up some code where "A" is QuestObjective and "B" is Quest. The Quest object needs to know when objective has been marked as completed.
Using event handlers, we can set it up so that B is notified when an action occurs on A.
Like this:
// B
public class Quest
{
public Quest()
{
Objectives = new List<QuestObjective>();
// load objectives... Fake
Objectives.Add(new QuestObjective("obj 1"));
Objectives.Add(new QuestObjective("obj 2"));
Objectives.Add(new QuestObjective("obj 3"));
foreach(var o in Objectives) // subscribe to QuestObjective events
{
o.ObjectiveCompleted += (sender, args) => ReportObjectiveCompleted();
}
}
public void ReportObjectiveCompleted()
{
// let 'em know
}
public List<QuestObjective> Objectives { get; set; }
}
// A
public class QuestObjective
{
public string Name { get; set; }
public QuestObjective(string name = "unknown")
{
Name = name;
}
public event EventHandler ObjectiveCompleted;
public void MarkCompleted()
{
// when a task is marked as complete and IF there are
// subscribers to this event then call the event handler
var a = ObjectiveCompleted;
if (a != null)
{
a(this, new EventArgs()); // use different event args to pass data
}
}
}

Passing additional parameters to event Action;

Definittion of ProgressChanged:
// Summary:
// Event called whenever the progress of the upload changes.
public event Action<IUploadProgress> ProgressChanged;
public void insertFile(String filePath)
{
//.. some code
insertRequest.ProgressChanged += Upload_ProgressChanged;
}
public void Upload_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
//.. I need filePath from insertFile() here!
}
How to pass additional paramtres to Upload_ProgressChanged ?
I did the following:
public void insertFile(String filePath)
{
//.. some code
ProgressChangedEventArgs args = new ProgressChangedEventArgs()
{
path = filePath
};
insertRequest.ProgressChanged += Upload_ProgressChanged;
}
static void Upload_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
public class ProgressChangedEventArgs : EventArgs
{
public string path { get; set; }
}
And I have mistake Can not implicitly convert type 'void' to 'System.Action<Google.Apis.Upload.IUploadProgress>'
Instead of using an event you can capture the variable inside a closure
insertRequest.ProgressChanges += progress => { /* Do something with filePath here */ };
Firstly, define the EventArgs class - this will let you have whatever information you like...
public class ProgressChgEventArgs : System.EventArgs
{
public string Name { get;set; }
public int InstanceId { get;set; }
public ProgressChgEventArgs(string name, int id)
{
Name = name;
InstanceId = id;
}
}
Next, create the event that consumes these arguments:
public event EventHandler<ProgressChgEventArgs> ProgressChanged;
Then, have an 'On....' method that invokes the handlers
public void OnProgressChanged(ProgressChgEventArgs e)
{
var handler = new EventHandler<ProgressChgEventArgs>();
if (handler != null)
handler(this, e);
}
Now, at the relevant point in your code (presumably when the progress changes!) you call OnProgressChanged(), passing in an appropriate instance of the ProgressChgEventArgs:
private void Progress(string caller, int callerId)
{
var arguments = new ProgressChgEventArgs(caller, callerId);
OnProgressChanged(arguments);
}

RegisterCallback<T>(Action<T> func) , how do I store this function pointer in a class?

I'm trying to expose an API such that, I do the following
RegisterCallback<T>(Action<T> func)
{
someObj.FuncPointer = func;
}
Later on, I call func(obj) .. and the obj is of type T that the user said.
More concrete example:
var callbackRegistrar = new CBRegistrar();
callbackRegistrar.RegisterCallback<ISomeClass>(SomeFunc);
public static void SomeFunc(ISomeClass data)
{
//
}
EDIT: So I may not have been clear, so I'll add more code:
I want to make only "one" object of CBRegistrar, and connect it with many Callbacks, as such:
var callbackRegistrar = new CBRegistrar();
callbackRegistrar.RegisterCallback<ISomeClass>(SomeFunc);
callbackRegistrar.RegisterCallback<ISomeOtherClass>(SomeFunc2);
...
In fact the above code is called by reflecting over a directory of plugins.
The user puts this in their code -->
public static void SomeFunc(ISomeClass data)
{
//
}
public static void SumFunc2(ISomeOtherClass data)
{
//
}
It looks to me as if this is not possible using Generics, etc. What it looks like I might have to do is make an interface called IPlugin or something, and ask the user to do this ..
[PluginIdentifier(typeof(ISomeClass))]
public static void SomeFunc(IPluginData data)
{
var castedStuff = data as ISomeClass; // ISomeClass inherits from IPluginData
}
Seems like asking the user to do stuff that we should take care of, but anyway ...
You need a Action<T> func to store it in. There is a semantic check to make here: if someone calls RegisterCallback twice (with different values), do you want to replace the callback, or keep both ? Assuming the latter, someObj probably wants an event (indeed, this entire API could be exposed as an event), so - in the someObj class:
public event Action<T> FuncPointer;
private void InvokeCallback(T data) {
var handler = FuncPointer;
if(handler != null) handler(data);
}
Noting that RegisterCallback could be replaced entirely, still keeping the data on obj:
public event Action<T> Completed {
add { obj.FuncPointer += value; }
remove { obj.FuncPointer -= value; }
}
Then usage would be:
var callbackRegistrar = new CBRegistrar();
callbackRegistrar.Completed += SomeFunc;
Callback functions are not much used in C#. They've been replaced by events which are more elegant and easier to work with.
class CBRegistrar
{
public delegate void ActionRequiredEventHandler(object sender, ISomeClass e);
public event ActionRequiredEventHandler ActionRequired;
void RaiseActionRequiredEvent(ISomeClass parm)
{
if ( ActionRequired != null)
{
ActionRequired(this, parm);
}
}
}
class APIConsumer
{
var callbackRegistrar = new CBRegistrar();
public APIConsumer()
{
callbackRegistrar.ActionRequired += SomeFunc;
}
public void SomeFunc(object sender, ISomeClass data)
{
}
}
If you still want to use Callbacks, you can use Delegates which are more or less function pointer.
The CBRegistrar will need to be generic (if it's OK to keep a single callback type) or it can do some internal casting (if several callback types need to be registered).
public class CBRegistrar<T>
{
private Action<T> callback;
private Dictionary<Type, object> callbackMap;
public CBRegistrar()
{
this.callbackMap = new Dictionary<Type, object>();
}
public void RegisterCallback(Action<T> func)
{
this.callback = func;
}
public void RegisterGenericCallback<U>(Action<U> func)
{
this.callbackMap[typeof(U)] = func;
}
public Action<U> GetCallback<U>()
{
return this.callbackMap[typeof(U)] as Action<U>;
}
}
public interface ISomeClass
{
string GetName();
}
public class SomeClass : ISomeClass
{
public string GetName()
{
return this.GetType().Name;
}
}
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var callbackRegistrar = new CBRegistrar<ISomeClass>();
callbackRegistrar.RegisterCallback(SomeFunc);
callbackRegistrar.RegisterGenericCallback<ISomeClass>(SomeFunc);
var someone = new SomeClass();
callbackRegistrar.GetCallback<ISomeClass>()(someone);
}
public static void SomeFunc(ISomeClass data)
{
// Do something
Console.WriteLine(data.GetName());
}
}
}

Event Bus implementation question

I'm trying to implement simple Event Bus
I started like this:
public class RegistrationData
{
public object RegisteredObject { get; set; }
public Type eventType { get; set; }
public EventHandler MethodToInvoke;
}
public class EventBus
{
private static EventBus instance;
private static readonly object lockObject = new object();
private static List<RegistrationData> registrationList;
private EventBus()
{
}
public static EventBus Instance
{
get
{
lock (lockObject)
{
registrationList = new List<RegistrationData>();
return instance ?? new EventBus();
}
}
}
public void Subscribe(RegistrationData registrationData)
{
if(!registrationList.Contains(registrationData)) registrationList.Add(registrationData);
}
public void Unsubscribe(object objectToUnregister, Type eventType)
{
foreach(RegistrationData data in registrationList)
if (data.RegisteredObject == objectToUnregister && data.eventType == eventType) registrationList.Remove(data);
}
public void UnregisterAllMessages(object objectToUnregister)
{
foreach(RegistrationData data in registrationList)
if(data.RegisteredObject == objectToUnregister) registrationList.Remove(data);
}
public void PublishEvent(object sender, EventArgs eventArgs)
{
foreach (RegistrationData data in registrationList)
if (EventArgs is typeof(data.Type)) data.MethodToInvoke(sender, eventArgs);
}
}
But I have problem in PublishEvent method. I'm unable to determine type of event argument.
And I'm suspicious that all this is pretty wrong.
Can someone point out what I do wrong, how should I implement this?
Or how event bus is generally implemented, or some framework which I can use instead of implementing my own and spending time on that.
Um, not really sure how your Eventbus should behave. Short of knowing where you are heading it can be useful to look at how other people implemented the problem.
There is a down-to-earth event aggregator in the caliburn.micro project
I like to use MemBus when I need an event aggregator, partly because I wrote it myself, partly because it covers all my needs in that respect. It is more involved than caliburn's one, but then it has more features
I think you should start by defining Event Bus. What do you see as the difference between an Event Bus and the built-in .NET mechanisms for firing and sinking events? What you have so far looks like it implements not much more than the equivalent of .NET events. .NET intrinsically supports event handling so you wouldn't need an event bus if you don't need more than what .NET already provides:
class Program
{
static void Main(string[] args)
{
BusinessObject1 bo = new BusinessObject1("First Value");
// Subscribe
bo.Publish += new BusinessObject.PublishObject(bo_Publish);
bo.Update("Second Value");
// UnSubscribe
bo.Publish -= new BusinessObject.PublishObject(bo_Publish);
bo.Update("Third Value");
// Subscribe multiple
bo.Publish += new BusinessObject.PublishObject(bo_Publish);
bo.Publish += new BusinessObject.PublishObject(bo_Publish2);
bo.Update("Fourth Value");
// UnregisterAllMessages
bo.UnsubcribeAll();
bo.Update("Fifth Value");
}
static void bo_Publish(BusinessObject sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
}
}
static void bo_Publish2(BusinessObject sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
}
}
}
abstract class BusinessObject
{
public delegate void PublishObject(BusinessObject sender, EventArgs args);
public event PublishObject Publish;
// PublishEvent
protected void Update(EventArgs args)
{
if (Publish != null)
Publish(this, args);
}
public void UnsubcribeAll()
{
Publish = null;
}
}
class BusinessObject1 : BusinessObject
{
public class PublishBusinessObject1EventArgs : EventArgs
{
public string oldValue;
public PublishBusinessObject1EventArgs(string oldValue)
{
this.oldValue = oldValue;
}
}
public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);
public string Value {get; private set;}
public BusinessObject1(string value)
{
this.Value = value;
}
public void Update(string newValue)
{
PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
Value = newValue;
base.Update(args);
}
}
Edit:
If you don't want your business objects to have to inherit from a common base class (as you suggested in your comment) you can make a few modifications so that EventBus stands more independently, but you still don't need to re-implement all the event registration framework to do this:
class Program
{
static void Main(string[] args)
{
BusinessObject1 bo = new BusinessObject1("First Value");
// Subscribe
EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
bo.Update("Second Value");
// UnSubscribe
EventBus.Publish -= new EventBus.PublishObject(EventBus_Publish);
bo.Update("Third Value");
// Subscribe multiple
EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
EventBus.Publish += new EventBus.PublishObject(EventBus_Publish2);
bo.Update("Fourth Value");
// UnregisterAllMessages
EventBus.UnsubcribeAll();
bo.Update("Fifth Value");
}
static void EventBus_Publish(object sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
}
}
static void EventBus_Publish2(object sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
}
}
}
static class EventBus
{
public delegate void PublishObject(object sender, EventArgs args);
public static event PublishObject Publish;
// PublishEvent
public static void Update(object sender, EventArgs args)
{
if (Publish != null)
Publish(sender, args);
}
public static void UnsubcribeAll()
{
Publish = null;
}
}
class BusinessObject1
{
public class PublishBusinessObject1EventArgs : EventArgs
{
public string oldValue;
public PublishBusinessObject1EventArgs(string oldValue)
{
this.oldValue = oldValue;
}
}
public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);
public string Value { get; private set; }
public BusinessObject1(string value)
{
this.Value = value;
}
public void Update(string newValue)
{
PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
Value = newValue;
EventBus.Update(this, args);
}
}
Edit 2: By the way, if you want more control over the subscription process, you can get more control there too by defining custom event accessors as described at http://msdn.microsoft.com/en-us/library/bb882534.aspx:
static class EventBus
{
public delegate void PublishObject(object sender, EventArgs args);
private static List<PublishObject> subscribers = new List<PublishObject>();
public static event PublishObject Publish
{
add
{
subscribers.Add(value);
Console.WriteLine("Added subscriber {0}.{1}", value.Method.DeclaringType.Name, value.Method.Name);
}
remove
{
bool result = subscribers.Remove(value);
Console.WriteLine("Removed subscriber {0}.{1} ({2})", value.Method.DeclaringType.Name, value.Method.Name, result ? "success" : "failure");
}
}
// PublishEvent
public static void Update(object sender, EventArgs args)
{
foreach (PublishObject p in subscribers)
{
Console.WriteLine("Publishing to {0}.{1}", p.Method.DeclaringType.Name, p.Method.Name);
p.Invoke(sender, args);
}
}
public static void UnsubcribeAll()
{
subscribers.Clear();
}
}
Well, as a first suggestion, it looks to me as if you're trying to implement this as a singleton. Otherwise, what would the
private static EventBus instance;
be good for? But the private instance member is never assigned anywhere, that's one thing I'd suggest you should fix. For reference, here's a really good article on various implementations of singletons. If you've got access to .net4 I'd suggest you use the LazySingleton3 approach.
The only other thing that comes to mind is, this looks like it could be a use-case for Generics. Have a look at the EventHandler<TEventArgs> Delegate.
Apart from that, I can't recommend much more since I don't exactly understand what you're trying to do.
EDIT
Have a look at the accepted answer on this question. It contains a link to a blog post of someone who implemented this several years ago. Seems like you don't need to re-invent the wheel.

Design Help – Polymorphic Event Handling

Design Question – Polymorphic Event Handling
I’m currently trying to reduce the number of Event Handles in my current project. We have multiple systems that send data over USB. I currently have a routine to read in the messages and parse the initial header details to determine which system the message came from. The headers are a little different, so the EventArgs I created are not the same. Then I notify all “observers” of the change. So what I have right now is the following:
public enum Sub1Enums : byte
{
ID1 = 0x01,
ID2 = 0x02
}
public enum Sub2Enums : ushort
{
ID1 = 0xFFFE,
ID2 = 0xFFFF
}
public class MyEvent1Args
{
public Sub1Enums MessageID;
public byte[] Data;
public MyEvent1Args(Sub1Enums sub1Enum, byte[] data)
{
MessageID = sub1Enum;
Data = data;
}
}
public class MyEvent2Args
{
public Sub2Enums MessageID;
public byte[] Data;
public MyEvent2Args(Sub2Enums sub2Enum, byte[] data)
{
MessageID = sub2Enum;
Data = data;
}
}
Form1 code
public class Form1
{
public delegate void TestHandlerCurrentlyDoing(MyEvent1Args eventArgs1);
public delegate void TestHandlerCurrentlyDoingAlso(MyEvent2Args eventArgs2);
public event TestHandlerCurrentlyDoing mEventArgs1;
public event TestHandlerCurrentlyDoingAlso mEventArgs2;
public Form1()
{
mEventArgs1 += new TestHandlerCurrentlyDoing(Form1_mEventArgs1);
mEventArgs2 += new TestHandlerCurrentlyDoingAlso(Form1_mEventArgs2);
}
void Form1_mEventArgs2(MyEvent2Args eventArgs2)
{
// Do stuff here
Sub2Enums mid = my_event2_args.MessageID;
byte[] data = my_event2_args.Data;
}
void Form1_mEventArgs1(MyEvent1Args eventArgs1)
{
// Do stuff here
Sub1Enums mid = my_event1_args.MessageID;
byte[] data = my_event1_args.Data;
}
And in the parse algorithm I have something like this based on which message it is:
void ParseStuff()
{
if (mEventArgs1 != null)
{
mEventArgs1(new MyEvent1Args(Sub1Enums.ID1, new byte[] { 0x01 }));
}
if (mEventArgs2 != null)
{
mEventArgs2(new MyEvent2Args(Sub2Enums.ID2, new byte[] { 0x02 }));
}
}
What I really want to do is this:
public class Form1
{
public delegate void TestHandlerDesired(MyEvent1Args eventArgs1);
public delegate void TestHandlerDesired(MyEvent2Args eventArgs2);
public event TestHandlerDesired mEventArgs;
public Form1()
{
mEventArgs += new TestHandlerDesired (Form1_mEventArgs1);
mEventArgs += new TestHandlerDesired (Form1_mEventArgs2);
}
}
And for ambiguity reasons we can’t do this. So my question is what would be a better approach to this problem?
If you're trying to reduce the number of event handles in order abstract / simplify the coding you have to do, then applying the Double Dispatch design pattern to your event args would be perfect. It's basically an elegant (but wordy) fix for having to perform safe type casts (/ is instanceof checks)
I could make MyEvent1Args and MyEvent2Args derive from a common base class and do the following:
public class BaseEventArgs : EventArgs
{
public byte[] Data;
}
public class MyEvent1Args : BaseEventArgs
{ … }
public class MyEvent2Args : BaseEventArgs
{ … }
public delegate void TestHandlerWithInheritance(BaseEventArgs baseEventArgs);
public event TestHandlerWithInheritance mTestHandler;
mTestHandler += new TestHandlerWithInheritance(TestHandlerForEvent1Args);
mTestHandler += new TestHandlerWithInheritance(TestHandlerForEvent2Args);
void TestHandlerForEvent1Args(BaseEventArgs baseEventArgs)
{
MyEvent1Args my_event1_args = (baseEventArgs as MyEvent1Args);
if (my_event1_args != null)
{
// Do stuff here
Sub1Enums mid = my_event1_args.MessageID;
byte[] data = my_event1_args.Data;
}
}
void TestHandlerForEvent2Args(BaseEventArgs baseEventArgs)
{
MyEvent2Args my_event2_args = (baseEventArgs as MyEvent2Args);
if (my_event2_args != null)
{
// Do stuff here
Sub2Enums mid = my_event2_args.MessageID;
byte[] data = my_event2_args.Data;
}
}
And in the parse algorithm I have something like this based on which message it is:
if (mTestHandler!= null)
{
mTestHandler (new MyEvent1Args(Sub1Enums.ID1, new byte[] { 0x01 }));
}
if (mTestHandler!= null)
{
mTestHandler (new MyEvent2Args(Sub2Enums.ID2, new byte[] { 0x02 }));
}
Take a break from polymorphism and look into using indirection, specifically the Event Aggregator pattern if you haven't already; Fowler first # http://martinfowler.com/eaaDev/EventAggregator.html and then postings by Jeremy Miller if you need more ideas.
Cheers,
Berryl
You could consider a few options (I am not sure what exactly you want to achieve here):
1. Create a hierarchy of EventArgs and make the observers responsible for filtering stuff they are interested in (this is what you proposed in your answer). This especially makes sense if some observers are interested in multiple types of messages, ideally described by the base class type.
2. Don't use .Net delegates, just implement it yourself such that when you register the delegate it also takes the type of event it expects. This assumes you have done the work from (1), but you want to pass the filtering to your class and not observers
E.g. (untested):
enum MessageType
{
Type1,Type2
}
private Dictionary<MessageType, TestHandlerWithInheritance> handlers;
public void RegisterObserver(MessageType type, TestHandlerWithInheritance handler)
{
if(!handlers.ContainsKey(type))
{
handlers[key] = handler;
}
else
{
handlers[key] = Delegate.Combine(handlers[key] , handler);
}
}
And when a new message arrives, you run the correct delegate from the handlers dictionary.
3. Implement events in the way its done in WinForms, so that you don't have an underlying event for ever exposed event. This makes sense if you expect to have more events than observers.
E.g.:
public event EventHandler SthEvent
{
add
{
base.Events.AddHandler(EVENT_STH, value);
}
remove
{
base.Events.RemoveHandler(EVENT_STH, value);
}
}
public void AddHandler(object key, Delegate value)
{
ListEntry entry = this.Find(key);
if (entry != null)
{
entry.handler = Delegate.Combine(entry.handler, value);
}
else
{
this.head = new ListEntry(key, value, this.head);
}
}
public void RemoveHandler(object key, Delegate value)
{
ListEntry entry = this.Find(key);
if (entry != null)
{
entry.handler = Delegate.Remove(entry.handler, value);
}
}
private ListEntry Find(object key)
{
ListEntry head = this.head;
while (head != null)
{
if (head.key == key)
{
return head;
}
head = head.next;
}
return head;
}
private sealed class ListEntry
{
// Fields
internal Delegate handler;
internal object key;
internal EventHandlerList.ListEntry next;
// Methods
public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next)
{
this.next = next;
this.key = key;
this.handler = handler;
}
}
Please let me know if you want me to expand on any of the answers.
If you're trying to reduce the number of event handles to save RAM, do what microsoft do (in System.ComponentModel.Component) and use an EventHandlerList to track all of your events. Here is an article that describes conserving memory use with an EventHandlerList, and here is a similar article that's written in C#..
The gist of it is that you can declare a single EventHandlerList (remember to dispose it) in your class, along with a unique key:
public class Foo
{
protected EventHandlerList listEventDelegates = new EventHandlerList();
static readonly object mouseDownEventKey = new object();
...override the event property:
public event MouseEventHandler MouseDown {
add { listEventDelegates.AddHandler(mouseDownEventKey, value); }
remove { listEventDelegates.RemoveHandler(mouseDownEventKey, value); }
}
...and provide a RaiseEvent method:
protected void RaiseMouseDownEvent(MouseEventArgs e)
{
MouseEventHandler handler = (MouseEventHandler) base.Events[mouseDownEventKey];
if (handler != null)
{
handler(this, e);
}
}
Of course, you just reuse the same EventHandlerList for all your events (but with different keys).

Categories

Resources