Using event and delegates I have tried this.
public delegate void ColourChangerAction(string str);
public static event ColourChangerAction OnClicked;
void OnGUI() {
if(GUI.Button(new Rect(Screen.width/2, Screen.height-200, 50,50),texture1)){
if (OnClicked != null) {
OnClicked(string strr);///error here!!!!!!!!!!!!!!!!
}
}
but it is showing me the error Invalide expression term string. If I don't provide string pararmenter then it shows does not take 0 arugements.
well I'm new to delegates and events. what I'm missing.??
while in another class I am doing this for getting event call.
EventManager.OnClicked += ColourChanger;//registering
public void ColourChanger(string colourName)
{
print("Colour Changer " + colourName);
if (colourName == "Red")
{
print(gameObject.name);
hitInfo.collider.gameObject.renderer.material.color = Color.red;
}
else if (colourName == "Green")
{
print(gameObject.name);
hitInfo.collider.gameObject.renderer.material.color = Color.green;
}
else if (colourName == "Yellow")
{
print(gameObject.name);
hitInfo.collider.gameObject.renderer.material.color = Color.yellow;
}
}
You have to register that event
OnClickedE += new ColourChangerAction(OnGUI);
You have to re-define the methode signature for subscribe this event.
void OnGUI(string str)
{
if (1 == 1)
{
if (OnClickedE != null)
{
// OnClicked(string strr);///error here!!!!!!!!!!!!!!!!
}
}
}
Hence your Whole code will be like the following:
//Defining Delegate and event
public delegate void ColourChangerAction(string str);
public static event ColourChangerAction OnClicked;
// Method signature for subscribe the event
static void OnGUI(string str)
{
if (1 == 1)
{
if (OnClickedE != null)
{
// perform your action here
}
}
}
//register the event
OnClicked += new ColourChangerAction(OnGUI);
// Invoke the event dynamically
OnClicked.DynamicInvoke("Some string");
Update:
In your case please replace the code OnClicked(string strr); with
OnClicked.DynamicInvoke("red");// pass the color here
1) Do not use static events.
Negative Aspects/Bad Practice of Static Event in C#
2) Use Event Design
Do use System.EventHandler instead of manually creating new delegates to be used as event handlers.
Example:
static void Main(string[] args)
{
Counter c = new Counter( ... );
c.ThresholdReached += c_ThresholdReached;
...
}
static void c_ThresholdReached(object sender, ThresholdReachedEventArgs e)
{
Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached);
...
}
class Counter
{
...
public void Add(int x)
{
total += x;
if (total >= threshold)
{
ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
args.Threshold = threshold;
args.TimeReached = DateTime.Now;
OnThresholdReached(args);
}
}
protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
{
//Thread safe event
EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;
}
public class ThresholdReachedEventArgs : EventArgs
{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}
Related
I want to bubble up a message through classes. I used events and did this:
public class TopLevel{
public event EventHandler<string> Message;
public MiddleLevel mid;
public TopLevel()
{
mid.Message += (s, e) => { Message(s, e) };
}
}
public class MiddleLevel{
public event EventHandler<string> Message;
public BottomLevel bottom;
public MiddleLevel()
{
bottom.Message += (s, e) => { Message(s, e) };
}
}
public class BootomLevel{
public event EventHandler<string> Message;
public void DoSomething()
{
Message?.Invoke(this, "I did it.");
}
}
public class Handler{
public void HandleEvent(TopLevel top)
{
top.Message += PrintMessage;
}
public void PrintMessage(object sender, string message)
{
Console.WrteLine(message);
}
}
Also tried changing constructor to lambda expressions like this:
public class TopLevel{
public event EventHandler<string> Message;
public MiddleLevel mid;
public TopLevel()
{
mid.Message += (s, e) => { Message?.Invoke(s, e); };
}
}
public class MiddleLevel{
public event EventHandler<string> Message;
public BottomLevel bottom;
public MiddleLevel()
{
bottom.Message += (s, e) => { Message?.Invoke(s, e); };
}
}
public class BootomLevel{
public event EventHandler<string> Message;
public void DoSomething()
{
Message?.Invoke(this, "I did it.");
}
}
public class Handler{
public void HandleEvent(TopLevel top)
{
top.Message += PrintMessage;
}
public void PrintMessage(object sender, string message)
{
Console.WrteLine(message);
}
}
Codes above doesn't print any message. Even if I handle event in MiddleLevel class, I still get no message. I assume it is because message call is made in constructor (Even though linq quarries update themselves)? If I handle event in Handle class straight from BottomLevel class, it obviously - works. But I need to bubble the message up, I can't think of any other way to to this, because of how classes are constructed. Is it even possible to do what I have in mind with a standard Eventhandler class? If so than how? Should I just make an event class myself as in one of the sites i refered?
I refered to these sites:
What is the preferred way to bubble events?
https://www.carlosble.com/2016/04/event-bubbling-in-c/
Updated answer:
If you want 'Handler' to be triggered you will have to make sure that 'BottomLevel' falls within the hierarchy of the 'TopLevel' class being passed to the handler, this can be done via dependency injection (DI).
If 'BottomLevel' instantiates it's own classes (no DI) then it will not know about 'Handler', so handler will never be triggered.
If you comment out the DI setup and un-comment the 'BottomLevel' instantiation you can see the different behaviors.
class Program
{
static void Main(string[] args)
{
//setup the classes (dependency injection)
TopLevel topLevel = new TopLevel();
MiddleLevel middleLevel = new MiddleLevel(topLevel);
BottomLevel bottomLevel = new BottomLevel(middleLevel);
//set up the handler
Handler h = new Handler(topLevel);
//using this will not link to 'Handler' as there is no relation between this bottom and top
//BottomLevel bottomLevel = new BottomLevel();
//trigger the bottom class
bottomLevel.TriggerBottom();
//or
bottomLevel.DoSomething(null, "call from main");
Console.ReadLine();
}
}
public class Handler
{
TopLevel _topLevel;
public Handler(TopLevel topLevel)
{
if (topLevel != null)
_topLevel = topLevel;
_topLevel.Message += _topLevel_Message;
}
private void _topLevel_Message(object sender, string e)
{
Console.WriteLine($"handler triggered : {e}");
}
}
public class TopLevel
{
public event EventHandler<string> Message;
public TopLevel()
{ }
public void TriggerTop()
{
Message?.Invoke(this, "origin top");
}
public void DoSomething(object sender, string e)
{
Console.WriteLine($"Do something at top : {e}");
Message?.Invoke(this, e);
}
}
public class MiddleLevel
{
TopLevel _TopLevel;
public event EventHandler<string> Message;
public MiddleLevel(TopLevel topLevel) : this()
{
_TopLevel = topLevel;
}
public MiddleLevel()
{
if (_TopLevel == null)
_TopLevel = new TopLevel();
//subscribe this message to bottom message event method
Message += (s, e) => { _TopLevel.DoSomething(s, e); };
}
public void TriggerMiddle()
{
Message?.Invoke(this, "origin middle");
}
public void DoSomething(object sender, string e)
{
Console.WriteLine($"do something in middle : {e}");
//invoke the event(s)
Message?.Invoke(sender, e);
}
}
public class BottomLevel
{
MiddleLevel _MidLevel;
public event EventHandler<string> Message;
public BottomLevel(MiddleLevel midLevel) : this()
{
_MidLevel = midLevel;
}
public BottomLevel()
{
if (_MidLevel == null)
_MidLevel = new MiddleLevel();
////here you assign it
Message += (s, e) => { _MidLevel.DoSomething(s, e); };
}
public void TriggerBottom()
{
DoSomething(this, "origin bottom");
}
public void DoSomething(object sender, string e)
{
Console.WriteLine($"do something at bottom : {e}");
Message?.Invoke(sender, e);
}
}
I am creating a car simulator where I have a key that turns on an engine. The engine is tied to a specific key with a callback method which calls the OnEngineTurn method which raises the event. No matter what I do to the EventHandler, I it never works because it always is null. Here is the code below. I am relatively new to C# so any help is appreciated
public delegate void MyEventHandler(object sender, EventArgs e);
class Engine
{
public event MyEventHandler EngineTurn;
//raise the event
protected virtual void OnEngineTurn(EngineEventArgs e)
{
MyEventHandler engineTurn = EngineTurn;
if (engineTurn != null)
{
MessageBox.Show("Hello World");
engineTurn(this, e);
}
else
{
MessageBox.Show("Null");
}
}
public CarKey GetNewKey()
{
return new CarKey(new KeyCallBack(OnEngineTurn));
}
}
class EngineEventArgs : EventArgs
{
public string name { get; set; }
}
delegate void KeyCallBack(EngineEventArgs e);
class CarKey
{
//we need a way to hook the engine up to the car so we don't crank, but one car with one key
private KeyCallBack keyCallBack;
public CarKey(KeyCallBack callBackDelegate)
{
this.keyCallBack = new KeyCallBack(callBackDelegate);
}
public void TurnTheKey(EngineEventArgs e)
{
if (keyCallBack != null)
{
MessageBox.Show("A");
keyCallBack(e);
}
}
}
carKey = engine1.GetNewKey() should tie a specific key to a specific engine with a callback method that calls back to the EngineTurn Event.... carKey.TurnTheKey(engineEventArgs) is suppose to raise the event.... Below is the constructor for CarKey... I have it inside the Engine class for the callback method...
carKey = engine1.GetNewKey();
engineEventArgs = new EngineEventArgs();
carKey.TurnTheKey(engineEventArgs);
public CarKey GetNewKey()
{
return new CarKey(new KeyCallBack(OnEngineTurn));
}
Solved the problem
class Simulator
{
private Engine engine = new Engine();
private Transmission transmission;
CarKey carKey;
//public ObservableCollection<string> FanSays { get { return fan.FanSays; } }
//public ObservableCollection<string> PitcherSays { get { return pitcher.PitcherSays; } }
// public int Trajectory { get; set; }
//public int Distance { get; set; }
public Simulator()
{
transmission = new Transmission(engine);
carKey = engine.GetNewKey();
}
public async void StartSimulator()
{
EngineEventArgs engineEventArgs = new EngineEventArgs("America!");
await new MessageDialog("made it inside the start method").ShowAsync();
carKey.StartTheEngine(engineEventArgs);
}
}
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);
}
I am trying to fire the event handler assigned to my timer mock. How can I test this private method here?
public interface ITimer
{
void Start();
double Interval { get; set; }
event ElapsedEventHandler Elapsed;
}
Client class assigns an event handler to this object. I want to test the logic in this class.
_timer.Elapsed += ResetExpiredCounters;
And the assigned method is private
private void ResetExpiredCounters(object sender, ElapsedEventArgs e)
{
// do something
}
I want to have this event handler in my mock and run it somehow. How can I do this?
Update:
I realized I was raising the event before I assigned the event handler. I corrected that but I still get this error:
System.ArgumentException : Object of type 'System.EventArgs' cannot be converted
to type 'System.Timers.ElapsedEventArgs'.
I raise it like this:
_timer.Raise(item => item.Elapsed += null, ElapsedEventArgs.Empty);
or
_timer.Raise(item => item.Elapsed += null, EventArgs.Empty);
Both won't work.
Update:
Here's the thing that worked for me. Note that it's not useful if you are trying to pass info to event handler like Jon pointed out in comments. I am just using it to mock the wrapper for System.Timers.Timer class.
_timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);
In the end, this won't help at all if you need to use event arguments since it will be always null. However, it's the only way since ElapsedEventArgs has only an internal constructor.
ElapsedEventArgs has a private constructor and can not be instantiated.
If you use:
timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);
Then the handler will recevie a null parameter and lose its SignalTime property:
private void WhenTimerElapsed(object sender, ElapsedEventArgs e)
{
// e is null.
}
You might want this parameter in some cases.
To solve this and make it more testable, I also created a wrapper for the ElapsedEventArgs, and made the interface use it:
public class TimeElapsedEventArgs : EventArgs
{
public DateTime SignalTime { get; private set; }
public TimeElapsedEventArgs() : this(DateTime.Now)
{
}
public TimeElapsedEventArgs(DateTime signalTime)
{
this.SignalTime = signalTime;
}
}
public interface IGenericTimer : IDisposable
{
double IntervalInMilliseconds { get; set; }
event EventHandler<TimerElapsedEventArgs> Elapsed;
void StartTimer();
void StopTimer();
}
The implementation will simply fire its own event getting the data from the real timer event:
public class TimerWrapper : IGenericTimer
{
private readonly System.Timers.Timer timer;
public event EventHandler<TimerElapsedEventArgs> Elapsed;
public TimeSpan Interval
{
get
{
return this.timer.Interval;
}
set
{
this.timer.Interval = value;
}
}
public TimerWrapper (TimeSpan interval)
{
this.timer = new System.Timers.Timer(interval.TotalMilliseconds) { Enabled = false };
this.timer.Elapsed += this.WhenTimerElapsed;
}
private void WhenTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
var handler = this.Elapsed;
if (handler != null)
{
handler(this, new TimeElapsedEventArgs(elapsedEventArgs.SignalTime));
}
}
public void StartTimer()
{
this.timer.Start();
}
public void StopTimer()
{
this.timer.Stop();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.timer.Elapsed -= this.WhenTimerElapsed;
this.timer.Dispose();
}
this.disposed = true;
}
}
}
Now, you can simplify and improve the mock of this event:
timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs());
var yesterday = DateTime.Now.AddDays(-1);
timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs(yesterday));
Less code to write, easier to work with and completely decoupled from the framework.
The Moq QuickStart guide has a section on events. I think you'd use
mock.Raise(m => m.Elapsed += null, new ElapsedEventArgs(...));
Dealt with this recently, you can construct an ElapsedEventArgs using reflection:
public ElapsedEventArgs CreateElapsedEventArgs(DateTime signalTime)
{
var e = FormatterServices.GetUninitializedObject(typeof(ElapsedEventArgs)) as ElapsedEventArgs;
if (e != null)
{
var fieldInfo = e.GetType().GetField("signalTime", BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
fieldInfo.SetValue(e, signalTime);
}
}
return e;
}
This way you can continue using the original ElapsedEventHandler delegate
var yesterday = DateTime.Now.AddDays(-1);
timer.Raise(item => item.Elapsed += null, CreateElapsedEventArgs(yesterday));
Could do something like this to wrap your Timer
public class FakeTimer : IMyTimer
{
private event ElapsedEventHandler elaspedHandler;
private bool _enabled;
public void Dispose() => throw new NotImplementedException();
public FakeTimer(ElapsedEventHandler elapsedHandlerWhenTimeFinished, bool startImmediately)
{
this.elaspedHandler = elapsedHandlerWhenTimeFinished;
_enabled = startImmediately;
}
public void Start() => _enabled = true;
public void Stop() => _enabled = false;
public void Reset() => _enabled = true;
internal void TimeElapsed()
{
if (this._enabled)
elaspedHandler.Invoke(this, new EventArgs() as ElapsedEventArgs);
}
}
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()); };
}
}