Reputation: 9499
I have a class:
public class User
{
public string id, name, email, image;
public User (IFBGraphUser user)
{
id = user.GetId ();
name = user.GetName ();
GetEmail ();
}
private void GetEmail()
{
FBRequestConnection.StartWithGraphPath ("/me", null, "GET", ConnectionReturn);
}
private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
{
var me = (FBGraphObject)result;
Console.WriteLine("this is a test");
this.email = me["email"].ToString();
}
}
With a async method: StartWithGraphPath
When the constructor is called I want to wait for StartWithGraphPath
to finish before GetEmail
returns.
How Can I accomplish this?
StartWithGraphPath
does not return an IAsyncResult
so I can't use AsyncWaitHandle
.
Edit
When the code is called:
User u = new User(user);
Console.WriteLine("hello");
My application output:
hello
this is a test
Which is what leads me to believe StartWithGraphPath
is being called async. Is there another explanation?
Whats odd is there is also a method called StartWithGraphPathAsync So wouldn't this one Im using be synchronous by deduction? It has a synchronous feel while in debugger but not when simply running the app
Upvotes: 0
Views: 258
Reputation: 10968
A quick solution would be to use a ManualResetEvent
or ManualResetEventSlim
, wait for it in the constructor and set it in the callback method:
private readonly ManualResetEventSlim myEvent;
public User(IFBGraphUser user)
{
myEvent = new ManualResetEventSlim();
id = user.GetId();
name = user.GetName();
GetEmail();
myEvent.Wait();
}
private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
{
var me = (FBGraphObject)result;
Console.WriteLine("this is a test");
this.email = me["email"].ToString();
myEvent.Set();
}
Please be aware that this solution only works if the StartWithGraphPath method is not using the thread that was used to call it to invoke the callback. If it does - e.g. classes close to the user interface often will execute callbacks on the UI thread - then a deadlock will occur. (Thanks to @L.B for pointing this out)
If you can modify your class design I would suggest removing the expensive call from the constructor. Constructing an object should usually be cheap, and using an asynchronous method in it suggests that it might take some time.
Upvotes: -1
Reputation: 239764
I wouldn't wait for it to complete in the constructor. Make the asynchrony visible (in a friendlier form) to the consuming code, and it can choose when or if to actually perform a wait for the email value:
public class User
{
public string id, name, image;
public User (IFBGraphUser user)
{
id = user.GetId ();
name = user.GetName ();
GetEmail ();
}
public Task<string> Email {
get{
return emailTask.Task;
}
}
private TaskCompletionSource<string> emailTask =
new TaskCompletionSource<string>();
private void GetEmail()
{
FBRequestConnection.StartWithGraphPath ("/me", null, "GET", ConnectionReturn);
}
private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
{
var me = (FBGraphObject)result;
Console.WriteLine("this is a test");
emailTask.SetResult(me["email"].ToString());
}
}
Upvotes: 2