Discussion:
C# Command to Wait a Specified Period of Time
(too old to reply)
Robert E. Flaherty
2008-01-15 00:21:01 UTC
Permalink
What is the C# command to wait for a specified period of time?

I am writing a windows service that will process a file once it has beed
created or changed. I'm using the fileSystemWatcher to detect when a
specific file has been created or changed. That works fine to a point. The
event does fire when the file is being created or changed but I then blow up
when I attempt to open the file because the creation or changing has not
finished.

Using the try/catch structure, I am wanting to attempt to open the file. If
it is still being used, then wait a minute and try again. I rather use a
command (assuming that one exists) to sit there for say a minute and try
again.

Using a timer evolves a callback. I was hoping for a simple solution.
Michael C
2008-01-15 00:40:23 UTC
Permalink
Post by Robert E. Flaherty
What is the C# command to wait for a specified period of time?
I am writing a windows service that will process a file once it has beed
created or changed. I'm using the fileSystemWatcher to detect when a
specific file has been created or changed. That works fine to a point.
The
event does fire when the file is being created or changed but I then blow up
when I attempt to open the file because the creation or changing has not
finished.
Using the try/catch structure, I am wanting to attempt to open the file.
If
it is still being used, then wait a minute and try again. I rather use a
command (assuming that one exists) to sit there for say a minute and try
again.
Using a timer evolves a callback. I was hoping for a simple solution.
Something like this will work. You can't sleep for 60 seconds because your
service won't stop for 60 seconds if someone pushes stop.

for(int i = 0; i < 60; i++)
{
System.Threading.Thread.Sleep(1000);
if(StoppHasBinPushed) break;
}
Peter Duniho
2008-01-15 01:06:03 UTC
Permalink
On Mon, 14 Jan 2008 16:21:01 -0800, Robert E. Flaherty =
[...]
Using the try/catch structure, I am wanting to attempt to open the =
file. If
it is still being used, then wait a minute and try again. I rather us=
e a
command (assuming that one exists) to sit there for say a minute and t=
ry
again.
Using a timer evolves a callback. I was hoping for a simple solution.=
What's not simple about using a timer?

Timer timer =3D new System.Windows.Forms.Timer();

timer.Interval =3D 60000;
timer.Tick +=3D delegate
{
if (!fRetry || TryOpenFile())
{
timer.Stop();
}
};
timer.Start();

Where the method "TryOpenFile()" contains your logic to attempt the =

operation with a try/catch exception handler, returning "true" on succes=
s, =

"false" on failure. The "fRetry" flag is provided for convenience, to =

support canceling as suggested by Michael. Declare it somewhere else, =

initialize to "false", set it to true if you want to cancel the operatio=
n =

before it's completed.

Alternatively, you could just store a reference to the timer elsewhere a=
nd =

stop the timer explicitly when the operation needs to be canceled.

I don't see the point in code like Michael's. Just as it doesn't make =

sense to block for 60 seconds, preventing the user from canceling the =

operation until then, it also doesn't make sense to block for 1 second, =
=

preventing the user from canceling the operation until then. You =

shouldn't block at all. Just set up a timer, try the operation on every=
=

tick, and once it succeeds, stop the timer.

It's really not that complicated. The number of lines of code is about =
=

the same as Michael's proposal, and this implementation has the added =

benefit of not doing something bad like blocking the GUI thread for =

extended periods of time.

You could, of course, put the operation on a whole new thread and let it=
=

sit and wait there. Then you could just call Sleep(60000) and perform =

whatever logic, including checking for a successful file open as well as=
=

having the operation canceled, in a loop there:

while (!fRetry)
{
if (TryOpenFile())
{
break;
}
Thread.Sleep(60000);
}

But then you need to put that code in a thread somewhere, as well as dea=
l =

with any cross-thread issues. I'd hardly say that's simpler solution th=
an =

using a timer.

Pete
Michael C
2008-01-15 02:23:52 UTC
Permalink
Post by Peter Duniho
I don't see the point in code like Michael's. Just as it doesn't make
sense to block for 60 seconds, preventing the user from canceling the
operation until then, it also doesn't make sense to block for 1 second,
preventing the user from canceling the operation until then. You
shouldn't block at all. Just set up a timer, try the operation on every
tick, and once it succeeds, stop the timer.

Doesn't this apply equally to all code that blocks then? Most database
lookups of any complexity will block for 1 second. In this situation we can
just reduce the interval to 1/10th. While your code is a similar number of
lines it is more complex. I can't say I'm a big fan of those inline
delegates.
Peter Duniho
2008-01-15 04:55:58 UTC
Permalink
Post by Michael C
Doesn't this apply equally to all code that blocks then? Most database
lookups of any complexity will block for 1 second. In this situation we can
just reduce the interval to 1/10th. While your code is a similar number of
lines it is more complex. I can't say I'm a big fan of those inline
delegates.
Why not? They work quite well, especially when you take advantage of the
variable-capturing they provide. They can greatly simplify your code, and
IMHO this is a good example of them doing just that. An implementation
not using an anonymous method would require at least twice as much
effort. Not that the effort would be great in either case, but the
alternative is less readable as well.

I would also disagree that my code is "more complex". It only appears
that way because you left out at least a couple of statements that are
required: the line that actually attempts to do the file open, and the
line that (ick!) calls DoEvents() so that you can respond to user input.

I personally find my code _simpler_, not more complex. It's not
re-entrant and it doesn't cause the thread to get stuck in the Form's
event-handling procedure for extended periods of time.

In any case, yes..."this" does apply equally to all code that blocks.
IMHO, it's not a good idea to put blocking code in the GUI thread. For
some "quick and dirty" or extremely simple applications it might be okay,
but it's not something that should show up in anything serious, and this
is especially true when the blocking could be significantly long.

Pete
Michael C
2008-01-15 23:32:48 UTC
Permalink
Post by Peter Duniho
Why not? They work quite well, especially when you take advantage of the
variable-capturing they provide. They can greatly simplify your code, and
IMHO this is a good example of them doing just that. An implementation
not using an anonymous method would require at least twice as much
effort. Not that the effort would be great in either case, but the
alternative is less readable as well.
It's not really clear what's going on. In order to be called back into they
must really be compiled as a function but they can access the original
function's local variables. It's not worth the code it saved imo.
Post by Peter Duniho
I would also disagree that my code is "more complex". It only appears
that way because you left out at least a couple of statements that are
required: the line that actually attempts to do the file open, and the
line that (ick!) calls DoEvents() so that you can respond to user input.
Complexity and line count are not the same thing. You create a timer object
and a delegate.
Post by Peter Duniho
I personally find my code _simpler_, not more complex. It's not
re-entrant and it doesn't cause the thread to get stuck in the Form's
event-handling procedure for extended periods of time.
As I said, line count does not equate to complexity.
Post by Peter Duniho
In any case, yes..."this" does apply equally to all code that blocks.
IMHO, it's not a good idea to put blocking code in the GUI thread. For
some "quick and dirty" or extremely simple applications it might be okay,
but it's not something that should show up in anything serious, and this
is especially true when the blocking could be significantly long.
I doubt there is an app in existance that puts every single call longer than
1 second on a seperate thread. It's probably a good ideal but is just that.

Michael
Peter Duniho
2008-01-16 01:17:28 UTC
Permalink
Post by Michael C
It's not really clear what's going on. In order to be called back into they
must really be compiled as a function but they can access the original
function's local variables. It's not worth the code it saved imo.
Well, I guess you're entitled to your opinion. IMHO, nothing could be
clearer than putting all of the relevant code in one place, rather than
making someone go looking for it.
Post by Michael C
Complexity and line count are not the same thing. You create a timer object
and a delegate.
And you call DoEvents(), which hides a remarkable degree of complexity
(such as the re-entrant behavior I mentioned).
Post by Michael C
As I said, line count does not equate to complexity.
I'm not saying it does. I'm saying you glossed over the complexity in
your own suggestion.
Post by Michael C
I doubt there is an app in existance that puts every single call longer than
1 second on a seperate thread.
Every .NET application I've written falls into that category. So I know
there's at least some applications in existence that qualifies.
Post by Michael C
It's probably a good ideal but is just that.
What's the point of having a "good ideal" if you don't actually strive to
achieve the ideal?

Pete
Michael C
2008-01-16 02:03:07 UTC
Permalink
Post by Peter Duniho
Well, I guess you're entitled to your opinion. IMHO, nothing could be
clearer than putting all of the relevant code in one place, rather than
making someone go looking for it.
IMO it's not MUCH better to have the code in a seperate function. In your
example we actually exit the function but jump back into it right in the
middle!! Yuk!
Post by Peter Duniho
And you call DoEvents(), which hides a remarkable degree of complexity
(such as the re-entrant behavior I mentioned).
Naturally either solution is potentially re-entrant.
Post by Peter Duniho
I'm not saying it does. I'm saying you glossed over the complexity in
your own suggestion.
Having a message queue is part of any windows app. Allowing that to process
is minor.
Post by Peter Duniho
Every .NET application I've written falls into that category. So I know
there's at least some applications in existence that qualifies.
So you start a new thread to show a form? :-) Every application I've ever
used, including stuff from the big names, blocks the GUI at one point or
another. I'd be suprised if I couldn't find somewhere your apps didn't do
the same.
Post by Peter Duniho
What's the point of having a "good ideal" if you don't actually strive to
achieve the ideal?
Generally they fall over in reality.

Michael
Michael C
2008-01-16 02:16:35 UTC
Permalink
Post by Michael C
IMO it's not MUCH better to have the code in a seperate function. In your
example we actually exit the function but jump back into it right in the
middle!! Yuk!
That should read "it's much better" :-)

Michael
Peter Duniho
2008-01-16 02:28:49 UTC
Permalink
Post by Michael C
IMO it's not MUCH better to have the code in a seperate function. In your
example we actually exit the function but jump back into it right in the
middle!! Yuk!
The anonymous method is not part of the method in which it's declared.
It's a complete method unto itself. The execution of the anonymous method
does not "jump back into" the method in which it's declared.

Perhaps the reason you don't like anonymous methods is that you have
failed to conceptualize them in a useful way. It's true that when you
don't understand something, it seems a lot less useful than when you do.
Post by Michael C
Post by Peter Duniho
And you call DoEvents(), which hides a remarkable degree of complexity
(such as the re-entrant behavior I mentioned).
Naturally either solution is potentially re-entrant.
Negative. An anonymous method doesn't have any of the re-entrancy issues
that calling DoEvents() does.
Post by Michael C
Post by Peter Duniho
I'm not saying it does. I'm saying you glossed over the complexity in
your own suggestion.
Having a message queue is part of any windows app.
Having a _synchronous_ message queue is a normal part of any Windows
application.
Post by Michael C
Allowing that to process is minor.
"Minor" is in the eye of the beholder. Suffice to say, I disagree with
the claim that completely changing the messaging architecture of an
application is a "minor" alteration.

Pete
Michael C
2008-01-16 03:07:20 UTC
Permalink
Post by Peter Duniho
The anonymous method is not part of the method in which it's declared.
It's a complete method unto itself. The execution of the anonymous method
does not "jump back into" the method in which it's declared.
Perhaps the reason you don't like anonymous methods is that you have
failed to conceptualize them in a useful way. It's true that when you
don't understand something, it seems a lot less useful than when you do.
Granted I don't understand what's going on under the hood but that is part
of the problem. As I said previously it must compile to a seperate function
but contains another functions local variables. I say again Yuk!
Post by Peter Duniho
Negative. An anonymous method doesn't have any of the re-entrancy issues
that calling DoEvents() does.
I don't see how you can say that. The only way mine can be re-entrant is if
the user clicks the GO button again. Yours suffers the same problem as far
as I can see as someone can kick off the entire process again if they like.
Post by Peter Duniho
"Minor" is in the eye of the beholder. Suffice to say, I disagree with
the claim that completely changing the messaging architecture of an
application is a "minor" alteration.
From the programmers pov doevents is quite simple. While your method isn't
much more complicated it still is.

BTW, who said anything about DoEvents. It was you who added the idea of
using DoEvents. And who said anything about a GUI, this is a windows
service.

Michael
Peter Duniho
2008-01-16 03:42:44 UTC
Permalink
Post by Michael C
Granted I don't understand what's going on under the hood but that is part
of the problem.
IMHO, it's poor practice to denigrate a language feature if you don't
fully understand it.
Post by Michael C
As I said previously it must compile to a seperate function
but contains another functions local variables. I say again Yuk!
And I say "Aha!" Variable capturing is one of the things that makes
anonymous methods so useful.
Post by Michael C
Post by Peter Duniho
Negative. An anonymous method doesn't have any of the re-entrancy issues
that calling DoEvents() does.
I don't see how you can say that. The only way mine can be re-entrant is if
the user clicks the GO button again.
Actually, it's re-entrant the moment you call DoEvents(). "Under the
hood", you've got a window's "window proc" (aka "wndproc") that is busy
handling a specific window message, having been called from a message pump
loop. When you call DoEvents(), that enters an entirely new message pump
loop, which will in turn call the window proc again.

Just because you don't see the re-entrancy, that doesn't mean it doesn't
exist, nor does it mean there's no potential for a problem. The user
clicking the same button again isn't the issue, and in fact I would expect
that a proper program would disable the button if it's inappropriate for
it to be clicked. It's the question of the fact that your window hasn't
finished handling one window message, and now may be asked to process any
number of new window messages.

It's an intractable design problem inherent in calling DoEvents(). It
leads to code that is at best difficult to maintain, and at worst can
contain subtle bugs that are difficult to find, never mind solve.
Post by Michael C
Yours suffers the same problem as far
as I can see as someone can kick off the entire process again if they like.
Mine ensures that the Form class is handling one message at a time. You
can always _add_ re-entrancy to the code, but it's not inherent in the
anonymous method technique, while it is inherent in using DoEvents().
Post by Michael C
From the programmers pov doevents is quite simple. While your method isn't
much more complicated it still is.
DoEvents() only _seems_ to be simple. The fact that you believe it
actually _is_ simple is a symptom of the basic problem. DoEvents() has
fooled you into thinking that it's simple, when in fact it's actually
quite a complicated addition to the architecture.
Post by Michael C
BTW, who said anything about DoEvents. It was you who added the idea of
using DoEvents. And who said anything about a GUI, this is a windows
service.
I can only laugh at this point. It appears that I missed an important
aspect of the original question (it's a service), and you missed an
important aspect of my reply: I used the System.Windows.Forms.Timer class
in a context where that class isn't useful or desirable at all.

That said, consider "GUI" as a general talking point if you will. The
issue here is whether the thread is available to process other activity.
You wrote "You can't sleep for 60 seconds because your service won't stop
for 60 seconds if someone pushes stop", so obviously you anticipate doing
_something_ inside that loop that allows the service to process other
input.

Whether that something is DoEvents() or something else, the same basic
issues apply though I agree that some specifics that apply to DoEvents()
obviously wouldn't in other implementations. In particular, there are
still re-entrancy issues and depending on the architecture of the service
they could be even more complex than those introduced by the use of
DoEvents() in a Forms application.

Now, in the context of a service, timers get more complicated than when
using System.Windows.Forms.Timer in a Forms application. All of what I
wrote was aimed specifically at a Forms application, and that's where the
System.Windows.Forms.Timer class is especially helpful. It ensures that
execution of the timer event handler is synchronized with other activity
in the GUI thread. Obviously that benefit doesn't apply when using some
other timer mechanism.

Because of that, in this situation my recommendation would be to run the
"retry" logic on a different thread, per my alternate suggestion in my
original reply.

There is still no need, nor is it desirable, to have any thread wait 60
seconds by actually waiting for 1 second 60 times. Just make the thread
wait 60 seconds and be done with it. If the process needs to be available
to do other work, handle that other work in a different thread.

Pete
Michael C
2008-01-16 05:08:52 UTC
Permalink
Post by Peter Duniho
IMHO, it's poor practice to denigrate a language feature if you don't
fully understand it.
I don't need to understand all details under the hood to dislike a feature.
I'm sure you dislike plenty of features without looking at the assembler.
Post by Peter Duniho
It's an intractable design problem inherent in calling DoEvents(). It
leads to code that is at best difficult to maintain, and at worst can
contain subtle bugs that are difficult to find, never mind solve.
Great. The end result is the same though, both methods can suffer
re-entracny. You're trying a little too hard to find a problem here when
none exists.
Post by Peter Duniho
Mine ensures that the Form class is handling one message at a time. You
can always _add_ re-entrancy to the code, but it's not inherent in the
anonymous method technique, while it is inherent in using DoEvents().
Rubbish, the exact same issue exists. The only possible problem is the user
clicking the button again and both methods have this problem.
Post by Peter Duniho
DoEvents() only _seems_ to be simple. The fact that you believe it
actually _is_ simple is a symptom of the basic problem. DoEvents() has
fooled you into thinking that it's simple, when in fact it's actually
quite a complicated addition to the architecture.
Maybe it is under the hood but from my pov (the programmers) it is very
simple.
Post by Peter Duniho
I can only laugh at this point.
That is where I exit this thread.

Michael
Peter Duniho
2008-01-16 07:58:13 UTC
Permalink
Post by Michael C
I don't need to understand all details under the hood to dislike a feature.
I'm sure you dislike plenty of features without looking at the assembler.
Apple and oranges.
Post by Michael C
Great. The end result is the same though, both methods can suffer
re-entracny. You're trying a little too hard to find a problem here when
none exists.
I didn't have to try hard at all. I have no idea what you're talking
about.
Post by Michael C
Rubbish, the exact same issue exists. The only possible problem is the user
clicking the button again and both methods have this problem.
Wrong. As I already explained, I expect the button to be disabled if
clicking it is a problem. That's not what I'm talking about.
Post by Michael C
Maybe it is under the hood but from my pov (the programmers) it is very
simple.
The behavior is not.
Post by Michael C
Post by Peter Duniho
I can only laugh at this point.
That is where I exit this thread.
Knee-jerk. You obviously didn't even read what I wrote. The laughing was
at myself.

Pete
Scott Roberts
2008-01-16 17:28:46 UTC
Permalink
Post by Michael C
Post by Peter Duniho
DoEvents() only _seems_ to be simple. The fact that you believe it
actually _is_ simple is a symptom of the basic problem. DoEvents() has
fooled you into thinking that it's simple, when in fact it's actually
quite a complicated addition to the architecture.
Maybe it is under the hood but from my pov (the programmers) it is very
simple.
It may be simple to call DoEvents(), but as Peter has already said, it will
be very difficult (if not impossible) to find and correct all of the
problems you have caused by doing so.
Scott Roberts
2008-01-16 17:32:26 UTC
Permalink
Post by Peter Duniho
Post by Michael C
IMO it's not MUCH better to have the code in a seperate function. In your
example we actually exit the function but jump back into it right in the
middle!! Yuk!
The anonymous method is not part of the method in which it's declared.
It's a complete method unto itself. The execution of the anonymous method
does not "jump back into" the method in which it's declared.
The "complexity" (if you will) comes from having a method declared right in
the middle of an executing code block. While I agree that this is not a huge
source of complexity, it *is* less readable (IMO, of course).
Jon Skeet [C# MVP]
2008-01-16 19:23:47 UTC
Permalink
Post by Scott Roberts
Post by Peter Duniho
The anonymous method is not part of the method in which it's declared.
It's a complete method unto itself. The execution of the anonymous method
does not "jump back into" the method in which it's declared.
The "complexity" (if you will) comes from having a method declared right in
the middle of an executing code block. While I agree that this is not a huge
source of complexity, it *is* less readable (IMO, of course).
Let's consider an example here, in C# 3. Suppose we want to write a
method which takes an IEnumerable<Person> and returns another
IEnumerable<Person> which has filtered out everyone under a certain
age, which is also a parameter to our method, i.e.:

public IEnumerable<Person> FilterByAge(IEnumerable<Person> input,
int ageLimit)

There are two obvious ways of doing this:
1) Use an iterator block. That's actually doing more behind the scenes
than an anonymous method.

2) Use the "Where" extension method, which accepts a delegate. Now we
have three choices:

a) Use an "old school" delegate. We need to be able to provide the age,
so we've got to write a whole extra class:

public class AgeFilter
{
int ageLimit;

public AgeFilter(int ageLimit)
{
this.ageLimit = ageLimit;
}

public bool Filter(Person p)
{
return p.Age < ageLimit;
}
}

then we implement our method with:

public IEnumerable<Person> FilterByAge(IEnumerable<Person> input,
int ageLimit)
{
AgeFilter filter = new AgeFilter(ageLimit);
return input.Where (filter.Filter);
}

b) We can use an anonymous method. No need for an extra class in our
source code - the compiler will generate it:

public IEnumerable<Person> FilterByAge(IEnumerable<Person> input,
int ageLimit)
{
return input.Where(delegate (Person person)
{ return person.Age < ageLimit; }
);
}

c) We can use a lambda expression:

public IEnumerable<Person> FilterByAge(IEnumerable<Person> input,
int ageLimit)
{
return input.Where(person => person.Age < ageLimit);
}


Now, do you really think that solution a is more obvious in its
intention than solutions b or c?
--
Jon Skeet - <***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Scott Roberts
2008-01-16 20:16:21 UTC
Permalink
Post by Jon Skeet [C# MVP]
Now, do you really think that solution a is more obvious in its
intention than solutions b or c?
No, I think solutions b and/or c are more obvious.

Of course, in your contrived example the anonymous method executes "in-line"
with the declaring code. In Peter's example, the anonymous method executes
on a timer event.

You'll also note that I have already stated that it's not that big of a
deal, but it is slightly less readable. And of course, that's just my
opinion.
Jon Skeet [C# MVP]
2008-01-16 20:45:48 UTC
Permalink
Post by Scott Roberts
Post by Jon Skeet [C# MVP]
Now, do you really think that solution a is more obvious in its
intention than solutions b or c?
No, I think solutions b and/or c are more obvious.
Of course, in your contrived example the anonymous method executes "in-line"
with the declaring code.
No, it doesn't. It only executes as the caller enumerates through the
returned IEnumerable.
Post by Scott Roberts
In Peter's example, the anonymous method executes
on a timer event.
You'll also note that I have already stated that it's not that big of a
deal, but it is slightly less readable. And of course, that's just my
opinion.
I think it's just a matter of getting used to things - and with C# 3
and LINQ, it's really well worth getting used to them.
--
Jon Skeet - <***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Scott Roberts
2008-01-16 20:57:13 UTC
Permalink
Post by Jon Skeet [C# MVP]
Post by Scott Roberts
Of course, in your contrived example the anonymous method executes "in-line"
with the declaring code.
No, it doesn't. It only executes as the caller enumerates through the
returned IEnumerable.
Post by Scott Roberts
In Peter's example, the anonymous method executes
on a timer event.
How about "in your example the anonymous method executes very near the
declaring code, instead of 60 seconds later on a timer event"?
Jon Skeet [C# MVP]
2008-01-16 22:14:59 UTC
Permalink
Post by Scott Roberts
Post by Jon Skeet [C# MVP]
Post by Scott Roberts
Of course, in your contrived example the anonymous method executes "in-line"
with the declaring code.
No, it doesn't. It only executes as the caller enumerates through the
returned IEnumerable.
Post by Scott Roberts
In Peter's example, the anonymous method executes
on a timer event.
How about "in your example the anonymous method executes very near the
declaring code, instead of 60 seconds later on a timer event"?
But my point is that it might be executed much later, or never at all,
or a long way from the original code. There's nothing to say the
returned value will immediately be iterated through.

I could certainly see your point if we were using something like
List<T>.ConvertAll, where the delegate is discarded after the method
call. In this case it's all using deferred execution though. Yet
another thing to understand :)
--
Jon Skeet - <***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Scott Roberts
2008-01-16 23:07:58 UTC
Permalink
Post by Jon Skeet [C# MVP]
Post by Scott Roberts
Post by Jon Skeet [C# MVP]
Post by Scott Roberts
Of course, in your contrived example the anonymous method executes "in-line"
with the declaring code.
No, it doesn't. It only executes as the caller enumerates through the
returned IEnumerable.
Post by Scott Roberts
In Peter's example, the anonymous method executes
on a timer event.
How about "in your example the anonymous method executes very near the
declaring code, instead of 60 seconds later on a timer event"?
But my point is that it might be executed much later, or never at all,
or a long way from the original code. There's nothing to say the
returned value will immediately be iterated through.
Well, there you go. My cursory observation of your code gave a completely
incorrect impression as to when the code would execute.

QED.
Peter Duniho
2008-01-16 23:59:05 UTC
Permalink
On Wed, 16 Jan 2008 15:07:58 -0800, Scott Roberts
Post by Scott Roberts
Well, there you go. My cursory observation of your code gave a
completely incorrect impression as to when the code would execute.
How would defining a method elsewhere and using that to instantiate a
delegate have helped that situation?

The method would still be called when the method would be called. If you
don't understand the timing of when it would be called, that's not because
of the fact that an anonymous method was used instead of a named one.

Pete
Scott Roberts
2008-01-17 02:49:41 UTC
Permalink
Post by Peter Duniho
On Wed, 16 Jan 2008 15:07:58 -0800, Scott Roberts
Post by Scott Roberts
Well, there you go. My cursory observation of your code gave a
completely incorrect impression as to when the code would execute.
How would defining a method elsewhere and using that to instantiate a
delegate have helped that situation?
It wouldn't, but a simple iterative method that executes when it is called
would.
Peter Duniho
2008-01-17 03:21:26 UTC
Permalink
On Wed, 16 Jan 2008 18:49:41 -0800, Scott Roberts
Post by Scott Roberts
Post by Peter Duniho
Post by Scott Roberts
Well, there you go. My cursory observation of your code gave a
completely incorrect impression as to when the code would execute.
How would defining a method elsewhere and using that to instantiate a
delegate have helped that situation?
It wouldn't, but a simple iterative method that executes when it is
called would.
What's that got to do with the question of using an anonymous method
then? It seems to me you are now taking issue with the use of an
IEnumerable implementation that has some deferred processing. It's a
completely orthogonal issue to the question of using an anonymous method.

I fail to see how Jon's example in any way suggests that use of an
anonymous method is less readable. You may in fact find it less readable,
but because you've chosen to go off on a tangent, it's impossible to know
why.

On this topic, in a previous post:

On Wed, 16 Jan 2008 12:57:13 -0800, Scott Roberts
Post by Scott Roberts
How about "in your example the anonymous method executes very near the
declaring code, instead of 60 seconds later on a timer event"?
You seem to be confusing temporal proximity with spatial proximity. What
difference does it make _when_ the method executes, as far as the use of
an anonymous method is concerned? Wherever you can use an anonymous
method, you can always declare a named method elsewhere that does the same
thing. This is a question of spatial proximity.

But if an anonymous method is used in a situation where it's not executed
until 60 seconds later, then even a named method would not be executed
until 60 seconds later. This is a question of temporal proximity, and it
has nothing to do with anonymous methods whatsoever. Mentioning "60
seconds later", a matter of temporal proximity, is completely off-topic in
a discussion about the usefulness of anonymous methods, being entirely
irrelevant to the question of anonymous methods. Anonymous methods don't
in and of themselves impose any sort of temporal structure onto the code.

I admit that code that executes in a temporarily separated manner can be
harder to read. It requires an expanded mental picture of the structure
of the code, one that incorporates a timeline. But anonymous methods
don't cause this to happen, and if you've got something that has to be
done at some time in the future, you are necessarily going to have code
that executes in a temporarily separated manner and any associated
conceptual issues. There's no way around that, by definition.

Pete
Scott Roberts
2008-01-17 04:29:07 UTC
Permalink
Not only do you possess superior knowledge of C#, you also type much faster.
:)

See my other post for general info on the point I have been trying to make.
See below for responses to your post.
Post by Peter Duniho
Post by Scott Roberts
How about "in your example the anonymous method executes very near the
declaring code, instead of 60 seconds later on a timer event"?
You seem to be confusing temporal proximity with spatial proximity. What
difference does it make _when_ the method executes, as far as the use of
an anonymous method is concerned? Wherever you can use an anonymous
method, you can always declare a named method elsewhere that does the same
thing. This is a question of spatial proximity.
You are 100% correct. The question of spacial and temporal proximity is
precisely what made your original code "less readable". You have a code
block (anonymous method) sitting in-line with the current execution path,
but it doesn't execute in-line. And in fact, it executes much, much later -
on a timer tick. The question of "when does this code execute" is
potentially confused by "where is this code located". Probably not by you,
but perhaps by one of us less-skilled programmers.

How does a named method solve this issue of readability? Well, as you say,
it moves the code for the method out of the linear execution path of the
implementing code.
Peter Duniho
2008-01-17 07:19:11 UTC
Permalink
On Wed, 16 Jan 2008 20:29:07 -0800, Scott Roberts
Post by Scott Roberts
[...]
How does a named method solve this issue of readability? Well, as you
say, it moves the code for the method out of the linear execution path
of the implementing code.
I don't see how that makes it more readable. You have to go somewhere
else to read the code. To me, that's _less_ readable.

That said, I accept that readability is in the eye of the beholder. I'm
happy to agree to disagree.
Scott Roberts
2008-01-17 04:08:44 UTC
Permalink
Post by Scott Roberts
Post by Peter Duniho
On Wed, 16 Jan 2008 15:07:58 -0800, Scott Roberts
Post by Scott Roberts
Well, there you go. My cursory observation of your code gave a
completely incorrect impression as to when the code would execute.
How would defining a method elsewhere and using that to instantiate a
delegate have helped that situation?
It wouldn't, but a simple iterative method that executes when it is called
would.
To be more clear, which of these two code blocks would a college intern be
more likely to read, understand, and modify without introducing errors?

public List<Person> FilterByAge(List<Person> input,
int ageLimit)
{
List<Person> result = new List<Person>();
foreach (Person p in input)
if (p.Age < ageLimit)
result.Add(p);
return result;
}

Or this one:

public List<Person> FilterByAge(List<Person> input,
int ageLimit)
{
return input.Where(delegate (Person person)
{ return person.Age < ageLimit; }
);
}


One uses a standard "for loop", a basic construct available in pretty much
every programming language since assembly. The other uses newer and more
advanced C#-specific constructs that are probably still completely foreign
to 90% (or more) of the current C# community.

The second construct is more elegant, concise, and most probably more
efficient. Peter and Jon, you both clearly have a much greater and deeper
understanding of C# than I (and the vast majority of other C# programmers)
do, but that is *exactly* what makes my opinion on "readability" more
salient than yours. I'm sure that once one gains experience with these newer
language constructs that they do enhance "readability" - for the people who
know and use them. But the fact of the matter is that, at least in my
experience, the vast majority of people making a living as a "programmer"
still struggle with objects and events, let alone delegates, anonymous
methods, and the like. Perhaps that is an indictment against me and my
knowledge (or lack thereof) of C#. I've heard it argued before that if it's
part of the language specification then a "C# programmer" should know how to
read & use it. But I'll tell you right now that there are a bazillion C# and
.Net language features that I don't know and will never know. And you and I
both know that this is true of the vast majority of programmers out there.

So you are more than welcome to continue writing elegant, concise, efficient
code until the cows come home. I hope that you do. But don't go around
saying that a code block consisting of delegates and anonymous methods is
"more readable" than a simple "for loop". And please note that I said
*simple* "for loop". Don't waste your time fabricating yet another
artificial example of a complex iterative loop that is easily solved with
another construct. The point here is not that a more advanced construct
can't be more readable in some situations; the point is that it's not more
readable to a wider audience *for the simple example provided*.

Since Jon has a penchant for ignoring context and missing points, let me
spell out the points I am NOT making:

I am NOT saying that advanced language constructs have no place in C#.

I am NOT saying that advanced language constructs cannot be more readable -
even to a novice programmer - in some situations.

I am certainly NOT saying that simple language constructs are always more
efficient than advanced ones.

I am NOT saying that readability must triumph over elegance and efficiency
in all cases.


And the point I am making:

I AM saying that given a relatively simple task, using a ubiquitous
construct (such as a "for loop") will be more *readable* to a wider audience
than using a more advanced construct.
Bill McCarthy
2008-01-17 04:43:26 UTC
Permalink
Hi Scott,

I would tend to write it as :

public List<Person> FilterByAge(List<Person> input, int ageLimit)
{
return (from p in input
where p.Age<ageLimit
select p)
.ToList<Person>();
}

Although if you look at how simple that statement becomes, you might never
need actually embody that in a function.

But the key to use the query syntax is you don't do the for each loop
yourself, instead you state what you want (declarative), not how to do it
(imperative). Declarative allows the where clause to be used most
efficiently for the input data. For example, let's say the input is now an
abstraction over a database. If you use a foreach loop, you have to get all
the data fro the database to filter it. The where clause however can be
compiled as an expression tree and translated to TSQL. Or another example
is you might want to run this query in parallel on a multiple processor
machine. Again the query syntax is better setup for that than an imperative
foreach loop.
Post by Scott Roberts
Post by Scott Roberts
Post by Peter Duniho
On Wed, 16 Jan 2008 15:07:58 -0800, Scott Roberts
Post by Scott Roberts
Well, there you go. My cursory observation of your code gave a
completely incorrect impression as to when the code would execute.
How would defining a method elsewhere and using that to instantiate a
delegate have helped that situation?
It wouldn't, but a simple iterative method that executes when it is
called would.
To be more clear, which of these two code blocks would a college intern be
more likely to read, understand, and modify without introducing errors?
public List<Person> FilterByAge(List<Person> input,
int ageLimit)
{
List<Person> result = new List<Person>();
foreach (Person p in input)
if (p.Age < ageLimit)
result.Add(p);
return result;
}
public List<Person> FilterByAge(List<Person> input,
int ageLimit)
{
return input.Where(delegate (Person person)
{ return person.Age < ageLimit; }
);
}
One uses a standard "for loop", a basic construct available in pretty much
every programming language since assembly. The other uses newer and more
advanced C#-specific constructs that are probably still completely foreign
to 90% (or more) of the current C# community.
The second construct is more elegant, concise, and most probably more
efficient. Peter and Jon, you both clearly have a much greater and deeper
understanding of C# than I (and the vast majority of other C# programmers)
do, but that is *exactly* what makes my opinion on "readability" more
salient than yours. I'm sure that once one gains experience with these
newer language constructs that they do enhance "readability" - for the
people who know and use them. But the fact of the matter is that, at least
in my experience, the vast majority of people making a living as a
"programmer" still struggle with objects and events, let alone delegates,
anonymous methods, and the like. Perhaps that is an indictment against me
and my knowledge (or lack thereof) of C#. I've heard it argued before that
if it's part of the language specification then a "C# programmer" should
know how to read & use it. But I'll tell you right now that there are a
bazillion C# and .Net language features that I don't know and will never
know. And you and I both know that this is true of the vast majority of
programmers out there.
So you are more than welcome to continue writing elegant, concise,
efficient code until the cows come home. I hope that you do. But don't go
around saying that a code block consisting of delegates and anonymous
methods is "more readable" than a simple "for loop". And please note that
I said *simple* "for loop". Don't waste your time fabricating yet another
artificial example of a complex iterative loop that is easily solved with
another construct. The point here is not that a more advanced construct
can't be more readable in some situations; the point is that it's not more
readable to a wider audience *for the simple example provided*.
Since Jon has a penchant for ignoring context and missing points, let me
I am NOT saying that advanced language constructs have no place in C#.
I am NOT saying that advanced language constructs cannot be more
readable - even to a novice programmer - in some situations.
I am certainly NOT saying that simple language constructs are always more
efficient than advanced ones.
I am NOT saying that readability must triumph over elegance and efficiency
in all cases.
I AM saying that given a relatively simple task, using a ubiquitous
construct (such as a "for loop") will be more *readable* to a wider
audience than using a more advanced construct.
Jon Skeet [C# MVP]
2008-01-17 07:28:02 UTC
Permalink
Post by Bill McCarthy
public List<Person> FilterByAge(List<Person> input, int ageLimit)
{
return (from p in input
where p.Age<ageLimit
select p)
.ToList<Person>();
}
Although if you look at how simple that statement becomes, you might never
need actually embody that in a function.
Interesting - because I tend to avoid using the query expression syntax
when it makes things more longwinded. If we were projecting to
something other than p, or doing things other than Where as well, I'd
use a query expression - but when the above 4 lines can be written as:

return p.Where(person => person.Age < ageLimit)
.ToList();

I don't see much reason to use the query expression.

<snip>
--
Jon Skeet - <***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Bill McCarthy
2008-01-17 09:14:36 UTC
Permalink
Hi Jon,
Post by Jon Skeet [C# MVP]
Post by Bill McCarthy
public List<Person> FilterByAge(List<Person> input, int ageLimit)
{
return (from p in input
where p.Age<ageLimit
select p)
.ToList<Person>();
}
Although if you look at how simple that statement becomes, you might never
need actually embody that in a function.
Interesting - because I tend to avoid using the query expression syntax
when it makes things more longwinded. If we were projecting to
something other than p, or doing things other than Where as well, I'd
return p.Where(person => person.Age < ageLimit)
.ToList();
I don't see much reason to use the query expression.
To me it's six of one half a dozen of the other, but I tend to prefer the
query syntax because it is easy to grow it and modify it, plus you avoid
lambdas which may not be as obvious as to exactly what the extension method
is taking or whether that method is an extension or not. Admittedly I tend
to write more VB, so the query becomes more succinct in query syntax as you
don't need to include the superfluous select clause; and conversely the
lambda syntax in VB is a bit more long winded: Function(person) person.Age <
ageLimit
Jon Skeet [C# MVP]
2008-01-17 09:35:50 UTC
Permalink
On Jan 17, 9:14 am, "Bill McCarthy" <***@NOSPAM.com> wrote:

<snip>
Post by Bill McCarthy
Post by Jon Skeet [C# MVP]
I don't see much reason to use the query expression.
To me it's six of one half a dozen of the other, but I tend to prefer the
query syntax because it is easy to grow it and modify it, plus you avoid
lambdas which may not be as obvious as to exactly what the extension method
is taking or whether that method is an extension or not.
Well, it's not really avoiding the lambdas - it's just hiding them.
Ditto whether or not it's an extension method. The compiler is
translating the query expression into exactly the non-query-expression
code, so any issues in one form are going to be issues in the other
too.
Post by Bill McCarthy
Admittedly I tend
to write more VB, so the query becomes more succinct in query syntax as you
don't need to include the superfluous select clause; and conversely the
lambda syntax in VB is a bit more long winded: Function(person) person.Age <
ageLimit
Now that's a more practical consideration :)

Jon
Bill McCarthy
2008-01-17 10:12:39 UTC
Permalink
Post by Jon Skeet [C# MVP]
<snip>
Post by Bill McCarthy
Post by Jon Skeet [C# MVP]
I don't see much reason to use the query expression.
To me it's six of one half a dozen of the other, but I tend to prefer the
query syntax because it is easy to grow it and modify it, plus you avoid
lambdas which may not be as obvious as to exactly what the extension method
is taking or whether that method is an extension or not.
Well, it's not really avoiding the lambdas - it's just hiding them.
I think the hiding is important. Typically in code we expect things to be
communitive, however that isn't the case with query comprehensions, such as
you can't extract the lambda and declare it outside the query. It may appear
you can, and in some circumstances it will work, and in others it will cause
a runtime exception. The problem is people often view lambdas as being
delegates (or anonymous delegates) which isn't really true, they can be
compiled as expression trees not a delegate.

So let's take the example if a queryable collection. If one were to see :
p.Where(person => person.Age < ageLimit)

it is easy to presume that you could extract that lambda out:

Func<Person,bool> f = p => p.Age < 20;
p.Where(f);

But in doing so, we've mistakenly bound to the Where on IEnumerable rather
than the one on IQueryable.
Post by Jon Skeet [C# MVP]
Ditto whether or not it's an extension method.
The difference is the query syntax guarantees binding tot he most derived
extension.
Post by Jon Skeet [C# MVP]
The compiler is
translating the query expression into exactly the non-query-expression
code, so any issues in one form are going to be issues in the other
too.
Not necessarily. It's an issue of "overload resolution" (for want of a
better term) ;)
Post by Jon Skeet [C# MVP]
Post by Bill McCarthy
Admittedly I tend
to write more VB, so the query becomes more succinct in query syntax as you
don't need to include the superfluous select clause; and conversely the
lambda syntax in VB is a bit more long winded: Function(person) person.Age <
ageLimit
Now that's a more practical consideration :)
<g>
Jon Skeet [C# MVP]
2008-01-17 10:25:25 UTC
Permalink
On Jan 17, 10:12 am, "Bill McCarthy" <***@NOSPAM.com> wrote:

<snip>
Post by Jon Skeet [C# MVP]
p.Where(person => person.Age < ageLimit)
Func<Person,bool> f = p => p.Age < 20;
p.Where(f);
But in doing so, we've mistakenly bound to the Where on IEnumerable rather
than the one on IQueryable.
<snip>

That's certainly true - but in my view that's a reason to not extract
the lambda out (and to educate people not to do it inappropriately),
rather than to avoid using the extension methods directly.

It's certainly a matter of personal preference though.

Jon
Bill McCarthy
2008-01-17 10:34:35 UTC
Permalink
Post by Jon Skeet [C# MVP]
<snip>
Post by Jon Skeet [C# MVP]
p.Where(person => person.Age < ageLimit)
Func<Person,bool> f = p => p.Age < 20;
p.Where(f);
But in doing so, we've mistakenly bound to the Where on IEnumerable rather
than the one on IQueryable.
<snip>
That's certainly true - but in my view that's a reason to not extract
the lambda out (and to educate people not to do it inappropriately),
rather than to avoid using the extension methods directly.
It's certainly a matter of personal preference though.
Yep. With due caution and understanding, both achieve the same exact same
thing :)
Marc Gravell
2008-01-17 10:50:40 UTC
Permalink
Post by Bill McCarthy
The difference is the query syntax guarantees binding tot he most
derived extension.
No it doesn't. There is no difference in this respect between the
query syntax ("from a in b where c select a") and the extension syntax
("b.Where(a=>c)"). They are identical. It is only the Expression vs
Func<> that makes a difference in terms of overload resolution. So
stick with the lambda and it is the same either way (it will select
Expression in both forms for *exactly* the same reasons).

Yes, it would be possible to be daft and throw a delegate into the
extension syntax, but a lot of daft things are possible if you try
hard enough...

Marc
Bill McCarthy
2008-01-17 10:59:52 UTC
Permalink
hi Marc,
Post by Bill McCarthy
The difference is the query syntax guarantees binding tot he most derived
extension.
No it doesn't. There is no difference in this respect between the query
syntax ("from a in b where c select a") and the extension syntax
("b.Where(a=>c)"). They are identical. It is only the Expression vs Func<>
that makes a difference in terms of overload resolution. So stick with the
lambda and it is the same either way (it will select Expression in both
forms for *exactly* the same reasons).
Yes, it would be possible to be daft and throw a delegate into the
extension syntax, but a lot of daft things are possible if you try hard
enough...
Right so there is that possibility which means there is not the same
guarantee. Yes if you use lambdas in situ you don't have any difference,
but the difference is using extension methods does allow you to declare the
lambda outside of the method call, and hence change the binding. So as I
said, the query syntax *guarantees* the most derived binding where as the
extension method call does not. We're saying the same thing really, but
seem to disagree on what *guarantee* means ;)
Marc Gravell
2008-01-17 11:05:14 UTC
Permalink
Re the guarantee - I think I see where you're coming from - but even
with a query expression, there is nothing to stop the programmer from
using a condition that can't be converted into a suitable query by the
provider, and no guarantee that the provider will be able to
mix-and-match via IEnumerable<T>. I don't think it can ever be truly
guaranteed either way - so I'd go with whichever solution a: works and
b: is clearest for maintenance. Sometimes IMO that would be query
syntax, and sometimes extension syntax.

Marc
Bill McCarthy
2008-01-17 11:07:57 UTC
Permalink
Re the guarantee - I think I see where you're coming from - but even with
a query expression, there is nothing to stop the programmer from using a
condition that can't be converted into a suitable query by the provider,
and no guarantee that the provider will be able to mix-and-match via
IEnumerable<T>. I don't think it can ever be truly guaranteed either way -
so I'd go with whichever solution a: works and b: is clearest for
maintenance. Sometimes IMO that would be query syntax, and sometimes
extension syntax.
Agreed :) The greatest difficulty is (a), determining what works, as that
does require runtime tests for queryable given the provider could be
changed.

Marc Gravell
2008-01-17 10:58:04 UTC
Permalink
Post by Bill McCarthy
Func<Person,bool> f = p => p.Age < 20;
p.Where(f);
And there-in lies the bug. You *can* extract the lambda out, but you
need to tell it how:

Expression<Func<Person, bool>> f = p => p.Age < 20;

I'll agree that this is a mouthfull (and you can't cheat with "var").
However, now you can use it composably:

IQueryable<Person> source = null; // just checking compilier
here
var query1 = source.Where(f); // IQueryable<Person>

Marc
Bill McCarthy
2008-01-17 11:04:47 UTC
Permalink
Hi Marc,
Post by Bill McCarthy
Func<Person,bool> f = p => p.Age < 20;
p.Where(f);
And there-in lies the bug. You *can* extract the lambda out, but you need
Expression<Func<Person, bool>> f = p => p.Age < 20;
Yep.
I'll agree that this is a mouthfull (and you can't cheat with "var").
IQueryable<Person> source = null; // just checking compilier here
var query1 = source.Where(f); // IQueryable<Person>
Right. You could for example have a list of expressions and dynamically
interchange them. And this is something you cannot do with query syntax,
only extensions.
Jon Skeet [C# MVP]
2008-01-17 07:26:01 UTC
Permalink
Post by Scott Roberts
Post by Scott Roberts
It wouldn't, but a simple iterative method that executes when it is called
would.
To be more clear, which of these two code blocks would a college intern be
more likely to read, understand, and modify without introducing errors?
public List<Person> FilterByAge(List<Person> input,
int ageLimit)
{
List<Person> result = new List<Person>();
foreach (Person p in input)
if (p.Age < ageLimit)
result.Add(p);
return result;
}
public List<Person> FilterByAge(List<Person> input,
int ageLimit)
{
return input.Where(delegate (Person person)
{ return person.Age < ageLimit; }
);
}
(Note: the latter won't compile as input.Where returns an
IEnumerable<Person> rather than List<Person> this is quite
important...)

Unless they'd taken some time to learn LINQ, the former is indeed
simpler. Once you understand LINQ and lambda expressions, the third
option I presented is simpler IMO. It says exactly what it wants to
achieve, without any fluff around it.

However, it's important to understand the radically different
characteristings of "build a list in memory" and "stream the data
through a filter". Your version won't cope with billions of records -
Where will.
Post by Scott Roberts
One uses a standard "for loop", a basic construct available in pretty much
every programming language since assembly. The other uses newer and more
advanced C#-specific constructs that are probably still completely foreign
to 90% (or more) of the current C# community.
Given that .NET 3.5 has only been out for a couple of months, that's to
be expected. I don't think we should embrace that as a status quo to be
maintained though.
Post by Scott Roberts
The second construct is more elegant, concise, and most probably more
efficient. Peter and Jon, you both clearly have a much greater and deeper
understanding of C# than I (and the vast majority of other C# programmers)
do, but that is *exactly* what makes my opinion on "readability" more
salient than yours. I'm sure that once one gains experience with these newer
language constructs that they do enhance "readability" - for the people who
know and use them. But the fact of the matter is that, at least in my
experience, the vast majority of people making a living as a "programmer"
still struggle with objects and events, let alone delegates, anonymous
methods, and the like. Perhaps that is an indictment against me and my
knowledge (or lack thereof) of C#. I've heard it argued before that if it's
part of the language specification then a "C# programmer" should know how to
read & use it. But I'll tell you right now that there are a bazillion C# and
.Net language features that I don't know and will never know. And you and I
both know that this is true of the vast majority of programmers out there.
At the moment, yes. That's part of the reason why I'm writing my book -
so that people *will* know these features, and use them.

I prefer to learn about the new features and disseminate that
knowledge, encouraging others to learn more advanced techniques which
will help them in the long run.

<snip>
Post by Scott Roberts
I AM saying that given a relatively simple task, using a ubiquitous
construct (such as a "for loop") will be more *readable* to a wider audience
than using a more advanced construct.
For how long though - and do you want to encourage that period of time
to be longer or shorter?
--
Jon Skeet - <***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Peter Duniho
2008-01-17 07:31:36 UTC
Permalink
On Wed, 16 Jan 2008 20:08:44 -0800, Scott Roberts
Post by Scott Roberts
To be more clear, which of these two code blocks would a college intern
be more likely to read, understand, and modify without introducing
errors?
I can't answer that question, not knowing enough college interns, and it
having been far too long ago for me to really remember what it was like to
be a college student myself. And you haven't suggested what change you
might ask the intern to make. I think some changes might be easier for
that intern to understand when looking at the second version.

I _can_ tell you that I find the intent of the code much more apparent in
the second version. The first version is very clear about the mechanics,
but any interpretation of the intent is completely hidden. The second
version, I can practically read in plain English: "return from the input
items where the age is less than the limit".

To me, readability has a lot more to do with understanding what the code
is supposed to be doing at a high level than it does with understanding
the exact mechanics of the code. That's the whole point of higher-level
languages: to abstract what is to this day still basically the same kind
of computing we've had from day one, to a degree that the code's _intent_
and _behavior_ is much more apparent. This almost always results in
obfuscation of the exact mechanics of the code, and causes the code to
tend toward a more human-language-like syntax.
Post by Scott Roberts
[...]
So you are more than welcome to continue writing elegant, concise,
efficient code until the cows come home. I hope that you do. But don't
go around saying that a code block consisting of delegates and anonymous
methods is "more readable" than a simple "for loop".
Why not? To me, it is. And I have a hard time accepting any claim that
being "more readable" can be defined in any sort of absolute terms. I
prefer my newspapers to be printed in English. I find that a lot more
readable than those printed in French, for example. But I know there are
people out there who have the exact opposite feeling about what's "more
readable".

Perhaps more to the point, I prefer my code do a good job describing what
it _does_, rather than worrying so much about whether it does a good job
describing _how_ it does it. This is where language features like this
are very useful, and to me knowing the semantics of some code is a lot
"more readable" than knowing the nuts and bolts.

Others may prefer to have a more clear understanding of the mechanics, and
then have to puzzle out what the code is actually supposed to accomplish.
For them, I can see how this sort of thing might be less readable. But I
don't accept that those people get to define in absolute terms what "more
readable" is, and I've seen too many new programmers understand and even
enjoy language constructs that you apparently find less readable for me to
feel that this is even an "old programmer versus new programmer" sort of
question.

Pete
Jon Skeet [C# MVP]
2008-01-17 07:18:49 UTC
Permalink
Post by Scott Roberts
Post by Peter Duniho
How would defining a method elsewhere and using that to instantiate a
delegate have helped that situation?
It wouldn't, but a simple iterative method that executes when it is called
would.
Try implementing the signature I provided without losing the benefits
of streaming data (rather than buffering it) without making things more
complicated.
--
Jon Skeet - <***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Jon Skeet [C# MVP]
2008-01-16 19:17:14 UTC
Permalink
Post by Michael C
Post by Peter Duniho
Well, I guess you're entitled to your opinion. IMHO, nothing could be
clearer than putting all of the relevant code in one place, rather than
making someone go looking for it.
IMO it's not MUCH better to have the code in a seperate function. In your
example we actually exit the function but jump back into it right in the
middle!! Yuk!
Anonymous functions take a little bit of a change of mindset, but they
really are incredibly useful.

You really are missing out (particularly in C# 3) if you decide to
avoid them all the time, rather than taking the trouble to understand
them. They *can* cause subtle bugs, but most of the time they're
absolutely fine, and can make code much, much simpler.
--
Jon Skeet - <***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Misbah Arefin
2008-01-15 05:40:00 UTC
Permalink
while(true)
{
try
{
TryOpenFile(e.Name);
ProcessFile(e.Name);
break;
}
catch(Exception ex)
{
System.Threading.Thread.Sleep(1000);
continue;
}
}
--
Misbah Arefin
Post by Robert E. Flaherty
What is the C# command to wait for a specified period of time?
I am writing a windows service that will process a file once it has beed
created or changed. I'm using the fileSystemWatcher to detect when a
specific file has been created or changed. That works fine to a point. The
event does fire when the file is being created or changed but I then blow up
when I attempt to open the file because the creation or changing has not
finished.
Using the try/catch structure, I am wanting to attempt to open the file. If
it is still being used, then wait a minute and try again. I rather use a
command (assuming that one exists) to sit there for say a minute and try
again.
Using a timer evolves a callback. I was hoping for a simple solution.
Loading...