IDE bug fixed in VS 2015? - vb.net

This appears to be an IDE bug specific to VB in VS 2013. The following code produces a compiler warning:
The Task returned from this Async Function will be dropped, and any exceptions in it ignored. Consider changing it to an Async Sub so its exceptions are propagated.
Well, clearly it already is an Async Sub (void in C#). While the general rule is to avoid Async Subs, it's perfectly acceptable—and even advised—to do so with top-level event handlers such as this.
That's why I'm calling this an IDE bug. It only manifests when we set an event handler in VB (using either AddHandler or the Handles keyword). I'm not finding other reports of it, which frankly is odd... I can't imagine I'm the only one seeing it.
But to the question: can anyone report whether this nuisance has been fixed in VS 2015? If it has, I'll have further justification for upgrading sooner rather than later. (I have other reasons holding me back for the time being that aren't appropriate for discussion here.)

The source of the problem is a relaxed delegate without an implicit widening conversion.
The Click event accepts an Object as the first parameter, but the handler accepts Button which makes it a relaxed delegate.
Because the handler's signature is more restrictive than the event signature, the code as is will only compile with Option Strict Off, and generate the seemingly spurious warning about a dropped async task.
Note that the problem does not occur when a widening conversion exists (e.g. where the event has Button and the handler has Object).
To fix the problem, you can:
Change the handler's signature to match the event signature exactly:
Private Async Sub cmdStart_Click(sender As Object, e As RoutedEventArgs)
Wrap the AddressOf into a delegate constructor:
AddHandler cmdStart.Click, New RoutedEventHandler(AddressOf cmdStart_Click)
Ditch the AddressOf and wrap the call to the handler into a lambda:
AddHandler cmdStart.Click, Sub(sender As Object, e As RoutedEventArgs)
Me.cmdStart_Click(sender, e)
End Sub
As for why this is happening and whose fault that is (yours, IDE's or the compiler's), I cannot tell you for sure.
However there is one thing I can see.
These two lines of code:
AddHandler cmdStart.Click, AddressOf RelaxedHandler
AddHandler cmdStart.Click, Sub(a0 As Object, a1 As EventArgs)
Me.RelaxedHandler(a0, a1)
End Sub
produce identical IL code, as far as the ILSpy is concerned:
this.cmdStart.Click += delegate(object a0, EventArgs a1)
{
this.RelaxedHandler((Button)a0, a1);
}
;
this.cmdStart.Click += delegate(object a0, EventArgs a1)
{
this.RelaxedHandler((Button)a0, a1);
}
;
but the first line generates a warning and the second line does not.

Related

Task's ConfigureAwait(False) not working

I've read a lot of tutorials with regards to await/async and i've understood that when it comes to await keyword it will go back to the main context then go back to await (when it's finished) and then continue further if there's something after away within async method. I also read that there is something like ConfigureAwait(False) which simply means that with opposite to i've written before when it comes to await it will not go back to main context at this point but will stay here and wll wait on await to be finished and then continue in async method after that will go back to main cotext. So diffrence is it will not go back to main context but wait on await. Correct if i am wrong. Based on that knowledge in first example i should see following result:
After DoStafAsync
Done running the long operation asynchronously.
but when i just simply add ConfigureAwait(False) it should be:
Done running the long operation asynchronously.
After DoStafAsync
as it will not go back to context but will wait and after async method finishes then will go back. Somehow i see the result as in first output. What i am missing?
Public Class Form1
Sub LongOperation()
Threading.Thread.Sleep(5000)
End Sub
Private Async Function DoStafAsync() As Task
lblStatus.Text = "Running a long operation asynchronously... (UI thread should be fully responsive)"
Await Task.Run(Sub()
LongOperation()
End Sub).ConfigureAwait(False)
lblStatus.Text = "Done running the long operation asynchronously."
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dfg
lblStatus.Text = "After DoStafAsync"
End Sub
Async Sub Dfg
Await DoStafAsync
End Sub
End Class
Additional question: Some people claims that tasks are not creating
threads but working on same ui thread. But there are some people
claiming that they are and there are also people claiming that
sometimes tasks creating threads and sometimes not. What is true here?
i've understood that when it comes to await keyword it will go back to the main context then go back to await (when it's finished) and then continue further if there's something after away within async method.
I don't think you're using the term "context" the way it's normally used when explaining async code. Check out my async intro blog post, which explains exactly what the context is and how it's used by await.
In particular, "context" does not mean "method or function". It does not determine how or when the code returns to another part of the code.
You may also want to read my MSDN article on async best practices, which explains why you should avoid async void (except for event handlers). If you make Form1_Load an async void and get rid of dfg, then it works as you would expect:
Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await DoStafAsync
lblStatus.Text = "After DoStafAsync"
End Sub
The reason it wasn't working before is because Form1_Load was not waiting for dfg (or DoStafAsync) to complete - it was just firing them off and then proceeding immediately to setting the label text. As I point out in my best practices article, one of the main problems with async void (async sub) is that the calling code cannot determine when it has completed (at least, not easily).

parallel event update UI

I have a class written in C#. In it I want to run a certain function in parallel on a list. After it completes on each item I would like to update a progress bar. However, I get very odd behavior from my program. It executes the event and reaches my sub but never proceeds to actually execute any code. Instead it just freezes. (I've mixed vb.net and c#. It will be rewritten at some point)
so in my windows form I call
progressBar.Visible = True
progressBar.Value = 0
progressBar.Maximum = dataGrid.SelectedRows.Count
AddHandler quoteManager.refreshStarted, AddressOf progressBarCounter
quoteManager.refreshAllAsync(list)
and the event is simply
Private Sub progressBarCounter()
Me.Invoke(Sub()
If progressBar.Value = progressBar.Maximum Then
progressBar.Visible = False
Else
progressBar.Value += 1
End If
End Sub)
End Sub
and in the quote manager class I have this defined.
public event Action refreshStarted;
public void refreshAllAsync(List<BindableQuote> bindableQuotes)
{
bindableQuotes.AsParallel()
.WithDegreeOfParallelism(10)
.ForAll((quote) => {
quote.refreshAll();
if (refreshStarted != null) { refreshStarted(); }
});
}
So for some reason I get it to enter progressBarCounter on each item in the list but it never exists. Instead it just keeps the form frozen.
I am not exactly sure this is what is happening, but it looks like progressBarCounter is blocking because you are calling Invoke. Should you be using BeginInvoke instead? Using BeginInvoke might solve the deadlock issue. See this post: What's the difference between Invoke() and BeginInvoke()
What appears to be happening here is that you access UI objects from multiple threads.
That's not supported. You'll have to run this code on a worker thread, and let it somehow accumulate progress, and send messages back to the UI thread. The BackgroundWorker class can help you implement the marshalling back to the UI thread.

.NET End Invoke / Post Operation Completed

My question involves async operations in VB .NET.
Given the following:
Delegate WorkerDelegate(Byval asyncOp As AsyncOperation)
Public Sub StartWork()
Dim worker as new WorkerDelegate(AddressOf DoWork)
Dim asyncOp as AsyncOperation = AsyncOperationManager.CreateOperation(New Object)
// begin work on different thread
worker.BeginInvoke(asyncOp, Nothing, Nothing)
End Sub
Private Sub DoWork(Byval asyncOp as AsyncOperation)
// do stuff
// work finished, post
asyncOp.PostOperationCompleted(AddressOf OnDownloadFinished, Nothing)
End Sub
Private Sub OnDownloadFinished()
// Back on the main thread now
End Sub
Most resources I've read say that if you use BeginInvoke on a delegate you must call EndInvoke. In my example above I am using the PostOperationCompleted method to switch threads back and report the operation has finished.
Do I still need to get an IAsyncResult when I call worker.BeginInvoke and add worker.EndInvoke in the OnDownloadFinished method?
It's best practice to call EndInvoke, because that's when resources assigned by the AsyncResult are cleaned.
However, AFAIK the async result used by the asynchronous delegate does not use any resource if you don't access the WaitHandle property, so not calling the EndInvoke may have no impact.
In your scenario, you should consider using ThreadPool.QueueUserWorkItem.
In the example on MSDN, the equivalent of your OnDownloadFinished method looks like this:
// This method is invoked via the AsyncOperation object,
// so it is guaranteed to be executed on the correct thread.
private void CalculateCompleted(object operationState)
{
CalculatePrimeCompletedEventArgs e =
operationState as CalculatePrimeCompletedEventArgs;
OnCalculatePrimeCompleted(e);
}
It does not call EndInvoke(). So it's safe to assume that not calling EndInvoke() in the PostOperationCompleted handler is alright.

Windows Forms with async socket; no text output

Currently I am having a weird problem which I simply do not understand. I have a simple GUI, with one button & one richeditbox. I have an async socket running, I am receiving some data over the network which I want to print to the gui(richeditbox). The async socket is being started when the user hits the button. So when I receive the network data I call a function which prints the data, here how it looks like (in form1 class):
Public Sub AddText(ByVal text As String)
Try
Console.WriteLine(text)
RichTextBox1.AppendText(text)
RichTextBox1.AppendText(vbNewLine)
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
End Sub
Then I simply do Form1.AddText(..) from my network class or a module (does it matter?). The problem is that nothing appears in the richeditbox, even though the AddText function is being called, no exceptions, no errors, simply nothing. I've looked thru it with the debugger, and "text" contained the data it had to print, but simply nothing appears.. Anyone have an idea?
If the socket is running on another thread (which, of course, it is because it's asynchronous), you may have to use InvokeRequired in order to get the RichTextBox to display the text. I had a similar issue with a listener on an asynchronous serial port listener.
I'm pretty sure David is right. Here's an example.
Delegate Sub AddTextDelegate(ByVal text as String)
Public Sub AddText(ByVal text as String)
If Me.InvokeRequired Then
Me.Invoke(new AddTextDelegate(AddressOf Me.AddText), new object() { text })
Else
Try
Console.WriteLine(text)
RichTextBox1.AppendText(text)
RichTextBox1.AppendText(vbNewLine)
Catch e as Exception
Console.WriteLine(e.ToString())
End Try
End If
End Sub
The deal is that controls have to be updated on the thread they were created on. It sounds like the AddText() routine is being called in the context of your async socket's thread. The AddText() routine will behave like a recursive function. The first time it's called, the InvokeRequired property will be true. This will cause it to be called again via the Invoke() call, which takes care of marshaling the data to the correct thread. The second time it's called, InvokeRequired will be false, and the control will be updated.
Fixed. I couldn't use Form1 to call the functions, because it's a type, its like a new var there with its own memory, since its a diff thread. So when I checked InvokeRequired, it said false because that Form1 belongs to that Thread, and thus no text was being displayed because I didn't even see the form. So just made a global var such as Public myForm As Form1 and assigned myForm to Form1 in Form1_Load.

Removing all event handlers in one go

Problem: I have a document class which contains a list of objects. These objects raise events such as SolutionExpired, DisplayExpired etc. The document needs to respond to this.
Documents can sometimes exchange objects, but a single object should never be 'part' of more than one document.
My document class contains a bunch of methods which serve as event handlers. Whenever an object enters the document, I use AddHandler to set up the events, and whenever an object is removed from the document I use RemoveHandler to undo the damage. However, there are cases where it's difficult to make sure all the steps are properly taken and I might thus end up with rogue event handlers.
Long story short; how do I remove all the handlers that are pointing to a specific method? Note, I don't have a list of potential event sources, these could be stored anywhere.
Something like:
RemoveHandler *.SolutionExpired, AddressOf DefObj_SolutionExpired
You can use Delegate.RemoveAll(). (The part you're interested in is in button2_Click)
public void Form_Load(object sender, EventArgs e)
{
button1.Click += new EventHandler(button1_Click);
button1.Click += new EventHandler(button1_Click);
button2.Click += new EventHandler(button2_Click);
TestEvent += new EventHandler(Form_TestEvent);
}
event EventHandler TestEvent;
void OnTestEvent(EventArgs e)
{
if (TestEvent != null)
TestEvent(this, e);
}
void Form_TestEvent(object sender, EventArgs e)
{
MessageBox.Show("TestEvent fired");
}
void button2_Click(object sender, EventArgs e)
{
Delegate d = TestEvent as Delegate;
TestEvent = Delegate.RemoveAll(d, d) as EventHandler;
}
void button1_Click(object sender, EventArgs e)
{
OnTestEvent(EventArgs.Empty);
}
You should note that it doesn't alter the contents of the delegates you pass in to it, it returns an altered delegate. Consequently, you won't be able to alter the events on a button you've dropped on a form from the form, as button1.Click can only have += or -= used on it, not =. This won't compile:
button1.Click = Delegate.RemoveAll(d, d) as EventHandler;
Also, be sure that wherever you're implementing this you're watching out for the potential of race conditions. You could end up with some really strange behavior if you're removing handlers from an event that is being called by another thread!
public class TheAnswer
{
public event EventHandler MyEvent = delegate { };
public void RemoveFromMyEvent(string methodName)
{
foreach (var handler in MyEvent.GetInvocationList())
{
if (handler.Method.Name == methodName)
{
MyEvent -= (EventHandler)handler;
}
}
}
}
EDIT 2: Apologies for my misunderstanding--I see that you were pretty clear about not having access to the event sources in your original post.
The simplest way I can think of to solve this problem involves implementing a Shared dictionary of object-to-document bindings. When an object enters a document, check the dictionary for an existing binding to another document; if present, remove handlers that refer to the old document before adding them for the new. Either way, update the dictionary with the new binding.
I think in most cases the performance and memory impacts would be negligible: unless you're dealing with many tens of thousands of small objects and frequently exchange them between documents, the memory overhead of each key/value pair and performance hit for each lookup operation should be fairly small.
As an alternative: if you can detect (in the document event handlers) that the sender of the event is no longer relevant to the document, you can detach the events there.
These seem like the kind of ideas you might have already rejected--but maybe not!
Use Delegate.RemoveAll (maybe using reflection if the Delegate instance is private).