From m$ site. I don't get the += o,a what is that ???
private void GetResponse(Uri uri, Action<Response> callback)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += (o, a) =>
{
if (callback != null)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Response));
callback(ser.ReadObject(a.Result) as Response);
}
};
wc.OpenReadAsync(uri);
}
wc.OpenReadCompleted += (o, a) => { }
This is assigning an anonymous delegate to the wc.OpenReadCompleted event. The (o,a) part are the method parameters.
o is object.
a is the EventArgs
As I can see from the signature of OpenReadCompletedEventHandler (which should be used to subscribe to OpenReadCompleted event), o is a sender and a is an instance of OpenReadCompletedEventArgs.
In general this approach of subscription to events is basically instantiating a delegate using a lambda expression, one can do this since C# 3.0.
Related
I'm working with a library that does not have F# documentation, only C#. Having no familiarity with C# I'm having a bit of trouble. Reading through the documentation for NetMQ, there is one line that I'm having trouble translating:
For context, here is the full example:
using (var rep1 = new ResponseSocket("#tcp://*:5001"))
using (var rep2 = new ResponseSocket("#tcp://*:5002"))
using (var poller = new NetMQPoller { rep1, rep2 })
{
rep1.ReceiveReady += (s, a) => // ??????
{
string msg = a.Socket.ReceiveString();
a.Socket.Send("Response");
};
rep2.ReceiveReady += (s, a) => // ??????
{
string msg = a.Socket.ReceiveString();
a.Socket.Send("Response");
};
poller.Run();
}
Specifically, I don't know what rep1.ReceiveReady += (s, a) => means in the context of C# as well as how to translate it to F#. Any ideas?
Thanks.
rep.ReceiveReady += (s, a) => { /*...*/ }; is subscribing to the ReceiveReady event with a lambda function. Here is a direct F# translation:
use rep1 = new ResponseSocket("#tcp://*:5001")
use rep2 = new ResponseSocket("#tcp://*:5002")
use poller = new NetMQPoller()
poller.Add rep1
poller.Add rep2
rep1.ReceiveReady.Add (fun a -> let msg = a.Socket.ReceiveString ()
a.Socket.Send "Response")
rep2.ReceiveReady.Add (fun a -> let msg = a.Socket.ReceiveString ()
a.Socket.Send "Response")
poller.Run ()
Further reading on event handling in F# can be found in the documentation. Note, however, that F# can also treat events as observables, which is likely to be considered more idiomatic.
I am developing an Windows Phone Application and I am stuck at a part.
My project is in c#/xaml - VS2013.
Problem :
I have a listpicker (Name - UserPicker) which is list of all user's names. Now I Want to get the UserID from the database for that UserName. I have implemented Web Api and I am using Json for deserialization.
But I am not able to return the String from DownloadCompleted event.
Code:
string usid = "";
selecteduser = (string)UserPicker.SelectedItem;
string uri = "http://localhost:1361/api/user";
WebClient client = new WebClient();
client.Headers["Accept"] = "application/json";
client.DownloadStringAsync(new Uri(uri));
//client.DownloadStringCompleted += client_DownloadStringCompleted;
client.DownloadStringCompleted += (s1, e1) =>
{
//var data = JsonConvert.DeserializeObject<Chore[]>(e1.Result.ToString());
//MessageBox.Show(data.ToString());
var user = JsonConvert.DeserializeObject<User[]>(e1.Result.ToString());
foreach (User u in user)
{
if (u.UName == selecteduser)
{
usid = u.UserID;
}
//result.Add(c);
return usid;
}
//return usid
};
I want to return the UserID of the selected user. But Its Giving me following errors.
Since 'System.Net.DownloadStringCompletedEventHandler' returns void, a return keyword must not be followed by an object expression
Cannot convert lambda expression to delegate type 'System.Net.DownloadStringCompletedEventHandler' because some of the return types in the block are not implicitly convertible to the delegate return type
If you check source code of DownloadStringCompletedEventHandler you will see that it is implemented like that:
public delegate void DownloadStringCompletedEventHandler(
object sender, DownloadStringCompletedEventArgs e);
That means that you can't return any data from it. You probably have some method that does something with selected user id. You will need to call this method from event handler. So if this method is named HandleSelectedUserId, then code might look like that:
client.DownloadStringCompleted += (sender, e) =>
{
string selectedUserId = null;
var users = JsonConvert.DeserializeObject<User[]>(e.Result.ToString());
foreach (User user in users)
{
if (user.UName == selecteduser)
{
selectedUserId = user.UserID;
break;
}
}
HandleSelectedUserId(selectedUserId);
};
client.DownloadStringAsync(new Uri("http://some.url"));
It's also a good idea to add event handler for DownloadStringCompleted event before you call DownloadStringAsync method.
Below is a segment of code from a client handler class.
I know I am passing the wrong information as noted in the comment below. I just need to know exactly what I should pass to make it work.
public static void add2ClientList(string s)
{
MainWindow.mainWindow.ClientListBox.Items.Add(s);
}
Action<string> addToClientListBox = new Action<string> (add2ClientList);
public void addClientToPool(Client c)
{
if (ClientPool == null)
{
ClientPool = new Client[] { c };
uiDispatcher.BeginInvoke(addToClientListBox, DispatcherPriority.Background, CancellationToken.None, TimeSpan.Zero, c.getClientIp());
// above is the issue apparently I am passing the wrong params
return;
}
List<Client> temp = new List<Client>();
foreach (Client cc in ClientPool)
{
temp.Add(cc);
}
temp.Add(c);
ClientPool = temp.ToArray();
uiDispatcher.BeginInvoke(addToClientListBox, DispatcherPriority.Background, CancellationToken.None, TimeSpan.Zero, c.getClientIp());
}
The only thing that appears to be missing is your param to pass to the delegate, I assume you want the following:
uiDispatcher.CurrentDispatcher.BeginInvoke(addToClientListBox, new object[]{"ParamString"}, DispatcherPriority.Background);
If that's not exactly what you were asking let me know :)
Delegate d = (Action<string>)add2ClientList;
uiDispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, d, c.getClientIp());
There is a List.
I want to download each url via webclient.DownloadStringAsync
the problem I encounter is:
how do I know which e.Result corresponds to what url ?
public class ressource{
public string url { get; set; }
public string result { get; set; }
}
List<ressource> urlist = new List<ressource>();
urlist.Add(new ressource(){url="blabla", result=string.empty});
....etc
var wc= new WebClient();
foreach(var item in urlist)
{
wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
wc.DownloadStringAsync(new Uri(item.url, UriKind.Absolute));
}
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
urlist[?].result = e.Result;
}
I feel completely stuck.
Thanks for your ideas.
the problem I encounter is: how do I know which e.Result corresponds to what url ?
There are various different options for this:
UserState
You can pass in a second argument to DownloadStringAsync, which is then available via DownloadStringCompletedEventArgs.UserState. For example:
// In your loop....
var wc = new WebClient();
wc.DownloadStringAsync(new Uri(item.url, UriKind.Absolute), item);
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var item = (ressource) e.UserState;
item.result = e.Result;
}
Multiple WebClients
You can create a new WebClient for each iteration of the loop, and attach a different event handler to it. A lambda expression is useful here:
// Note: this is broken in C# 3 and 4 due to the capture semantics of foreach.
// It should be fine in C# 5 though.
foreach(var item in urlist)
{
var wc = new WebClient();
wc.DownloadStringCompleted += (sender, args) => item.result = args.Result;
wc.DownloadStringAsync(new Uri(item.url, UriKind.Absolute));
}
DownloadStringTaskAsync
You could DownloadStringTaskAsync instead, so that each call returns a Task<string>. You could keep a collection of these - one for each element in urlist - and know which is which that way.
Alternatively, you could just fetch all the results synchronously, but I suspect you don't want to do that.
Additional information
Unfortunately, WebClient doesn't support multiple concurrent connections, so with all the options above you should create a new WebClient per iteration anyway.
Another alternative, and the one I prefer, is to use Microsoft's Reactive Framework (Rx). It handles all the background threading for you, similar to the TPL, but often easier.
Here's how I would do it:
var query =
from x in urlist.ToObservable()
from result in Observable.Using(
() => new WebClient(),
wc => Observable.Start(() => wc.DownloadString(x.url)))
select new
{
x.url,
result
};
Now to get the results back into the original urlist.
var lookup = urlist.ToDictionary(x => x.url);
query.Subscribe(x =>
{
lookup[x.url].result = x.result;
});
Simple as that.
I have a DownloadManager class that manages multiple DownloadItem objects. Each DownloadItem has events like ProgressChanged and DownloadCompleted. Usually you want to use the same event handler for all download items, so it's a bit annoying to have to set the event handlers over and over again for each DownloadItem.
Thus, I need to decide which pattern to use:
Use one DownloadItem as a template and clone it as necessary
var dm = DownloadManager();
var di = DownloadItem();
di.ProgressChanged += new DownloadProgressChangedEventHandler(di_ProgressChanged);
di.DownloadCompleted += new DownloadProgressChangedEventHandler(di_DownloadCompleted);
DownloadItem newDi;
newDi = di.Clone();
newDi.Uri = "http://google.com";
dm.Enqueue(newDi);
newDi = di.Clone();
newDi.Uri = "http://yahoo.com";
dm.Enqueue(newDi);
Set the event handlers on the DownloadManager instead and have it copy the events over to each DownloadItem that is enqeued.
var dm = DownloadManager();
dm.ProgressChanged += new DownloadProgressChangedEventHandler(di_ProgressChanged);
dm.DownloadCompleted += new DownloadProgressChangedEventHandler(di_DownloadCompleted);
dm.Enqueue(new DownloadItem("http://google.com"));
dm.Enqueue(new DownloadItem("http://yahoo.com"));
Or use some kind of factory
var dm = DownloadManager();
var dif = DownloadItemFactory();
dif.ProgressChanged += new DownloadProgressChangedEventHandler(di_ProgressChanged);
dif.DownloadCompleted += new DownloadProgressChangedEventHandler(di_DownloadCompleted);
dm.Enqueue(dif.Create("http://google.com"));
dm.Enqueue(dif.Create("http://yahoo.com"));
What would you recommend?
Why are the DownloadItems responsible for reporting progress (from an API design perspective)?
I'd say that the DownloadManager is responsible for downloading DownloadItems, and therefore also for reporting progress. (The internal implementation strategy may, of course, differ.)
I'd go with the second option:
var dm = DownloadManager
{
"http://google.com",
new DownloadItem("http://yahoo.com") { Retries = 5 }
};
dm.ProgressChanged += (sender, e) =>
Console.WriteLine("Download {0}: {1:P}", e.Uri, (double)e.Progress / 100.0);
dm.DownloadCompleted += (sender, e) =>
Console.WriteLine("Download {0}: completed!", e.Uri);
dm.DownloadAllCompleted += (sender, e) =>
Console.WriteLine("All downloads completed!");
dm.Add("http://stackoverflow.com");
dm.DownloadAllAsync();
If you happen to have a copy of the Framework Design Guideline (2nd ed.) at hand, have a look at pages 305--312 (Event-based Async Pattern).
I would say Template method pattern with a factory would be the right approach for this.