How do I create a public action, but keep the invoke private? - c#

Let say I have an action like this:
public Action OnSomeAction;
I would like to be able to subscribe to this action from outside of the class, but not be able to invoke it:
OnSomeAction.Invoke();
Is there a way to do this without making the action private and creating methods for subscribing and unsubscribing like this:
private Action _someAction;
public void Subscribe(Action listener)
{
_someAction += listener;
}
public void Unsubscribe(Action listener)
{
_someAction -= listener;
}
private void Invoke()
{
_someAction.Invoke();
}

Are you looking for event?
public class MyClass {
// Let firing event be private
private void onMyAction() {
Action action = MyAction;
if (action != null)
action();
}
public void FireDemo() {
onMyAction();
}
//TODO: I've put Action, but, probably, EventHandler will be a better choice
// while event itself (subscribe / unsubscribe) being public
public event Action MyAction;
}
Demo:
MyClass myClass = new MyClass();
var first = () => {Console.WriteLine("I'm the first")};
var second = () => {Console.WriteLine("I'm the second")};
var none = () => {Console.WriteLine("I should not fire")};
myClass.MyAction += first;
myClass.MyAction += second;
myClass.MyAction += none;
// Unsubsribe
myClass.MyAction -= none;
// Direct attempt will NOT compile:
// myClass.MyAction();
myClass.FireDemo();

Although this looks a lot like events. You can achieve it using Action too. Consider following code which is almost same as you suggested:
private List<Action> _someActionList = new List<Action>();
public void Subscribe(Action listener)
{
_someActionList.Add(listener);
}
public void Unsubscribe(Action listener)
{
_someActionList.Remove(listener);
}
private void Invoke()
{
foreach(action in _someActionList)
{
action();
}
}
I hope this is exactly what you want to do. If not then please elaborate further.

Related

Can I use a action to pass a value to a method parameter?

I will try my best to explain what I am asking.
Imagine I have a Action in one class:
public Action Something;
And in another class I subscribe a method ot it with a parameter
private void Foo(int num)
{
Debug.Log(num);
}
OneClass.Something += Foo;
So basically I want the parameter of Foo to be something that the OneClass Passes. Is that something that exists in C#?
Is this what you are trying to do:
public class Bar
{
public event Action<int> Something;
protected void OnSomething(int arg)
{
Something?.Invoke(arg);
}
}
class Program
{
static void Main(string[] args)
{
var bar = new Bar();
bar.Something += Foo;
}
static void Foo(int x)
{
Debug.WriteLine(x);
}
}
What I have above is an event .Something defined with an event handler of Action<int> function prototype. Action<T> represents methods of the form void f(T item).
Then I subscribe to the event with bar.Something += Foo
Finally, I define the method OnSomething() in order to allow the class Bar to trigger the event when needed.

Getting callback from class function

I am trying to figure out how to wait for a callback from a class function (I guess).
My class looks like this:
public class DataLogOut
{
public delegate void OnLogoutResponse(ResponseData data);
public event OnLogoutResponse onLogoutResponse;
public static void LogoutPlayer()
{
new EndSessionRequest().SetDurable(true).Send((response) => {
if (!response.HasErrors)
{
GS.Reset();
if (onLogoutResponse != null)
{
onLogoutResponse(new ResponseData()
{
//data = response
});
}
}
else
{
if (onLogoutResponse != null)
{
onLogoutResponse(new ResponseData()
{
errors = response.Errors,
hasErrors = response.HasErrors
});
}
}
});
}
}
Now I am calling the "LogOutPlayer" like this:
public void LogOut()
{
DataLogOut.LogoutPlayer();
DataLogOut.onLogoutResponse += onLogoutResponse;
}
Now i am getting errors in both scripts.
In the first I am getting: Delegate invocation can be simplified
... and in the second script I am not allowed to do this:
DataLogOut.onLogoutResponse += onLogoutResponse;
Really hop someone can help me with this and thanks in advance :-)
There are several issues in your code. Please see comments:
public class DataLogOut
{
// No need for this, we will use "EventHandler"
// public delegate void OnLogoutResponse(ResponseData data);
//public event OnLogoutResponse onLogoutResponse; -> replaced by
public event EventHandler<ResponseData> onLogoutResponse;
// Convenience Method to fire the event
protected virtual void OnLogoutResponse( ResponseData data )
{
var handler = onLogoutResponse;
if( handler != null ){
handler( this, data );
}
}
// Let's simplify it by making it non-static
//public static void LogoutPlayer()
public void LogoutPlayer
{
new EndSessionRequest().SetDurable(true).Send((response) => {
if (!response.HasErrors)
{
GS.Reset();
OnLogoutResponse(new ResponseData()
{
//data = response
});
}
else
{
OnLogoutResponse(new ResponseData()
{
errors = response.Errors,
hasErrors = response.HasErrors
});
}
});
}
}
Usage:
public void LogOut()
{
// I made it non-static, so we need an instance ...
var logout = new DataLogout();
// first register for the event, then have it fired.
logout.onLogoutResponse += onLogoutResponse;
// ^-- You tried to register the handler on the class. Which failed,
// because the event was not static.
logout.LogoutPlayer();
}
// the handler's signature now must look like this:
public void onLogoutResponse( object sender, ResponseData data ){
// your code here
}
If you want to keep it static, then make the event static, too:
public static event EventHandler<ResponseData> onLogoutResponse;
then you need to make the convenience event trigger static, too
protected static void OnLogoutResponse( ResponseData data )
{
var handler = onLogoutResponse;
if( handler != null ){
handler( typeof(DataLogout), data ); // cannot use "this", of course in static context.
}
}
and then can use it as in your example:
public void LogOut()
{
// first register for the event, then have it fired.
DataLogout.onLogoutResponse += onLogoutResponse;
// ^-- You tried to register the handler on the class. Which failed,
// because the event was not static.
DataLogout.LogoutPlayer();
}
// the handler's signature now must look like this:
public void onLogoutResponse( object sender, ResponseData data ){
// your code here
}

C# One Time (Fire once) Events Implementation

I'm interested creating an event handling object that you can subscribe for one time execution only and then the action is automatically unsubscribed
Is there similar native functionality in .NET?
Here is what works for me right now:
public class CustomTimer
{
private event Action OneSecond;
private readonly Timer timer;
// Registered actions that should be called only once
private readonly ICollection<Action> oneOffs;
public CustomTimer()
{
this.timer = new Timer { Interval = 1000 };
this.timer.Elapsed += this.OnOneSecond;
this.oneOffs = new HashSet<Action>();
}
public bool IsRunning => this.timer.Enabled;
public void Start()
{
this.timer.Start();
}
public void Stop()
{
this.timer.Stop();
}
public void Subscribe(Action callback)
{
this.OneSecond += callback;
}
public void SubscribeOnce(Action callback)
{
this.oneOffs.Add(callback);
this.Subscribe(callback);
}
public void Unsubscribe(Action callback)
{
this.OneSecond -= callback;
this.oneOffs.Remove(callback);
}
protected virtual void OnOneSecond(object sender, ElapsedEventArgs elapsedEventArgs)
{
this.OneSecond?.Invoke();
this.UnsubscribeOneOffs();
}
private void UnsubscribeOneOffs()
{
if (this.oneOffs.Count > 0)
{
foreach (var action in this.oneOffs)
{
this.OneSecond -= action;
}
this.oneOffs.Clear();
}
}
}
Here the events are set to execute every second.
How can I use similar strategy in other object that trigger events unpredictably
and prevent events execution while the UnsubscribeOneOffs() method is running.
Should I use some kind of lock?
There is no need to register one time actions as OneSecond event handlers. Just keep them in a separate list.
public class CustomTimer
{
List<Action> _oneTimeActions = new List<Action>();
public void SubscribeOnce(Action handler)
{
lock(_oneTimeActions)
{
_oneTimeActions.Add(handler);
}
}
protected virtual void OnOneSecond(object sender, ElapsedEventArgs elapsedEventArgs)
{
// get a local copy of scheduled one time items
// removing them from the list.
Action[] oneTimers;
lock(_oneTimeActions)
{
oneTimers = _oneTimeActions.ToArray();
_oneTimeActions.Clear();
}
// Execute periodic events first
this.OneSecond?.Invoke();
// Now execute one time actions
foreach(var action in oneTimers)
{
action();
}
}
}

Why is a "forwarded" event not raised when assigning a method group but is when assigning a delegate?

Given the following code:
public delegate void Signal();
public static class SignalExtensions
{
public static void SafeInvoke(this Signal signal)
{
Signal copy = signal;
if (copy != null)
{
copy();
}
}
}
public class RootEventSource
{
public event Signal RootEvent;
public void Raise()
{
this.RootEvent.SafeInvoke();
}
}
public class EventForwarder
{
private readonly RootEventSource rootEventSource;
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
// this is the critical part
this.rootEventSource.RootEvent
+= () => this.AnotherEvent.SafeInvoke();
}
public event Signal AnotherEvent;
// just an example of another method which is using the root event source
public override string ToString()
{
return this.rootEventSource.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var rootEventSource = new RootEventSource();
var eventForwarder = new EventForwarder(rootEventSource);
eventForwarder.AnotherEvent += HandleAnotherEvent;
rootEventSource.Raise();
Console.WriteLine("done");
Console.ReadKey();
}
private static void HandleAnotherEvent()
{
Console.WriteLine("received AnotherEvent");
}
}
This results in the output:
received AnotherEvent
done
Now I make a slight change to the implementation of EventForwarder to use a method group for forwarding the event:
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
}
The output becomes:
done
So AnotherEvent is not raised.
Until now i would have considered the two lines:
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
this.rootEventSource.RootEvent += () => this.AnotherEvent.SafeInvoke();
as being equivalent. It seems they're not.
So what is the difference? Plus why is the event not being raised?
PS: while usually R# suggests to replace () => this.AnotherEvent.SafeInvoke(); by this.AnotherEvent.SafeInvoke it doesn't do so here. So apparently it knows that it should not do it here.
When you assign a method group to event like this:
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
you in fact create a delegate from method SignalExtensions.SafeInvoke which as a parameter takes your this.AnotherEventdelegate object. Since it is initially null, you create a delegate with null parameter. This null value will of course never change, since delegates are immutable.
If you want to forward an event you should maybe do it like this:
public class EventForwarder
{
private readonly RootEventSource rootEventSource;
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
}
public event Signal AnotherEvent
{
add { this.rootEventSource.RootEvent += value; }
remove { this.rootEventSource.RootEvent -= value; }
}
}

C# Threading Mechanism

Let's say I have an exposed interface as such:
interface IMyService
{
MyResult MyOperation();
}
This operation is synchronous and returns a value.
My implemented interface has to do the following:
Call an asynchronous method
Wait for event #1
Wait for event #2
This is due to a 3rd party COM object I am working with.
This code looks similar to the following
public MyResult MyOperation()
{
_myCOMObject.AsyncOperation();
//Here I need to wait for both events to fire before returning
}
private void MyEvent1()
{
//My Event 1 is fired in this handler
}
private void MyEvent2()
{
//My Event 2 is fired in this handler
}
My two events can happen in either order, it is quite random.
What is the proper threading mechanism I can use to synchronize this? I was using ManualResetEvent before I had to start waiting for the second event, and have not seen an easy way to use it for both events. These 2 events set variables that allow me to create the return value for MyOperation().
Any ideas on a good implementation for this? I have no control over the way the 3rd party object is implemented.
Two ManualResetEvents should do the trick for you. Just initialize them to false before you call the _myCOMObject.AsyncOperation(). Like this:
private ManualResetEvent event1;
private ManualResetEvent event2;
public MyResult MyOperation()
{
event1 = new ManualResetEvent(false);
event2 = new ManualResetEvent(false);
_myCOMObject.AsyncOperation();
WaitHandle.WaitAll(new WaitHandle[] { event1, event2 });
}
private void MyEvent1()
{
event1.Set();
}
private void MyEvent2()
{
event2.Set();
}
Edit
Thanks for the comments. I've changed the wait call to use WaitAll
My implementation example is as follows:
namespace ConsoleApplication1
{
class Program
{
private static WaitHandle[] waitHandles;
private static event EventHandler Evt1;
private static event EventHandler Evt2;
static void Main(string[] args)
{
waitHandles = new WaitHandle[]{
new ManualResetEvent(false),
new ManualResetEvent(false)
};
Evt1 += new EventHandler(Program_Evt1);
Evt2 += new EventHandler(Program_Evt2);
OnEvt1();
OnEvt2();
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Finished");
Console.ReadLine();
}
static void Program_Evt2(object sender, EventArgs e)
{
Thread.Sleep(2000);
((ManualResetEvent)waitHandles[0]).Set();
}
static void Program_Evt1(object sender, EventArgs e)
{
((ManualResetEvent)waitHandles[1]).Set();
}
static void OnEvt1()
{
if (Evt1 != null)
Evt1(null, EventArgs.Empty);
}
static void OnEvt2()
{
if (Evt2 != null)
Evt2(null, EventArgs.Empty);
}
}
}
I make it sleep for the purposes of this example and the WaitAll functionality
Cheers,
Andrew
P.S. another example would be using AsyncCallback, really quick and dirty example, but gives you more keys to open the door with :-) . Hope this helps!!
namespace ConsoleApplication1
{
class Program
{
private static WaitHandle[] waitHandles;
private static event EventHandler Evt1;
private static event EventHandler Evt2;
static void Main(string[] args)
{
waitHandles = new WaitHandle[]{
new ManualResetEvent(false),
new ManualResetEvent(false)
};
var callabck1 = new AsyncCallback(OnEvt1);
var callabck2 = new AsyncCallback(OnEvt2);
callabck1.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[0]));
callabck2.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[1]));
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Finished");
Console.ReadLine();
}
static void OnEvt1(IAsyncResult result)
{
Console.WriteLine("Setting1");
var handle = result.AsyncWaitHandle;
((ManualResetEvent)handle).Set();
}
static void OnEvt2(IAsyncResult result)
{
Thread.Sleep(2000);
Console.WriteLine("Setting2");
var handle = result.AsyncWaitHandle;
((ManualResetEvent)handle).Set();
}
}
public class ManualResetResult : IAsyncResult
{
private object _state;
private ManualResetEvent _handle;
public ManualResetResult(object state, ManualResetEvent handle)
{
_state = state;
_handle = handle;
}
#region IAsyncResult Members
public object AsyncState
{
get { return _state; }
}
public WaitHandle AsyncWaitHandle
{
get { return _handle; }
}
public bool CompletedSynchronously
{
get { throw new NotImplementedException(); }
}
public bool IsCompleted
{
get { throw new NotImplementedException(); }
}
#endregion
}
}
I am not sure I understood your question, but AutoResetEvent.WaitAll seems to solve your problem, if I got it right. It allows you to set more than one handler and it will only be released when all are set.
http://msdn.microsoft.com/en-us/library/z6w25xa6.aspx

Categories