class Program
{
static void Main(string[] args)
{
Brain b = new Brain();
b.start();
}
}
public class Map
{
public string key;
public string value;
public Map(string key, string value)
{
this.key = key;
this.value = value;
}
}
public class Ears
{
private SpeechRecognitionEngine ears;
public Ears(List<Map> knowledge, EventHandler<SpeechRecognizedEventArgs> onRecognise, EventHandler<SpeechRecognitionRejectedEventArgs> onReject)
{
this.ears = new SpeechRecognitionEngine();
Choices commands = new Choices();
foreach (var item in knowledge)
{
commands.Add(item.key);
}
GrammarBuilder gBuilder = new GrammarBuilder();
gBuilder.Append(commands);
Grammar grammar = new Grammar(gBuilder);
ears.LoadGrammar(grammar);
ears.SetInputToDefaultAudioDevice();
ears.RecognizeAsync(RecognizeMode.Multiple);
ears.SpeechRecognized += onRecognise;
ears.SpeechRecognitionRejected += onReject;
}
public void stop()
{
ears.RecognizeAsyncCancel();
}
public void start()
{
ears.RecognizeAsync(RecognizeMode.Multiple);
}
}
public class Brain
{
protected List<Map> commands;
protected List<Map> answers;
private readonly Ears ears;
public Brain()
{
commands = new List<Map>();
commands.Add(new Map("list",""));
commands.Add(new Map("somethingElse","someValue"));
ears = new Ears(commands, onRecognized, onRejected);
}
private void onRecognized(object sender, SpeechRecognizedEventArgs e)
{
Console.WriteLine(e.Result.Text);
terminateCommands(new Map(e.Result.Text.Trim().ToLower(), null));
}
public void start() {
while (true) {
string answer = Console.ReadLine();
if (answer!="")
{
terminateCommands(new Map(answer, null));
}
}
}
private void onRejected(object sender, SpeechRecognitionRejectedEventArgs e)
{
}
private void terminateCommands(Map cmd) {
Console.WriteLine("need user input");
var answer = Console.ReadLine();
Console.WriteLine("need user input again");
var answer2 = Console.ReadLine();
}
}
The 'list' method works just fine (called by terminateCommands), if I use my keyboard. So terminateCommands is called from the 'start' method. However, if I use VoiceRecognition (basic Mycrosoft.Speech), and terminateCommands is called from the eventHandler, every time, when the list method needs a user input, I have to press Enter, write sg, and then press Enter again. It seems like that there's another Console.ReadLine() before every Console.ReadLine() in the 'list' method.
ears.stop() just stops the voice recognition.
Map is a class, containing 2 strings(value,key).
I'm kinda confused. Any idea?
Related
I'm currently trying to understand events in C # by writing my own custom event. My goal is to trigger an event after the user enters something into the console. if the string equals to "--start" something should happen. I am currently not reaching my breakpoint in the constructor of my custom event. I hope you can help me.
here is my code:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Welcome to the BlackJack Console Game!{Environment.NewLine}" );
Console.WriteLine($"You get 500 Credits. The minimal Bet is 10 and the maximal 50!{Environment.NewLine}");
Console.WriteLine($"You can check your Creditcount with --credits{Environment.NewLine}");
Console.WriteLine($"To Start the Game write --start in the command line{Environment.NewLine}");
string userInput = Console.ReadLine();
Game game = new Game();
game.UserInput = userInput;
}
}
public class Game
{
public event EventHandler<UserInputEvent> UserWritedInput;
private string _userInput;
public string UserInput
{
get { return _userInput; }
set
{
_userInput = value;
OnUserWritedInput();
}
}
public void OnUserWritedInput()
{
UserWritedInput?.Invoke(this, new UserInputEvent(_userInput));
}
}
public class UserInputEvent : EventArgs
{
private string _userInput;
public UserInputEvent(string userInput)
{
this._userInput = userInput;
if (_userInput.Equals("--start"))
{
Console.WriteLine("game started!");
}
}
}
You haven't subscribed to the event:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Welcome to the BlackJack Console Game!{Environment.NewLine}" );
Console.WriteLine($"You get 500 Credits. The minimal Bet is 10 and the maximal 50!{Environment.NewLine}");
Console.WriteLine($"You can check your Creditcount with --credits{Environment.NewLine}");
Console.WriteLine($"To Start the Game write --start in the command line{Environment.NewLine}");
string userInput = Console.ReadLine();
Game game = new Game();
game.UserWritedInput += OnUserWritedInput;
game.UserInput = userInput;
}
private void OnUserWritedInput(object sender, UserInputEvent args)
{
if (args.UserInput.Equals("--start"))
{
Console.WriteLine("game started!");
}
}
}
public class UserInputEvent : EventArgs
{
public string UserInput {get;}
public UserInputEvent(string userInput)
{
UserInput = userInput;
}
}
Is this possible to make an app what will recognize if I tell eg. "top" or "back" in the background and will start some actions when find if I tell that.
I tested speech recognition when click on button and it shows google voice recognition.
Can I do that without click on button, with recognition in real time in the background?
Yes it is very possible, if you are using google voice recognition on android, what you have to do to get rid of the clicking button is to make your own SpeechRecognizer class and inherit it.
Here is the code I use on my apps :
public class CustomRecognizer : Java.Lang.Object, IRecognitionListener, TextToSpeech.IOnInitListener
{
private SpeechRecognizer _speech;
private Intent _speechIntent;
public string Words;
public CustomRecognizer(Context _context)
{
this._context = _context;
Words = "";
_speech = SpeechRecognizer.CreateSpeechRecognizer(this._context);
_speech.SetRecognitionListener(this);
_speechIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
_speechIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);
_speechIntent.PutExtra(RecognizerIntent.ActionRecognizeSpeech, RecognizerIntent.ExtraPreferOffline);
_speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1000);
_speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1000);
_speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 1500);
}
void startover()
{
_speech.Destroy();
_speech = SpeechRecognizer.CreateSpeechRecognizer(this._context);
_speech.SetRecognitionListener(this);
_speechIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
_speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1000);
_speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1000);
_speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 1500);
StartListening();
}
public void StartListening()
{
_speech.StartListening(_speechIntent);
}
public void StopListening()
{
_speech.StopListening();
}
public void OnBeginningOfSpeech()
{
}
public void OnBufferReceived(byte[] buffer)
{
}
public void OnEndOfSpeech()
{
}
public void OnError([GeneratedEnum] SpeechRecognizerError error)
{
Words = error.ToString();
startover();
}
public void OnEvent(int eventType, Bundle #params)
{
}
public void OnPartialResults(Bundle partialResults)
{
}
public void OnReadyForSpeech(Bundle #params)
{
}
public void OnResults(Bundle results)
{
var matches = results.GetStringArrayList(SpeechRecognizer.ResultsRecognition);
if (matches == null)
Words = "Null";
else
if (matches.Count != 0)
Words = matches[0];
else
Words = "";
//do anything you want for the result
}
startover();
}
public void OnRmsChanged(float rmsdB)
{
}
public void OnInit([GeneratedEnum] OperationResult status)
{
if (status == OperationResult.Error)
txtspeech.SetLanguage(Java.Util.Locale.Default);
}}
To use it on your activity, just create the class and call StartListening()
Look at my code pls. I have ThirdClass to triger an event. In Second Class I Handle that event. But how to handle that event in my Program Class. In this class I have no ThirdClass object to subscribe an event. Do I have to declerate another event in Second class In order to triger MyPurpose() method?
public class Program
{
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.LaunchSecondClass();
//A want to run this method when OnCounted event fired
//...
//myProgram.MyPurpose();
//...
}
public void MyPurpose()
{
Console.WriteLine("Program Class here!");
Console.ReadLine();
}
}
public class SecondClass
{
public void LaunchSecondClass()
{
ThirdClass third = new ThirdClass();
third.myEvent += this.OnCounted;
third.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
}
}
public class ThirdClass
{
public event EventHandler myEvent;
public void count()
{
for (int i = 0; i < 3; i++)
{
//Only for testing
Thread.Sleep(1000);
}
OnCounted();
}
protected virtual void OnCounted()
{
if (myEvent != null)
myEvent(this, EventArgs.Empty);
}
}
There are a lot of way to do what you want, as I said. The goal can be achieved, despite this, I highly recommend you to pay attention to #Glubus 's comment, .
Here's three of this ways:
Option 1 - Expose a new Event on SecondClass
public class SecondClass
{
public event EventHandler SecondEvent;
public void LaunchSecondClass()
{
ThirdClass third = new ThirdClass();
third.myEvent += this.OnCounted;
third.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
SecondEvent?.Invoke(); // Based on C# 6
}
}
And in your main program:
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.SecondEvent += MyPurpose;
second.LaunchSecondClass();
}
Option 2 - Expose the ThirdClass on SecondClass as a property
public class SecondClass
{
public ThirdClass ThirdClass { get; }
public SecondClass()
{
ThirdClass = new ThirdClass();
ThirdClass.myEvent += this.OnCounted;
}
public void LaunchSecondClass()
{
if(ThirdClass == null)
ThirdClass = new ThirdClass();
ThirdClass.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
}
}
And in your main program:
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.ThirdClass.myEvent += MyPurpose;
second.LaunchSecondClass();
}
Option 3 - Pass and Action (Delegate) to be executed by the SecondClass's method
public class SecondClass
{
public void LaunchSecondClass(Action action)
{
ThirdClass third = new ThirdClass();
third.myEvent += this.OnCounted;
if(action != null)
third.myEvent += (o, a) => action.Invoke();
third.count();
}
private void OnCounted(object sender, EventArgs e)
{
Console.WriteLine("Second Class Here.");
//Console.ReadLine();
}
}
And in your main program:
static void Main(string[] ars)
{
Program myProgram = new Program();
SecondClass second = new SecondClass();
second.LaunchSecondClass(MyPurpose);
}
Remember that there are no guarantees of a best practicle choice without know the real scenario where you're intent to apply it. So, maybe you must search up a design pattern to your problem and follow up the SOLID principles when planning your solutions
This is the best way to get a clean and efficient code .
Here's a good read about this topics:
Design Patterns
SOLID Principles
I Hope it help you, and sorry for my bad english
I'm searching for this for several hours now and was not able to find proper solution. I'm c# beginner.
I have a winforms app with a ListBox and a class that does some work and should run forever on separate thread. I want to push MyDataStruct to ListBox each time its created in WorkerClass.Work.
Later on, several WorkerClass instances should run simultaneously and I will have combobox to pick which instance data to feed to ListBox . Is it better to have WorkerClas return only single MyDataStruct and keep their queue in Form1 class or have a queue in each WorkerClass and exchange the entire queue with Form1 every time it changes?
is my void QueueToLb good way to add queue data to ListBox ?
thank you for your support.
public partial class Form1 : Form
{
Queue<MyDataStruct> qList;
MyDataStruct myDataStruct;
private void RunTask()
{
//how do I make MyLongTask to update either qList or myDataStuct
Task.Run(() =>
{
MyLongTask(0, 1000);
});
}
private void MyLongTask(int low, int high)
{
WorkerClass wc = new WorkerClass();
wc.Work(low,high);
}
private void QueueToLb()
{
//is this good way to update listbox from queue?
List<MyDataStruct> lstMds = qList.Reverse<MyDataStruct>().ToList<MyDataStruct>();
List<string> lstStr = new List<string>();
foreach (MyDataStruct m in lstMds)
{
lstStr.Add(m.ToString());
}
listBox1.DataSource = lstStr;
}
}
public class WorkerClass
{
Queue<MyDataStruct> qList; //not sure if its better to keep the queue here or in Form1
public WorkerClass()
{
qList = new Queue<MyDataStruct>();
}
public void Work(int low, int high) //does some work forever
{
while (true)
{
if (qList.Count > 11) qList.Dequeue();
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(low, high);
qList.Enqueue(mds);
Thread.Sleep(1000);
}
}
}
public class MyDataStruct
{
public DateTime dt;
public int num;
public override string ToString()
{
StringBuilder s = new StringBuilder();
s.Append(num.ToString());
s.Append(" - ");
s.Append(dt.ToShortDateString());
return s.ToString();
}
}
OK I think I figured how to use BackgroundWorker on this, I'll be happy if someone could verify it is correct
public partial class Form1 : Form
{
Queue<MyDataStruct> qList;
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(Bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
}
private void Form1_Load(object sender, EventArgs e)
{
qList = new Queue<MyDataStruct>(12);
}
private void button1_Click(object sender, EventArgs e)
{
bw.RunWorkerAsync();
}
private void MyLongTask(int low = 0, int high = 1000)
{
WorkerClass wc = new WorkerClass(bw);
wc.Work(low,high);
}
private void BindToLbWithQueue()
{
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(0, 1000);
qList.Enqueue(mds);
QueueToLb();
}
private void QueueToLb()
{
//is this good way to update listbox from queue?
List<MyDataStruct> lstMds = qList.Reverse<MyDataStruct>().ToList<MyDataStruct>();
List<string> lstStr = new List<string>();
foreach (MyDataStruct m in lstMds)
{
lstStr.Add(m.ToString());
}
listBox1.DataSource = lstStr;
}
#region worker
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
MyLongTask();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
qList = (Queue<MyDataStruct>)e.UserState;
QueueToLb();
}
#endregion
}
public class WorkerClass
{
Queue<MyDataStruct> qList; //not sure if its better to keep the queue here or in Form1
BackgroundWorker bw = null;
public WorkerClass(BackgroundWorker bw)
{
this.bw = bw;
qList = new Queue<MyDataStruct>();
}
public void Work(int low, int high) //does some work forever
{
while (true)
{
if (qList.Count > 11) qList.Dequeue();
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(low, high);
qList.Enqueue(mds);
bw.ReportProgress(0, qList);
Thread.Sleep(1000);
}
}
}
public class MyDataStruct
{
public DateTime dt;
public int num;
public override string ToString()
{
StringBuilder s = new StringBuilder();
s.Append(num.ToString());
s.Append(" - ");
s.Append(dt.ToShortDateString());
return s.ToString();
}
}
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.