Arguments to methods passed by control invocation

H

Harlan Messinger

I have a list of files that implements IEnumerable<string>, and I am
trying to list them to a listbox on a Windows form:

private void GetFiles()
{
int count = 0;
files = new LazyFileList(@"w:\", "*.asp");
foreach (string file in files)
{
if (canceled)
{
System.Windows.Forms.MessageBox.Show("Enumeration canceled");
break;
}
string status = "" + ++count + " of " + files.Count + " processed";

BeginInvoke(new MethodInvoker(delegate() { UpdateScreen(file,
status); }));
}
}

private void UpdateScreen(string file, string status)
{
textBox1.AppendText(file + System.Environment.NewLine);
label1.Text = status;
label1.Refresh();
}


What's happening in the form is that for most of the files in any given
directory, the name of the LAST file in that directory is displayed, but
the status seems to be getting updated normally.

With a breakpoint on the BeginInvoke line, the Locals window shows me,
on successive iterations:

file = w:\\file01.asp, status = 1 of 37 processed
file = w:\\file02.asp, status = 2 of 37 processed
file = w:\\file03.asp, status = 3 of 37 processed
etc.

With a breakpoint on the call to textBox1.AppendText, the value of
status proceeds sequentially as above, but the file names shown don't
match. The same name will appear multiple times, and then it will jump
ahead to another one:

file = w:\\file14.asp, status = 1 of 37 processed
file = w:\\file14.asp, status = 2 of 37 processed
file = w:\\file14.asp, status = 3 of 37 processed
file = w:\\file14.asp, status = 4 of 37 processed
file = w:\\file35.asp, status = 5 of 37 processed
file = w:\\file35.asp, status = 6 of 37 processed

etc.

I can think of some ideas as to why the values of file and status as
they are at the time I call BeginInvoke aren't the ones that will be
used when the invoker invokes the delegate. But then I don't understand
why only the file name manifests the problem, and the status is fine.

I actually don't want to create an anonymous delegate and then create a
MethodInvoker around *it* anyway, but I don't know a more compact way to
handle the invocation.
 
A

Adam Clauss

Harlan said:
I have a list of files that implements IEnumerable<string>, and I am
trying to list them to a listbox on a Windows form:

private void GetFiles()
{
int count = 0;
files = new LazyFileList(@"w:\", "*.asp");
foreach (string file in files)
{
if (canceled)
{
System.Windows.Forms.MessageBox.Show("Enumeration canceled");
break;
}
string status = "" + ++count + " of " + files.Count + "
processed";

BeginInvoke(new MethodInvoker(delegate() { UpdateScreen(file,
status); }));
}
}
The reason you see that is because, I suspect when the code for the
foreach loop is generated, the "string file" variable is actually scoped
"around" the for loop. The value of the "one" variable is simply
assigned - it is not redeclared as a new variable with each iteration.
So, when the form finally gets around to invoking your anonymous
delegate, it comes back to the scope of your GetFiles() method. Status
is fine - it is a local variable that gets redeclared with each
iteration of the for loop (that specific instance of the local status
variable is part of the scope that gets maintained with the anonymous
delegate). But each anonymous delegate instance actually looks at the
SAME instance of the "file" variable, which in most cases has the LAST
value set by the for loop (assuming that for loops completes prior to
the Invokes actually occuring).

Possible fixes:
1) Declare a local file variable the same you do the status variable,
assigning it the value of "file." Then use that local variable in the
call to UpdateScreen.
2) *Probably my preference* Define a delegate representing your
UpdateScreen call (the delegate takes two string parameters). Pass
"UpdateScreen" itself as a delegate to BeginInvoke and pass the
file/status variables as parameters to BeginInvoke (rather than
embedding the call and parameters in an anonymous delegate). These
values will get "copied" into the parameter of BeginInvoke, and each
invocation will maintain it's unique values.

-Adam
 
P

Peter Duniho

Harlan said:
[...]
With a breakpoint on the call to textBox1.AppendText, the value of
status proceeds sequentially as above, but the file names shown don't
match. The same name will appear multiple times, and then it will jump
ahead to another one:

file = w:\\file14.asp, status = 1 of 37 processed
file = w:\\file14.asp, status = 2 of 37 processed
file = w:\\file14.asp, status = 3 of 37 processed
file = w:\\file14.asp, status = 4 of 37 processed
file = w:\\file35.asp, status = 5 of 37 processed
file = w:\\file35.asp, status = 6 of 37 processed

etc.

I can think of some ideas as to why the values of file and status as
they are at the time I call BeginInvoke aren't the ones that will be
used when the invoker invokes the delegate. But then I don't understand
why only the file name manifests the problem, and the status is fine.

Well, look at it more closely then. :)

You are on the right track. As you've noticed, the code being executed
as a result of the BeginInvoke() method call is executed later, so the
values of "file" and "status" might not be the same. Except that notice
those two variables have a very important difference: the "file"
variable is declared outside of the "foreach" block, while the "status"
variable is declared within.

When you use an anonymous method (not "anonymous delegate"), variables
are captured according to very specific rules. In particular, every
time a block is entered, variables declared within that block are
considered to be new variables for the purpose of capturing.

So in your anonymous method, the "file" variable that is captured is the
same one always, while the "status" variable is a new variable each time
through the loop. Each anonymous method gets a different "status"
variable, but they are all using the same "file" variable.

Since the invocation of the anonymous method is delayed until the main
GUI thread gets to handle them, and then in that thread a bunch of them
can be handled all at once, each group of invocations that are handled
all use the most recent value for "file" that they can see.

In your example, you can see that by the time your first group of
invocations get processed by the GUI thread, the "file" variable has
gotten up to "file14", and then the next time, up to "file35", etc.
I actually don't want to create an anonymous delegate and then create a
MethodInvoker around *it* anyway, but I don't know a more compact way to
handle the invocation.

I'm not sure what you mean. Personally, I think that invoking anonymous
_methods_ (not "anonymous delegate") is a fairly elegant way to deal
with BeginInvoke(). The one thing I would change is to just case the
anonymous method, which the compiler knows to create a delegate instance
from, to MethodInvoker. Maybe that's what you're talking about?

That would look like this:

BeginInvoke((MethodInvoker)delegate() { UpdateScreen(file, status); });

Note that part of the problem here is that you're using BeginInvoke()
instead of Invoke(). The former can be more efficient, granted, but it
makes these kinds of concurrency issues show up more easily. If you
were using Invoke(), then you'd be guaranteed the values of "file" and
"status" remained in lockstep with the method invocation itself.

If you want to continue using BeginInvoke(), then you need to force the
current value of "file" to be stored in a location that won't change
before the anonymous method gets to execute. The easiest way to do this
is to just add a new variable within the loop block:

foreach (string file in files)
{
string status = ++count.ToString() + " of " +
files.Count.ToString() + " processed";
string fileT = file;

BeginInvoke((MethodInvoker)delegate() { UpdateScreen(fileT,
status); });
}

Then each anonymous method you invoke gets its own "fileT" variable, the
value of which is initialized once and never changed.

Alternatively, you can accomplish the same thing in a more explicit way
by making the new variable part of a whole new method:

private void GetFiles()
{
int count = 0;
files = new LazyFileList(@"w:\", *.asp");
foreach (string file in files)
{
string status = ++count.ToString() + " of " +
files.Count.ToString() + " processed";

QueueUpdate(file, status);
}
}

private QueueUpdate(string file, string status)
{
BeginInvoke((MethodInvoker)delegate() { UpdateScreen(file, status); });
}

Note that you might consider not even doing the string construction in
the worker thread. After all, presumably you'd like that thread to do
as little work as possible; otherwise, why go to the trouble to use
BeginInvoke() instead of Invoke() in the first place? In that case, you
might wind up with something like this:

private void GetFiles()
{
int count = 0;
files = new LazyFileList(@"w:\", *.asp");
foreach (string file in files)
{
int countT = ++count;
string fileT = file;

BeginInvoke((MethodInvoke)delegate() { UpdateScreen(fileT,
countT); });
}
}

private UpdateScreen(string file, int count)
{
string status = count.ToString() + " of " + files.Count.ToString()
+ " processed";

textBox1.AppendText(file);
textBox1.AppendText(Environment.NewLine);
label1.Text = status;
}

As you can see, there are lots of variations on the theme. Any should
work fine. The important thing is to remember that the loop variable in
the "foreach" loop is created only once, and so each invocation of the
anonymous method uses the same variable.

Some final comments:

– I remove the empty string you had at the beginning of the
concatenation used to initialize "status". I don't know why you had it,
but it looks like busy-work. Also, I've explicitly called ToString().
I prefer this to the way you had it, because it avoids the auto-boxing
of the ints that has to happen otherwise. Technically a potential
premature optimization, but it's something I'm in the habit of doing,
especially when dealing with threading code where performance is often
more important.

– the entire example implies that your GetFiles() method is not
executing on the main GUI thread, otherwise why use BeginInvoke()? But
it also implies that the UpdateScreen() method _is_ executed on the main
GUI thread (i.e. that's what BeginInvoke() does), so there should be no
need to call "label1.Refresh()" in the UpdateScreen() method. (In
general, if you catch yourself calling Control.Refresh(), that's a sign
the code needs some proper threading, but here it appears you can just
take it out altogether. You're already doing the threading work).

– I split the call to AppendText() into two. Normally, I wouldn't
bother with a little micro-optimization like that, and in fact you might
choose to keep the code the way you had it before. But I did it to
highlight the fact that when you concatenate the string before calling
AppendText() once, you wind up unavoidably creating a whole new String
object, just to tack on that one extra character pair (to be clear:
creating the object isn't such a big deal, but you have to copy all of
the original data when you do it…the copy is inefficient use of your
resources). Calling AppendText() twice avoids the concatenation
operation, and allows TextBox do handle the concatenation as efficiently
as it can (which is probably much more efficient than creating a whole
new String object).

Hope that helps.

Pete
 
H

Harlan Messinger

Peter said:
Harlan said:
[...]
With a breakpoint on the call to textBox1.AppendText, the value of
status proceeds sequentially as above, but the file names shown don't
match. The same name will appear multiple times, and then it will jump
ahead to another one:

file = w:\\file14.asp, status = 1 of 37 processed
file = w:\\file14.asp, status = 2 of 37 processed
file = w:\\file14.asp, status = 3 of 37 processed
file = w:\\file14.asp, status = 4 of 37 processed
file = w:\\file35.asp, status = 5 of 37 processed
file = w:\\file35.asp, status = 6 of 37 processed

etc.

I can think of some ideas as to why the values of file and status as
they are at the time I call BeginInvoke aren't the ones that will be
used when the invoker invokes the delegate. But then I don't
understand why only the file name manifests the problem, and the
status is fine.

Well, look at it more closely then. :)

You are on the right track. As you've noticed, the code being executed
as a result of the BeginInvoke() method call is executed later, so the
values of "file" and "status" might not be the same. Except that notice
those two variables have a very important difference: the "file"
variable is declared outside of the "foreach" block, while the "status"
variable is declared within.

When you use an anonymous method (not "anonymous delegate"), variables

I suppose not "anonymous delegate", but I didn't mean "anonymous method"
either. I was focused on the anonymous delegate *type*. As opposed to
declaring a named delegate type like MyDelegate below,

public delegate void MyDelegate(string x, string, y);
...
BeginInvoke(new MyDelegate(UpdateScreen),
new object[] { file, status });

(where this way of calling BeginInvoke is what I'd had in mind), I had
in mind that there would be some syntax analogous to non-typedef'ed
function pointers in C++ that would allow something like

BeginInvoke(new void delegate(string, string)(UpdateScreen),
new object[]{ file, status });

where I would wrap a typed delegate around UpdateScreen without having
to name the type and declare it in advance. Thus, an anonymous delegate
type.

The fact of my having changed this to use MethodInvoker and an explicit
call to UpdateScreen within braces (the anonymous method to which you
refer) just reflected where I happened to be while trying out different
approaches.
are captured according to very specific rules. In particular, every
time a block is entered, variables declared within that block are
considered to be new variables for the purpose of capturing.

So in your anonymous method, the "file" variable that is captured is the
same one always, while the "status" variable is a new variable each time
through the loop. Each anonymous method gets a different "status"
variable, but they are all using the same "file" variable.

Interesting. But difficult to comprehend. It kind of screws with the
fact that the anonymous method is a black box until it's finally
invoked, at which point the call to UpdateScreen is still a method
taking two arguments, each of which is a reference type being passed by
value, and yet they are treated differently because now, for the status
variable, earlier values were somehow preserved for use now, while the
same isn't true of the file variable, because of scope. I've gotta go
find me a resource. I *believe* you, and I understand it at a surface
level, but I gotta get a deeper grasp on this.
Since the invocation of the anonymous method is delayed until the main
GUI thread gets to handle them, and then in that thread a bunch of them
can be handled all at once, each group of invocations that are handled
all use the most recent value for "file" that they can see.

In your example, you can see that by the time your first group of
invocations get processed by the GUI thread, the "file" variable has
gotten up to "file14", and then the next time, up to "file35", etc.


I'm not sure what you mean. Personally, I think that invoking anonymous
_methods_ (not "anonymous delegate") is a fairly elegant way to deal
with BeginInvoke(). The one thing I would change is to just case the
anonymous method, which the compiler knows to create a delegate instance
from, to MethodInvoker. Maybe that's what you're talking about?

That would look like this:

BeginInvoke((MethodInvoker)delegate() { UpdateScreen(file, status); });

(I went into what I had in mind above.)
Note that part of the problem here is that you're using BeginInvoke()
instead of Invoke(). The former can be more efficient, granted, but it
makes these kinds of concurrency issues show up more easily. If you
were using Invoke(), then you'd be guaranteed the values of "file" and
"status" remained in lockstep with the method invocation itself.

I pretty much figured that I don't want the processing code to keep
getting blocked waiting for the UI thread, though I see how it would, of
course, solve the problem.
If you want to continue using BeginInvoke(), then you need to force the
current value of "file" to be stored in a location that won't change
before the anonymous method gets to execute. The easiest way to do this
is to just add a new variable within the loop block:

foreach (string file in files)
{
string status = ++count.ToString() + " of " + files.Count.ToString()
+ " processed";
string fileT = file;

BeginInvoke((MethodInvoker)delegate() { UpdateScreen(fileT, status);
});
}

Then each anonymous method you invoke gets its own "fileT" variable, the
value of which is initialized once and never changed.

Alternatively, you can accomplish the same thing in a more explicit way
by making the new variable part of a whole new method:

private void GetFiles()
{
int count = 0;
files = new LazyFileList(@"w:\", *.asp");
foreach (string file in files)
{
string status = ++count.ToString() + " of " +
files.Count.ToString() + " processed";

QueueUpdate(file, status);
}
}

private QueueUpdate(string file, string status)
{
BeginInvoke((MethodInvoker)delegate() { UpdateScreen(file, status); });
}

Note that you might consider not even doing the string construction in
the worker thread. After all, presumably you'd like that thread to do
as little work as possible; otherwise, why go to the trouble to use
BeginInvoke() instead of Invoke() in the first place? In that case, you
might wind up with something like this:

private void GetFiles()
{
int count = 0;
files = new LazyFileList(@"w:\", *.asp");
foreach (string file in files)
{
int countT = ++count;
string fileT = file;

BeginInvoke((MethodInvoke)delegate() { UpdateScreen(fileT,
countT); });
}
}

private UpdateScreen(string file, int count)
{
string status = count.ToString() + " of " + files.Count.ToString() +
" processed";

textBox1.AppendText(file);
textBox1.AppendText(Environment.NewLine);
label1.Text = status;
}

As you can see, there are lots of variations on the theme. Any should
work fine. The important thing is to remember that the loop variable in
the "foreach" loop is created only once, and so each invocation of the
anonymous method uses the same variable.

Some final comments:

– I remove the empty string you had at the beginning of the
concatenation used to initialize "status". I don't know why you had it,

Habit from ASP/VBScript, where this is the easiest way to handle a
string catenation where the first term isn't already a string.
but it looks like busy-work. Also, I've explicitly called ToString(). I
prefer this to the way you had it, because it avoids the auto-boxing of
the ints that has to happen otherwise. Technically a potential
premature optimization, but it's something I'm in the habit of doing,
especially when dealing with threading code where performance is often
more important.

– the entire example implies that your GetFiles() method is not
executing on the main GUI thread, otherwise why use BeginInvoke()?

Correct, it isn't.
But
it also implies that the UpdateScreen() method _is_ executed on the main
GUI thread (i.e. that's what BeginInvoke() does), so there should be no
need to call "label1.Refresh()" in the UpdateScreen() method.

If I remove it, label1 only updates during rests between groups of files
that are being retrieved in the background. When it's there, the label
changes continuously as the file names are being listed to the textbox.
(In
general, if you catch yourself calling Control.Refresh(), that's a sign
the code needs some proper threading, but here it appears you can just
take it out altogether. You're already doing the threading work).
[snip]

As always, thanks. And Happy New Year!
 
P

Peter Duniho

Harlan said:
[...]
When you use an anonymous method (not "anonymous delegate"), variables

I suppose not "anonymous delegate", but I didn't mean "anonymous method"
either. I was focused on the anonymous delegate *type*.

Sorry. There's no such thing as an "anonymous delegate type" in C#.
As opposed to
declaring a named delegate type like MyDelegate below,

public delegate void MyDelegate(string x, string, y);
...
BeginInvoke(new MyDelegate(UpdateScreen),
new object[] { file, status });

Whether you declare your own delegate type, or you use one of .NET's,
you still have a named delegate type.

Note that for BeginInvoke(), the argument type is Delegate, a base class
for all delegate types. But that type is still named, and Delegate
being abstract, you _still_ need to provide some other named delegate
type (e.g. MethodInvoker) in order for the anonymous method to work
(since the compiler can't instantiate the abstract type). The compiler
has to have a named type somewhere to tell it what type to use for the
delegate instance implicitly created by using the anonymous method.
(where this way of calling BeginInvoke is what I'd had in mind), I had
in mind that there would be some syntax analogous to non-typedef'ed
function pointers in C++ that would allow something like

BeginInvoke(new void delegate(string, string)(UpdateScreen),
new object[]{ file, status });

where I would wrap a typed delegate around UpdateScreen without having
to name the type and declare it in advance. Thus, an anonymous delegate
type.

Unfortunately, there's no such thing in C#.
[...]
So in your anonymous method, the "file" variable that is captured is
the same one always, while the "status" variable is a new variable
each time through the loop. Each anonymous method gets a different
"status" variable, but they are all using the same "file" variable.

Interesting. But difficult to comprehend. It kind of screws with the
fact that the anonymous method is a black box until it's finally
invoked,

I'm not sure what you mean by "is a black box". Other than variable
capturing, anonymous methods are just like regular methods, except they
have no name. They are no more a "black box" than any method is.

Variable capturing exists so that variables declared outside the
anonymous method can still be used, and is a source of great convenience
with respect to anonymous methods.
at which point the call to UpdateScreen is still a method
taking two arguments, each of which is a reference type being passed by
value, and yet they are treated differently because now, for the status
variable, earlier values were somehow preserved for use now, while the
same isn't true of the file variable, because of scope.

The way that scope affects variables for anonymous methods is actually
just like scope affects variables in other contexts. A variable name is
valid only within the scope in which it's declared. As soon as you exit
that scope, the variable is no longer valid.

It's the same reason that when you have local variables in a loop, such
this loop that has no anonymous method:

bool fFirst = true;
foreach (string str in files)
{
int count;

if (fFirst)
{
count = 0;
fFirst = false;
}
else
{
count++;
}
}

…each time through the loop, you get a whole new "count" variable. The
above code is invalid, because the "else" code path attempts to use
"count" before it's been initialized. And that's because (in part, at
least) because each time through the loop, the code is dealing with a
whole new "count" variable.

(Even if "count" were pulled out of the loop, the compiler still
wouldn't be able to detect that it's been initialized, but in the above
example, it really _isn't_ initialized except for the first time through
the loop).
I've gotta go
find me a resource. I *believe* you, and I understand it at a surface
level, but I gotta get a deeper grasp on this.

Well, of course the C# specification is _the_ go-to resource. :)
[...]
But it also implies that the UpdateScreen() method _is_ executed on
the main GUI thread (i.e. that's what BeginInvoke() does), so there
should be no need to call "label1.Refresh()" in the UpdateScreen()
method.

If I remove it, label1 only updates during rests between groups of files
that are being retrieved in the background.

That's not precisely true.
When it's there, the label
changes continuously as the file names are being listed to the textbox.

The label will eventually get updated, even if the background thread
doesn't pause. But it will depend on the thread preemption, and you are
right that you won't see every value presented to the label control,
because it can change many times before it gets a chance to paint itself.

On the other hand, call Refresh() is probably doing around as much to
slow your code down as calling Invoke() instead of BeginInvoke() would.
Drawing's not cheap, and keeping your main GUI thread busy drawing for
every single change to the label's value just means it will take that
much longer for other GUI updates to occur (repainting of other controls
for other reasons, responding to user actions, etc.), as well as keeping
a CPU core occupied while the GUI thread sits there processing all of
the updates to the label control, redrawing it for each and every update
(a CPU core that could be used to do other background processing, should
there be any to do).

As a general rule, if the GUI thread cannot keep up with the frequency
of changes of information being presented to the user, then the user
themselves really has no need to be informed of each and every change
that occurs.

Ironically, Refresh() may be so slow that it slows the frequency of
changes in the UI enough that the user can notice each change. But
that's actually a big problem: it means your GUI thread is not actually
keeping up with what's going on in the background.

In fact, with this design and depending on what work the background
thread is actually doing, there's a good possibility that your GUI
thread will have to sit there redrawing the label control over and over
again long after the background thread has actually completed. And
regardless, the design does not really provide a real-time indication to
the user of what work has actually been completed.

Which is all a long way of saying, I wouldn't include the call to
Refresh() there. :)

Pete
 
H

Harlan Messinger

Peter said:
Harlan said:
[...]
When you use an anonymous method (not "anonymous delegate"), variables

I suppose not "anonymous delegate", but I didn't mean "anonymous method"
either. I was focused on the anonymous delegate *type*.

Sorry. There's no such thing as an "anonymous delegate type" in C#.
As opposed to
declaring a named delegate type like MyDelegate below,

public delegate void MyDelegate(string x, string, y);
...
BeginInvoke(new MyDelegate(UpdateScreen),
new object[] { file, status });

Whether you declare your own delegate type, or you use one of .NET's,
you still have a named delegate type.

Note that for BeginInvoke(), the argument type is Delegate, a base class
for all delegate types. But that type is still named, and Delegate
being abstract, you _still_ need to provide some other named delegate
type (e.g. MethodInvoker) in order for the anonymous method to work
(since the compiler can't instantiate the abstract type). The compiler
has to have a named type somewhere to tell it what type to use for the
delegate instance implicitly created by using the anonymous method.
(where this way of calling BeginInvoke is what I'd had in mind), I had
in mind that there would be some syntax analogous to non-typedef'ed
function pointers in C++ that would allow something like

BeginInvoke(new void delegate(string, string)(UpdateScreen),
new object[]{ file, status });
where I would wrap a typed delegate around UpdateScreen without
having
to name the type and declare it in advance. Thus, an anonymous delegate
type.

Unfortunately, there's no such thing in C#.

It seems bizarre to me that they went out of their way to simplify
coding by implementing anonymous methods--sparing developers the trouble
of having to formally set up a named method--and wound up making them
explicitly declare a named delegate type, even for a single use, instead.

I wonder if they couldn't have made it even simpler than my fake code
above. I know they can't just let you use the name of the method and let
it go at that because methods can be overloaded and the compiler has to
know which method by that name you meant, but syntax identifying the
signature, like

BeginInvoke(UpdateScreen(string, string))

seems like it should have been manageable. The return type shouldn't
even need to be stated, since for a method by the name given, it can be
inferred from the method's signature.

[snipping discussion of the use of Refresh on the label--understood, thanks]
 
P

Peter Duniho

Harlan said:
[...] Thus, an anonymous delegate type.

Unfortunately, there's no such thing in C#.

It seems bizarre to me that they went out of their way to simplify
coding by implementing anonymous methods--sparing developers the trouble
of having to formally set up a named method--and wound up making them
explicitly declare a named delegate type, even for a single use, instead.

What alternative would you suggest?

First, keep in mind that Control.Invoke() and Control.BeginInvoke() were
present long before anonymous methods and the compiler-inferred delegate
construction syntax existed (the latter referring to the ability to just
use the method name, rather than writing out "new
MyDelegateType(MethodName)". The compiler wasn't doing _any_ delegate
inferences when those methods were designed.

Second, keep in mind that for the compiler to infer a delegate type, a
non-abstract one needs to be provided. But, you can use
Control.Invoke() and Control.BeginInvoke() with _any_ delegate
signature. With a delegate that takes arguments, you simply need to
provide an array of the argument values as necessary.

I would be curious what design, given the above, you would offer in lieu
of what we have now.
I wonder if they couldn't have made it even simpler than my fake code
above.

How is this (what you posted):

BeginInvoke(new void delegate(string, string)(UpdateScreen),
new object[]{ file, status });

better than this (something you can do in .NET today):

BeginInvoke((Action<string, string>)UpdateScreen,
new object[]{ file, status });

?
I know they can't just let you use the name of the method and let
it go at that because methods can be overloaded and the compiler has to
know which method by that name you meant, but syntax identifying the
signature, like

BeginInvoke(UpdateScreen(string, string))

seems like it should have been manageable.

The question is, manageable for whom? Today, a method name followed by
an open paren is _always_ a method call. Introducing a new possibility
significantly complicates the grammar of C#, creating problems in the
design of the language as well as the implementation of the compiler.

I think the bottom line is probably that, at least as of today,
anonymous delegate types would have very limited utility, especially as
compared to the cost of implementing them in the language and the
compiler. The current compiler syntax sugar already provides much of
the same degree of convenience, at a much lower cost (and even that cost
has been non-trivial).

Pete
 
H

Harlan Messinger

Peter said:
Harlan said:
[...] Thus, an anonymous delegate type.

Unfortunately, there's no such thing in C#.

It seems bizarre to me that they went out of their way to simplify
coding by implementing anonymous methods--sparing developers the
trouble of having to formally set up a named method--and wound up
making them explicitly declare a named delegate type, even for a
single use, instead.

What alternative would you suggest?

First, keep in mind that Control.Invoke() and Control.BeginInvoke() were
present long before anonymous methods and the compiler-inferred delegate
construction syntax existed (the latter referring to the ability to just
use the method name, rather than writing out "new
MyDelegateType(MethodName)". The compiler wasn't doing _any_ delegate
inferences when those methods were designed.

Second, keep in mind that for the compiler to infer a delegate type, a
non-abstract one needs to be provided. But, you can use
Control.Invoke() and Control.BeginInvoke() with _any_ delegate
signature. With a delegate that takes arguments, you simply need to
provide an array of the argument values as necessary.

I would be curious what design, given the above, you would offer in lieu
of what we have now.
I wonder if they couldn't have made it even simpler than my fake code
above.

How is this (what you posted):

BeginInvoke(new void delegate(string, string)(UpdateScreen),
new object[]{ file, status });

better than this (something you can do in .NET today):

BeginInvoke((Action<string, string>)UpdateScreen,
new object[]{ file, status });

?

I didn't say it was. I didn't *know* about this. Now I feel like you
were holding out on me. :) This seems to be just what I was looking for.
The question is, manageable for whom? Today, a method name followed by
an open paren is _always_ a method call. Introducing a new possibility
significantly complicates the grammar of C#, creating problems in the
design of the language as well as the implementation of the compiler.

Lambda expressions, LINQ, auto-implemented properties, anonymous
methods, and "yield return" all significantly complicated the grammar of
C#, creating problems in the design of the language as well as in the
implementation of the compiler. So I'm not seeing that complications to
the grammar and problems in the design of the language and the compiler
implementation ought to be a bar to my suggesting even one *more* way to
simplify the act of coding. Besides which, this is moot because you just
showed me that they *did* add the feature I was asking about, albeit in
a different manner and with a different syntax.
 
P

Peter Duniho

Harlan said:
[...]
Lambda expressions, LINQ, auto-implemented properties, anonymous
methods, and "yield return" all significantly complicated the grammar of
C#, creating problems in the design of the language as well as in the
implementation of the compiler.

It's a "cost/benefit" analysis, not a "cost" analysis. :) If there's a
significant enough benefit, the cost is worthwhile.
So I'm not seeing that complications to
the grammar and problems in the design of the language and the compiler
implementation ought to be a bar to my suggesting even one *more* way to
simplify the act of coding.

Because the benefit isn't large enough to justify the cost. Anonymous
delegate types would be a fairly large cost, for a relatively tiny
benefit. The other features you mention may in fact cost more, but they
also add MUCH more in the way of benefit.
Besides which, this is moot because you just
showed me that they *did* add the feature I was asking about, albeit in
a different manner and with a different syntax.

I suppose. I wouldn't actually call it the same feature. After all,
the delegate type still has a name. It's hardly anonymous. :)

But yes, since .NET 3.0, there is much less need for one to define one's
own delegate type for one-off uses like that.

Pete
 
P

Peter Duniho

Peter said:
[...]
But yes, since .NET 3.0, there is much less need for one to define one's
own delegate type for one-off uses like that.

By the way, I should point out: Control.Invoke()/Control.BeginInvoke()
has a "fast path" for when the passed-in delegate type is MethodInvoker
(and maybe one or two others I am forgetting at the moment). So if you
are trying to tweak things to squeeze the last bit of performance out of
what's a relatively expensive operation, it's worthwhile to make sure
you're using that delegate type.

Of course, there's non-trivial overhead for an anonymous method that
captures variables. You'd have to measure to see which is better in a
given circumstance: MethodInvoker with an anonymous method (to map one
signature to another) vs. a generic delegate type with a direct call (no
anonymous method capturing class, but then you have to allocate and fill
the parameters array).

But if your method already matches the MethodInvoker delegate type,
that's definitely the one to use.

Pete
 
H

Harlan Messinger

Peter said:
Harlan said:
[...]
Lambda expressions, LINQ, auto-implemented properties, anonymous
methods, and "yield return" all significantly complicated the grammar
of C#, creating problems in the design of the language as well as in
the implementation of the compiler.

It's a "cost/benefit" analysis, not a "cost" analysis. :) If there's a
significant enough benefit, the cost is worthwhile.
So I'm not seeing that complications to the grammar and problems in
the design of the language and the compiler implementation ought to be
a bar to my suggesting even one *more* way to simplify the act of coding.

Because the benefit isn't large enough to justify the cost. Anonymous
delegate types would be a fairly large cost, for a relatively tiny
benefit. The other features you mention may in fact cost more, but they
also add MUCH more in the way of benefit.
Besides which, this is moot because you just showed me that they *did*
add the feature I was asking about, albeit in a different manner and
with a different syntax.

I suppose. I wouldn't actually call it the same feature. After all,
the delegate type still has a name. It's hardly anonymous. :)

*Does* it have a name? What is its name? I thought like anonymous
methods and the backing variables for auto-implemented properties, they
were items that dared not speak their names. :)
 
P

Peter Duniho

Harlan said:
[...]
I suppose. I wouldn't actually call it the same feature. After all,
the delegate type still has a name. It's hardly anonymous. :)

*Does* it have a name?
Sure.

What is its name?

The name of the generic type is Action<T1, T2>. The name of the
concrete type is Action<string, string> (well, at least as far as the
user code goes…if you call GetType().GetName() on the type, you get
something a little less readable, just like for any generic type).
I thought like anonymous
methods and the backing variables for auto-implemented properties, they
were items that dared not speak their names. :)

No, the delegate type I showed is just a normal generic type.

You wouldn't say that Dictionary<TKey, TValue> was anonymous, would you?

Pete
 
H

Harlan Messinger

Peter said:
Harlan said:
[...]
I suppose. I wouldn't actually call it the same feature. After all,
the delegate type still has a name. It's hardly anonymous. :)

*Does* it have a name?
Sure.

What is its name?

The name of the generic type is Action<T1, T2>. The name of the
concrete type is Action<string, string> (well, at least as far as the
user code goes…if you call GetType().GetName() on the type, you get
something a little less readable, just like for any generic type).
I thought like anonymous methods and the backing variables for
auto-implemented properties, they were items that dared not speak
their names. :)

No, the delegate type I showed is just a normal generic type.

You wouldn't say that Dictionary<TKey, TValue> was anonymous, would you?

OK, I see what you mean. For some reason I was thinking of Action as
LINQese for something unnamed happening under the hood. Sorry for my
confusion!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top