C# pass action with parameters - c#

today i was looking for some projects with ilspy i did not understand how Actions can be used like this, in one class this method is called
public void Login(Action success, Action<bool> failure)
{
if (!FB.IsInitialized)
{
Debug.Log("[FB] Not yet initialized. Will init again!");
FB.Init(new InitDelegate(this.OnInitComplete), null, null);
return;
}
new LoginWithReadPermissions(this.READ_PERMISSIONS, delegate
{
ServiceLocator.GetDB().SetBool("facebookBound", true, false);
this.OnLoginCompleted(success, failure);
}, delegate
{
failure(false);
});
}
and this is the other class that is called by above method
public class LoginWithReadPermissions
{
private readonly Action _failureCallback;
private readonly Action _successCallback;
public LoginWithReadPermissions(string[] scope, Action success, Action failure)
{
this._successCallback = success;
this._failureCallback = failure;
FB.LogInWithReadPermissions(scope, new FacebookDelegate<ILoginResult>(this.AuthCallback));
}
private void AuthCallback(ILoginResult result)
{
if (result.Error == null && FB.IsLoggedIn)
{
this._successCallback();
}
else
{
this._failureCallback();
}
}
}
Can someone please explain what is going on there I have never faced with this kind of Action usage. Thanks for replies.

public void Login(Action success, Action<bool> failure)
{
if (!FB.IsInitialized)
{
Debug.Log("[FB] Not yet initialized. Will init again!");
FB.Init(new InitDelegate(this.OnInitComplete), null, null);
return;
}
new LoginWithReadPermissions(this.READ_PERMISSIONS, delegate
{
ServiceLocator.GetDB().SetBool("facebookBound", true, false);
this.OnLoginCompleted(success, failure);
}, delegate
{
failure(false);
});
}
The code above is just shorthand for this:
public void Login(Action success, Action<bool> failure)
{
Action successAction = () =>
{
ServiceLocator.GetDB().SetBool("facebookBound", true, false);
this.OnLoginCompleted(success, failure);
};
Action failureAction = () =>
{
failure(false);
};
if (!FB.IsInitialized)
{
Debug.Log("[FB] Not yet initialized. Will init again!");
FB.Init(new InitDelegate(this.OnInitComplete), null, null);
return;
}
new LoginWithReadPermissions(this.READ_PERMISSIONS, successAction, failureAction);
}

Related

Need help controlling event handler synchronously in c#

I have a code snippet as below:
public void PopulateItemList()
{
foreach(var item in collection)
{
bool isSuccess = GetSubItem(item.Id);
if(!isSuccess)
{
ShowError();
break;
}
}
}
public bool GetSubItem(string parentId)
{
bool isSuccess = false;
_service.Communication<ViewModel, Request, Response>((ViewModel vm, ref Request) =>
{
request.ApiName = Const.FXXXName;
request.Id = parentId;
}, (viewModel, response, error) =>
{
DoSomething();
...
isSuccess = response.IsOk; // I put a breakpoint here
}
return isSuccess; // I also put a breakpoint here
}
The prototype of Communication function is as below:
public void Communication<VM, S, R>(WebSocketOut<VM, S) send, WebSocketIn<VM, R> recv)
where is WebSocketIn and WebSocketOut are two delegates. The problem is when I debug the program, the "return isSuccess;" statement is always run before it receive the result from "response.IsOk" so the returned value of that func is always "false". In other words, the order of execution is:
return isSuccess
isSuccess = response.IsOk (after MessageReceived(WebSocket4Net) event fired)
I don't know how to make it run in reversed order. Any advice, recommendations or help will be greatly appreciated.
Thanks
You can use async/await and TaskCompletionSource
async public void PopulateItemList() //!!async keyword
{
foreach(var item in collection)
{
bool isSuccess = await GetSubItem(item.Id); //!!await
if(!isSuccess)
{
ShowError();
break;
}
}
}
public Task<bool> GetSubItem(string parentId)
{
var tcs = new TaskCompletionSource<bool>(); //!!
_service.Communication<ViewModel, Request, Response>((ViewModel vm, ref Request) =>
{
request.ApiName = Const.FXXXName;
request.Id = parentId;
}, (viewModel, response, error) =>
{
DoSomething();
...
tcs.SetResult(response.IsOk); //!!
}
return tcs.Task;
}
Try to use AutoResetEvent to wait async function complete.
public bool GetSubItem(string parentId)
{
System.Threading.AutoResetEvent autoEvent = new System.Threading.AutoResetEvent(false);
bool isSuccess = false;
ThreadPool.QueueUserWorkItem((o) =>
{
_service.Communication<ViewModel, Request, Response>((ViewModel vm, ref Request) =>
{
request.ApiName = Const.FXXXName;
request.Id = parentId;
}, (viewModel, response, error) =>
{
DoSomething();
...
isSuccess = response.IsOk; // I put a breakpoint here
autoEvent.Set();
}
});
autoEvent.WaitOne();
return isSuccess; // I also put a breakpoint here
}

How can I improve and/or modularize my handling of event based tasks?

So I have a server and I'm making calls to it through a wrapped up WebSocket (WebSocket4Net) and one of the requirements of the library I'm building is the ability to await on the return of the request. So I have a class MessageEventHandler that contains events that are triggered by the class MessageHandler as messages come in.
MessageEventHandler ex.
public class MessageEventHandler : IMessageEventHandler
{
public delegate void NodeNameReceived(string name);
public event Interfaces.NodeNameReceived OnNodeNameReceived;
public void NodeNameReceive(string name)
{
if (this.OnNodeNameReceived != null)
{
this.OnNodeNameReceived(name);
}
}
}
MessageHandler ex.
public class MessageHandler : IMessageHandler
{
private IMessageEventHandler eventHandler;
public MessageHandler(IMessageEventHandler eventHandler)
{
this.eventHandler = eventHandler;
}
public void ProcessDataCollectorMessage(string message)
{
var serviceMessage = JsonConvert.DeserializeObject<ServiceMessage>(message);
switch (message.MessageType)
{
case MessageType.GetNodeName:
{
var nodeName = serviceMessage.Data as string;
if (nodeName != null)
{
this.eventHandler.NodeNameReceive(nodeName);
}
break;
}
default:
{
throw new NotImplementedException();
}
}
}
Now building upon those classes I have the class containing my asynchronous function that handles the call to get the node name.
public class ClientServiceInterface : IClientServiceInterface
{
public delegate void RequestReady(ServiceMessage serviceMessage);
public event Interfaces.RequestReady OnRequestReady;
public int ResponseTimeout { get; private set; }
private IMessageEventHandler messageEventHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
}
public Task<string> GetNodeNameAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>();
var setHandler = default(NodeNameReceived);
setHandler = name =>
{
taskCompletionSource.SetResult(name);
this.messageEventHandler.OnNodeNameReceived -= setHandler;
};
this.messageEventHandler.OnNodeNameReceived += setHandler;
var ct = new CancellationTokenSource(this.ResponseTimeout);
var registration = new CancellationTokenRegistration();
registration = ct.Token.Register(
() =>
{
taskCompletionSource.TrySetCanceled();
this.messageEventHandler.OnNodeNameReceived -= setHandler;
registration.Dispose();
},
false);
var serviceMessage = new ServiceMessage() { Type = MessageType.GetNodeName };
this.ReadyMessage(serviceMessage);
return taskCompletionSource.Task;
}
}
As you can see I wouldn't call it pretty and I apologize if anyone threw up a little reading it. But this is my first attempt at wrapping a Task with Asynchronous Event. So with that on the table I could use some help.
Is there a better way to accomplish what I'm trying to achieve here? Remembering that I want a user of the library to either subscribe to the event and listen for all callbacks OR they can simply await the return depending on
their needs.
var nodeName = await GetNodeNameAsync();
Console.WriteLine(nodeName);
or
messageEventHandler.OnNodeNameReceived += (name) => Console.WriteLine(name);
GetNodeNameAsync();
Alternatively if my approach is actually 'good' can anyone provide any advice as to how I can write a helper function to abstract out setting up each function in this way? Any help would be greatly appreciated.
So I've written a couple classes to solve the problem I was having. The first of which is my CallbackHandle class which contains the task inside the TaskCompletionSource so each time that a request is made in my example a new callback handle is created.
public class CallbackHandle<T>
{
public CallbackHandle(int timeout)
{
this.TaskCompletionSource = new TaskCompletionSource<T>();
var cts = new CancellationTokenSource(timeout);
cts.Token.Register(
() =>
{
if (this.Cancelled != null)
{
this.Cancelled();
}
});
this.CancellationToken = cts;
}
public event Action Cancelled;
public CancellationTokenSource CancellationToken { get; private set; }
public TaskCompletionSource<T> TaskCompletionSource { get; private set; }
}
Then I have a 'handler' that manages the handles and their creation.
public class CallbackHandler<T>
{
private readonly IList<CallbackHandle<T>> callbackHandles;
private readonly object locker = new object();
public CallbackHandler()
{
this.callbackHandles = new List<CallbackHandle<T>>();
}
public CallbackHandle<T> AddCallback(int timeout)
{
var callback = new CallbackHandle<T>(timeout);
callback.Cancelled += () =>
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.TrySetResult("Error");
};
lock (this.locker)
{
this.callbackHandles.Add(callback);
}
return callback;
}
public void EventTriggered(T eventArgs)
{
lock (this.locker)
{
if (this.callbackHandles.Count > 0)
{
CallbackHandle<T> callback =
this.callbackHandles.First();
if (callback != null)
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.SetResult(eventArgs);
}
}
}
}
}
This is a simplified version of my actual implementation but it should get someone started if they need something similar. So to use this on my ClientServiceInterface class in my example I would start by creating a class level handler and using it like this:
public class ClientServiceInterface : IClientServiceInterface
{
private readonly CallbackHandler<string> getNodeNameHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
this.getNodeNameHandler = new
CallbackHandler<string>();
this.messageEventHandler.OnNodeNameReceived += args => this.getNodeNameHandler.EventTriggered(args);
}
public Task<string> GetNodeNameAsync()
{
CallbackHandle<string> callbackHandle = this.getNodeNameHandler.AddCallback(this.ResponseTimeout);
var serviceMessage = new ServiceMessage
{
Type = MessageType.GetNodeName.ToString()
};
this.ReadyMessage(serviceMessage);
return callbackHandle.TaskCompletionSource.Task;
}
// Rest of class declaration removed for brevity
}
Which is much better looking than what I had before (at least in my opinion) and it's easy to extend.
For starters follow a thread-safe pattern:
public void NodeNameReceive(string name)
{
var evt = this.OnNodeNameReceived;
if (evt != null)
{
evt (name);
}
}
If you do not take a reference to the event object it can be set to null between the time you check null and call the method.

how to return Action<bool> inside method?

I have the following code in my class:
private static void SetUserMeta(string pUserToken, string pMetaKey, string pMetaValue, Action<bool> callback)
{
BuddyClient client = CreateBuddy();
bool rValue = false;
client.LoginAsync((user, state) =>
{
if (state.Exception != null)
{
rValue = false;
}
else
{
client.Metadata.SetAsync((result, resultState) =>
{
if (resultState.Exception != null)
{
rValue = false;
}
else
{
rValue = true;
}
}, key: pMetaKey, value: pMetaValue);
}
callback(rValue);
}, token: pUserToken);
}
and I want to get rValue and return it from my other method which is the following
public static void SetBuddyData(string pUserToken, BuddyData pMetaValue, Action<bool> callback)
{
//my problem is here and I don't know how to get and return data from SetUserMeta
return SetUserMeta(pUserToken, "SavedGameData", pMetaValue.Serialize());
}
And also I want to call this return value from my application. These codes are in my library. How can I do it?
Just pass callback to SetUserMeta like
public static void SetBuddyData(string pUserToken, BuddyData pMetaValue, Action<bool> callback)
{
SetUserMeta(pUserToken, "SavedGameData", callback);
}
And call SetBuddyData like this
SetBuddyData("my user token", myBundle, isLoggedIn => HandleUserLogin(isLoggedIn));
Where at HandleUserLogin you will process bool callback data, returned at callback(rValue); in SetUserMeta method. It's body example is shown
next
public static void HandleUserLogin(bool isLogged)
{
Console.WriteLine("user is {0} logged in", isLogged ? "" : "not");
}
You also can take advantage of method group syntax and call SetBuddyData method like:
SetBuddyData("my user token", myBundle, HandleUserLogin);

Having a function as an argument

I am trying to make a new Timers class to aid with my learning of c#. How would I let an argument for a function be a function?
It's pretty simple. Just make the argument be some kind of delegate type, like Action or Func.
void PassAnAction(Action action)
{
action(); // call the action
}
T PassAFunction<T>(Func<T> function)
{
return function();
}
public class MyTimer {
private readonly Action _fireOnInterval;
public MyTimer(Action fireOnInterval, TimeSpan interval, ...) {
if (fireOnInterval == null) {
throw new ArgumentNullException("fireOnInterval");
}
_fireOnInterval = fireOnInterval;
}
private void Fire() {
_fireOnInterval();
}
...
}
You can call it like this:
new MyTimer(() => MessageBox.Show("Elapsed"), TimeSpan.FromMinutes(5), ...)

C# Building Fluent API for method invocations

What do I have to do to say that InvokeMethod can invoke a method and when using special options like Repeat it shall exexute after the Repeat.
My problem for now is that the method will already exexute before it knows that it has to be called 100 times.
class Program
{
static void Main()
{
const bool shouldRun = true;
new MethodExecuter()
.ForAllInvocationsUseCondition(!Context.WannaShutDown)
.InvokeMethod(A.Process).Repeat(100)
.When(shouldRun).ThenInvokeMethod(B.Process).Repeat(10)
.ForAllInvocationsUseCondition(Context.WannaShutDown)
.When(shouldRun).ThenInvokeMethod(C.Process);
}
}
MethodExpression
public class MethodExpression
{
private bool _isTrue = true;
private readonly MethodExecuter _methodExecuter;
public MethodExpression(bool isTrue, MethodExecuter methodExecuter)
{
_isTrue = isTrue;
_methodExecuter = methodExecuter;
}
public MethodExecuter ThenInvokeMethod(Action action)
{
if (_isTrue)
{
action.Invoke();
_isTrue = false;
}
return _methodExecuter;
}
}
MethodExecuter
public class MethodExecuter
{
private bool _condition;
private int _repeat = 1;
public MethodExpression When(bool isTrue)
{
return new MethodExpression(isTrue && _condition, this);
}
public MethodExecuter InvokeMethod(Action action)
{
if (_condition)
{
for (int i = 1; i <= _repeat; i++)
{
action.Invoke();
}
}
return this;
}
public MethodExecuter ForAllInvocationsUseCondition(bool condition)
{
_condition = condition;
return this;
}
public MethodExecuter Repeat(int repeat)
{
_repeat = repeat;
return this;
}
}
Use a final method ("go", or "execute") to actually kick things off.
new MethodExecuter()
.ForAllInvocationsUseCondition(!Context.WannaShutDown)
.InvokeMethod(A.Process).Repeat(100)
.When(shouldRun).ThenInvokeMethod(B.Process).Repeat(10)
.ForAllInvocationsUseCondition(Context.WannaShutDown)
.When(shouldRun).ThenInvokeMethod(C.Process)
.Go();
What you've provided looks a bit like programming a workflow or state machine. In order to capture invocations and respect conditions during execution, you'd need to change your approach slightly.
Instead of invoking actions as they come in, consider pushing your actions into a queue and then providing an mechanism to run the state machine.
new MethodInvoker()
.ForAllInvocationsUseCondition(true)
.InvokeMethod( Process.A ).Repeat(100)
.Run();
There are a lot of ways to skin this cat, but I think one source of this difficulty is in the fact that you actually invoke the method within the InvokeMethod() method (go figure!).
Typically, we use fluent APIs to turn syntax that is evaluated from the inside-out into something that can be expressed in a left-to-right fashion. Thus, the expression builder components of the interface are used to build up state throughout the expression, and only at the end does the "real work" happen.
One solution to your immediate problem is to queue up each action with its associated options (invocation conditions, repeat count, etc.), and add some ExecuteAll() method to MethodExecuter that dequeues and executes the fully configured actions at the end of the member chain.
Another solution would be to put all of the execution options inside the InvokeMethod() method; something like:
.Invoke(x => x.Method(A.Process).Repeat(100))
This method would look something like:
public MethodExecuter Invoke(Action<IExecutionBuilder> executionBuilder)
{
var builder = new ExecutionBuilder();
executionBuilder(builder);
var action = builder.Action;
var repeat = builder.RepeatCount;
if (_condition)
{
for (int i = 1; i <= repeat; i++)
{
action();
}
}
return this;
}
I haven't worked through this in Visual Studio, but the other items would be something like:
public interface IExecutionBuilder
{
IExecutionBuilder Method(Action action);
IExecutionBuilder Repeat(int count);
}
public class ExecutionBuilder : IExecutionBuilder
{
public ExecutionBuilder()
{
RepeatCount = 1; // default to repeat once
Action = () => {}; // default to do nothing, but not null
}
public IExecutionBuilder Method(Action action)
{
Action = action;
return this;
}
public IExecutionBuilder Repeat(int repeat)
{
RepeatCount = repeat;
return this;
}
public int RepeatCount { get; private set; }
public Action Action { get; private set; }
}
Note that RepeatCount and Action are not exposed on the interface. This way, you will not see these members when calling .Invoke(x => x., but will have access to them when using the concrete ExecutionBuilder class inside the Invoke() method.
You could have a SetInvokeMethod and an Execute Method.
SetInvokeMethod(Action).Repeat(100).Execute()
In a sentence, your code is too "eager". The InvokeMethod method is called, and performs the action, before your fluent grammar structure tells your code how many times it should repeat.
To change this, try also specifying the method you are invoking as a private field in your methodInvoker, then include a command that is a "trigger" to actually perform the commands. The key is "lazy evaluation"; in a fluent interface, nothing should be done until it has to; that way you have most of the control over when it does happen.
public class FluentMethodInvoker
{
Predicate condition = ()=>true;
Predicate allCondition = null;
Action method = ()=> {return;};
bool iterations = 1;
FluentMethodInvoker previous = null;
public FluentMethodInvoker(){}
private FluentMethodInvoker(FluentMethodInvoker prevNode)
{ previous = prevNode; }
public FluentMethodInvoker InvokeMethod(Action action)
{
method = action;
}
//Changed "When" to "If"; the function does not wait for the condition to be true
public FluentMethodInvoker If(Predicate pred)
{
condition = pred;
return this;
}
public FluentMethodInvoker ForAllIf(Predicate pred)
{
allCondition = pred;
return this;
}
private bool CheckAllIf()
{
return allCondition == null
? previous == null
? true
: previous.CheckAllIf();
: allCondition;
}
public FluentMethodInvoker Repeat(int repetitions)
{
iterations = repetitions;
return this;
}
//Merging MethodExecuter and MethodExpression, by chaining instances of FluentMethodInvoker
public FluentMethodInvoker Then()
{
return new FluentMethodInvoker(this);
}
//Here's your trigger
public void Run()
{
//goes backward through the chain to the starting node
if(previous != null) previous.Run();
if(condition && CheckAllIf())
for(var i=0; i<repetitions; i++)
method();
return;
}
}
//usage
class Program
{
static void Main()
{
const bool shouldRun = true;
var invokerChain = new FluentMethodInvoker()
.ForAllIf(!Context.WannaShutDown)
.InvokeMethod(A.Process).Repeat(100)
.When(shouldRun)
.Then().InvokeMethod(B.Process).Repeat(10)
.ForAllIf(Context.WannaShutDown)
.When(shouldRun)
.Then().InvokeMethod(C.Process);
//to illustrate that the chain doesn't have to execute immediately when being built
invokerChain.Run();
}
}

Categories

Resources