I have 2 C# classes one of them has a string delegate and the other subscribes an function to that delegate.
My question is I want to combine the two called string functions from the delegate instead of choosing the return value between them randomly
delgatesystem.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class delegatesystem : MonoBehaviour {
public delegate string MyDelegate();
public static event MyDelegate MyEvent;
string GameObjectsNames = "";
void Update ()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (MyEvent != null)
{
GameObjectsNames += MyEvent();
}
}
}
}
delegatesave.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class delegatesave : MonoBehaviour {
void Start ()
{
delegatesystem.MyEvent += DelegateFunction;
}
string DelegateFunction()
{
return gameObject.name;
}
}
note: the delgatesave.cs is attached to 2 gameobjects.
First of all, creating events with non-void delegates is an antipattern. Events are typically used with potentially more subscribers.
If a non-void multicast delegate is invoked with multiple subscribers, always the return value of the last subscribed method is returned.
But after all you can do something like this:
string[] objectNames = MyEvent.GetInvocationList().Cast<MyDelegate>().Select(del => del()).ToArray();
However, a better solution would be to use more conventional events:
public class PopulateNamesEventArgs : EventArgs
{
private List<string> names = new List<string>();
public string[] Names => names.ToArray();
public void AddName(string name) => names.Add(name);
}
And then in your class:
public event EventHandler<PopulateNamesEventArgs> MyEvent;
protected virtual void OnMyEvent(PopulateNamesEventArgs e) => MyEvent?.Invoke(this, e);
Invokation:
var e = new PopulateNamesEventArgs();
OnMyEvent(e);
string[] objectNames = e.Names; // the result is now populated by the subscribers
Subscription:
void Start()
{
delegatesystem.MyEvent += DelegateFunction;
}
void DelegateFunction(object sender, PopulateNamesEventArgs e)
{
e.AddName(gameObject.name);
}
Related
Zero to many methods are added from on or more methods on classes
I pass a Guid as arg
Currently, I have subscribed the EventHandler as a wrapped action
When unsubscribing the Action I expect to still be able to fire the other delegates.
public class SomeHandler : IDisposable
{
private event EventHandler<Guid>? _onChangedSync;
public void AddOnChanged(Action<Guid> eventDelegate)
=> _onChangedSync += (_, arg) => eventDelegate(arg);
public void UnsubscribeOnChanged(Action<Guid> actionEvent)
{
var invocations = _onChangedSync?.GetInvocationList().Cast<EventHandler<Guid>>().ToList();
var found = invocations?.FirstOrDefault(del => del.Method == actionEvent.Method);
_onChangedSync -= found;
}
public void Change(Guid clinicId)
{
if (_onChangedSync != null)
{
_onChangedSync.Invoke(this, clinicId);
}
}
public void Dispose()
{
if (_onChangedSync != null)
{
foreach (var e in _onChangedSync.GetInvocationList().Cast<EventHandler<Guid>>())
{
_onChangedSync -= e;
}
}
GC.SuppressFinalize(this);
}
}
This is the unit test I use.
Subscribe 2 separate methods/actions
Unsubscribe 1 of the methods/actions
Expect only one of them to be called
Currently _callCount == 2 after run test
public class SomeTests
{
private int _callCount;
private void ListenAction1(Guid arg)
=> _callCount++;
private void ListenAction2(Guid arg)
=> _callCount++;
[Fact]
public void Unsubscribed_events_must_not_be_invoked()
{
// Given
var testArg = Guid.Parse("54b9bfde-ef2b-4aab-b705-4c59371c0e4f");
using var state = new SomeHandler();
state.AddOnChanged(ListenAction1);
state.AddOnChanged(ListenAction2);
state.UnsubscribeOnChanged(ListenAction1);
// When
state.Change(testArg);
// Then
Assert.Equal(expected: 1, _callCount);
}
}
How do I remove one of the subscribed from the EventHandler by passing the action again and comparing it with the rest of the invocations?
I have to access data from an event in another class.
In that class things are like this:
namespace MavLink
{
public class Mavlink
{
...
public event PacketReceivedEventHandler PacketReceived;
...
private void ProcessPacketBytes(byte[] packetBytes, byte rxPacketSequence)
{
...
if (PacketReceived != null)
{
PacketReceived(this, packet);
}
...
}
}
public delegate void PacketReceivedEventHandler(object sender, MavlinkPacket e);
}
And in the main I've tried to do like this:
...
m.ParseBytes(newlyReceived);
m.PacketReceived += (sender, e) => {
Console.WriteLine(e.SystemId);
Console.WriteLine(e.ComponentId);
Console.WriteLine(e.SequenceNumber);
Console.WriteLine(e.TimeStamp);
Console.WriteLine(e.Message);
};
But it doesn't seem work.
Thank you for your help.
Edit:
It compiles without errore but nothing is printed on the console. I don't know how to check if the event has been rised though.
Well on this what I read I have created a usual Event that will give you some data to access.
We start with creating the event.
public delegate void PacketReceivedEventHandler(var pPacket);
public event PacketReceivedEventHandler PacketReceived;
I put a var in there cause I didn't exactly saw what you are "delivering". Just change it into whatever you need.
So, lets continue. Place this when the Event needs to be triggered.
if (Mavlink.PacketReceived != null)
Mavlink.PacketReceived(YourPackage);
YourPackage is whatever you want to deliver.
But you need to subscribe a event to do stuff with it.
Mavlink.PacketReceived += Mavlink_PacketReceived;
C# usually created a class if you double tab after the +=. But here is the class I created.
private void Mavlink_PacketReceived(var pPacket)
{
if(pPacket != null)
{
Console.WriteLine(pPacket.SystemId);
Console.WriteLine(pPacket.ComponentId);
Console.WriteLine(pPacket.SequenceNumber);
Console.WriteLine(pPacket.TimeStamp);
Console.WriteLine(pPacket.Message);
}
}
I dont know what comes after that in your code, but make sure that there will be something to make you command line wait so it wont close after firing that.
I made an example, which works fine, hope it helps. Replace EventHandler by your PacketRecievedEventHandler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var sender = new Sender();
var reciever = new Reciever(sender);
sender.ProcessPacketBytes(null, byte.MaxValue);
Console.ReadLine();
}
}
/// <summary></summary>
public class Sender
{
private readonly object _objectLock = new object();
public event EventHandler PacketReceived
{
add
{
lock (_objectLock)
{
PacketRecievedEvent += value;
}
}
remove
{
lock (_objectLock)
{
PacketRecievedEvent -= value;
}
}
}
private event EventHandler PacketRecievedEvent;
public void ProcessPacketBytes(byte[] packetBytes, byte rxPacketSequence)
{
EventHandler handler = this.PacketRecievedEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
public class Reciever
{
public Reciever(Sender sendertest)
{
sendertest.PacketReceived += (sender, e) =>
{ Console.WriteLine(e.GetType()); };
}
}
}
I dont know how to make a command run then the timer stops
here is the code :
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using System.Threading;
using System;
public class introtogamescript : MonoBehaviour
{
public class Example
{
public static void Main()
{
// Create an instance of the Example class, and start two
// timers.
Example ex = new Example();
ex.StartTimer(7000);
}
public void StartTimer(int dueTime)
{
Timer t = new Timer(new TimerCallback(TimerProc));
t.Change(dueTime, 0);
}
private void TimerProc(object state)
{
// The state object is the Timer object.
Timer t = (Timer)state;
t.Dispose();
SceneManager.LoadScene(2);
}
}
}
Thanks for the help.
You can use coroutines like this:
public void WaitAndExecute(float timer, Action callBack = null)
{
StartCoroutine(WaitAndExecute_CR(timer,callBack));
}
IEnumerator WaitAndExecute_CR(float timer, Action callBack = null)
{
yield return new WaitForSeconds(timer);
if (callBack != null)
{
callBack();
}
}
void Start()
{
WaitAndExecute(5,() => {Debug.Log("This will be printed after 5 seconds");});
WaitAndExecute(10,JustAnotherMethod);
}
void JustAnotherMethod()
{
Debug.Log("This is another way to use callback methods");
}
When you start the timer use lambdas or anonymous delegate - this way you will have the timer as closure.
You can also use Invoke(string methodName, float callDelay)
Example:
void SomeMethod()
{
Invoke("OtherMethod",3.5f);
}
//It should be called 3.5 seconds after SomeMethod
void OtherMethod()
{
print(something);
}
And you can not invoke any parameter
I have created a very simple dummy program to understand Delegates and events. In my below program I am simple calling a method. When I call a method, five methods are automatically called with the help of delegates and events.
Kindly take a look at my program and do let me know where I am wrong or right as this is my first time using delegates and events.
using System;
namespace ConsoleApplication1
{
public delegate void MyFirstDelegate();
class Test
{
public event MyFirstDelegate myFirstDelegate;
public void Call()
{
Console.WriteLine("Welcome in Delegate world..");
if (myFirstDelegate != null)
{
myFirstDelegate();
}
}
}
class AttachedFunction
{
public void firstAttachMethod()
{
Console.WriteLine("ONE...");
}
public void SecondAttachMethod()
{
Console.WriteLine("TWO...");
}
public void thirdAttachMethod()
{
Console.WriteLine("THREE...");
}
public void fourthAttachMethod()
{
Console.WriteLine("FOUR...");
}
public void fifthAttachMethod()
{
Console.WriteLine("FIVE...");
}
}
class MyMain
{
public static void Main()
{
Test test = new Test();
AttachedFunction attachedFunction = new AttachedFunction();
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.firstAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.SecondAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.thirdAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.fourthAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.fifthAttachMethod);
test.Call();
Console.ReadLine();
}
}
}
Events are implemented using Delegates. That said by convention events take the form of:
void EventHandler(Object sender, EventArgs args);
EventHandler is actually a delegate defined in .Net. EventArgs is a class in .Net that acts as a placeholder to pass additional information. If you have additional information you would create a class that derived from EventArgs and contained properties for the additional data; therefore you would create your own delegate like so:
void MyEventHandler(Object sender, MyEventArgs args);
Microsoft has a tutorial on events here and also describes defining and raising events here
This is a common pattern with dealing with events:
// define the delegate
public delegate void CustomEventHandler(object sender, CustomEventArgs e);
// define the event args
public class CustomEventArgs : EventArgs
{
public int SomeValue { get; set; }
public CustomEventArgs( int someValue )
{
this.SomeValue = someValue;
}
}
// Define the class that is raising events
public class SomeClass
{
// define the event
public event CustomEventHandler CustomEvent;
// method that raises the event - derived classes can override this
protected virtual void OnCustomEvent(CustomEventArgs e)
{
// do some stuff
// ...
// fire the event
if( CustomEvent != null )
CustomEvent(this, e);
}
public void SimulateEvent(int someValue)
{
// raise the event
CustomEventArgs args = new CustomEventArgs(someValue);
OnCustomEvent(args);
}
}
public class Main
{
public static void Main()
{
SomeClass c = new SomeClass();
c.CustomEvent += SomeMethod;
c.SimulateEvent(10); // will cause event
}
public static void SomeMethod(object sender, CustomEventArgs e)
{
Console.WriteLine(e.SomeValue);
}
}
Try putting the line
public delegate void MyFirstDelegate();
inside the Test class.
Also, use the Invoke function on the event instead, i.e.
myFirstDelegate.Invoke();
I would like to create a method that takes an event as an argument and adds eventHandler to it to handle it properly. Like this:
I have two events:
public event EventHandler Click;
public event EventHandler Click2;
Now I would like to pass a particular event to my method like this (pseudocode):
public AttachToHandleEvent(EventHandler MyEvent)
{
MyEvent += Item_Click;
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool.Click);
Is it possible?
I've noticed that this code worked fine, and returned to my project and noticed that when I pass an event declared in my class, it works, but when I pass event from other class it still does not work.
What I get is this error:
The event
'System.Windows.Forms.ToolStripItem.Click'
can only appear on the left hand side
of += or -=
My original answer was suitable from within the class that defined the event, but you've since updated your question to reflect that you wish to accomplish this from outside the defining class, so I've stricken that.
Only the class that defines an event can refer to the implicit delegate variable that the event uses. From outside that class, you only have access to the add and remove methods, via += and -=. This means that you can't do what you're asking, directly. You can, however, use a functional approach.
class A{
public event EventHandler Event1;
public void TriggerEvent1(){
if(Event1 != null)
Event1(this, EventArgs.Empty);
}
}
class B{
static void HandleEvent(object o, EventArgs e){
Console.WriteLine("Woo-hoo!");
}
static void AttachToEvent(Action<EventHandler> attach){
attach(HandleEvent);
}
static void Main(){
A a = new A();
AttachToEvent(handler=>a.Event1 += handler);
a.TriggerEvent1();
}
}
I did it like this:
public AttachToHandleEvent(Object obj, string EventName)
{
EventInfo mfi = obj.GetType().GetEvent(EventName);
MethodInfo mobj = mfi.GetAddMethod();
mobj.Invoke(obj, new object[] { Item_Click});
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool "Click");
Thank you all for advice. This solution could not be done without your help.
It's not possible. You can use a delegate instead of an event if that meets your needs.
Just write tool.Click += Item_Click;
Edit: From MSDN "Events can only be invoked from within the class or struct where they (it) are declared". So what you are trying to do is not possible. Could you elaborate more on your needs? Why would you want to pass an event as a parameter?
delegate void doIt(object sender, object data);
event doIt OnDoIt;
void add(doIt theDel)
{
OnDoIt += theDel;
}
void doIt1(object a, object b)
{
}
void doIt2(object a, object b)
{
}
void add()
{
add(doIt1);
add(doIt2);
}
Your question suggests that you got some mechanisms wrong:
You can't pass events!
You most probably want to pass a function as a parameter, so the calling method will call that other method at some point. In technical terms this is a delegate. I suggest using the already defined Action class. Here's an example snippet:
void MyFunction (string otherArguments, Action onFinished){
...
if (onFinished != null)
onFinished.Invoke();
}
The nice thing about this is that when calling MyFunction you can declare the Action using the inline syntax:
MyFunction("my other argument", ()=>{
///do stuff here, which will be execuded when the action is invoked
});
I pass functions/methods (instead of events) like this:
class A
{
public void something()
{
var myAction =
new Action<object, object>((sender, evArgs) => {
MessageBox.Show("hiii, event happens " + (evArgs as as System.Timers.ElapsedEventArgs).SignalTime);
});
B.timer(myAction);
}
}
class B
{
public static void timer( Action<object, System.Timers.ElapsedEventArgs> anyMethod)
{
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(anyMethod);
myTimer.Interval = 2000;
myTimer.Start();
}
}
Giving an update to this question with an object oriented solution.
Instead of using an Action<EventHandler> that registers the event, you could create an object handling that for you
public class AEvent
{
private readonly A aInstance;
private AEvent(A instance) {
aInstance = instance;
}
public void Add(EventHandler eventHandler)
=> a.Event1 += eventHandler;
public void Remove(EventHandler eventHandler)
=> a.Event1 -= eventHandler;
public EventHandler Invoke => aInstance.Event1;
}
Then later on use that object like this:
static void Main(){
A a = new A();
AEvent aEvent = new AEvent(A)
aEvent.Add(handler);
a.Invoke();
}
One approach I haven't seen here would be to create an object which has delegates for subscribe and unsubscribe. Here is a complete example program.
class Program
{
private event EventHandler<EventArgs> eventHandler;
public static void Main(string[] args)
{
Program program = new Program();
Thing thing = new Thing(new EventWrapper<EventArgs>(
delegate(EventHandler<EventArgs> handler) { program.eventHandler += handler; },
delegate(EventHandler<EventArgs> handler) { program.eventHandler -= handler; }
));
// events are fired
program.eventHandler?.Invoke(program, EventArgs.Empty);
thing.Unsubscribe();
}
}
class Thing
{
private readonly Action<EventHandler<EventArgs>> _unsubscribeEventHandler;
public Thing(EventWrapper<EventArgs> eventHandler)
{
this._unsubscribeEventHandler = eventHandler.Unsubscribe;
eventHandler.Subscribe?.Invoke(OnEvent);
Console.WriteLine("subscribed");
}
private void OnEvent(object? sender, EventArgs e)
{
Console.WriteLine("event fired");
}
public void Unsubscribe()
{
_unsubscribeEventHandler?.Invoke(OnEvent);
Console.WriteLine("unsubscribed");
}
}
class EventWrapper<T> where T : EventArgs
{
public Action<EventHandler<T>> Subscribe { get; private set; }
public Action<EventHandler<T>> Unsubscribe { get; private set; }
public EventWrapper(Action<EventHandler<T>> subscribe, Action<EventHandler<T>> unsubscribe)
{
Subscribe = subscribe;
Unsubscribe = unsubscribe;
}
}
In this example, we created a new class called EventWrapper<T> which wraps delegates for += and -= and exposes them with Subscribe and Unsubscribe methods. The delegates will need to be created by the class which created the event.