C# Command to Wait a Specified Period of Time

  • Thread starter Thread starter Robert E. Flaherty
  • Start date Start date
Scott Roberts said:
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.
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.
 
No, it doesn't. It only executes as the caller enumerates through the
returned IEnumerable.

How about "in your example the anonymous method executes very near the
declaring code, instead of 60 seconds later on a timer event"?
 
Scott Roberts said:
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 said:
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.
 
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
 
Peter Duniho said:
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.
 
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:

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 said:
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.
 
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.

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.
 
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.
 
Scott Roberts said:
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.
 
[...]
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 said:
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; }
);
}

(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.
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.
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.

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?
 
Bill McCarthy said:
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.

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>
 
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.
[...]
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
 
Hi Jon,

Jon Skeet said:
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.

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
 
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.
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
 
Jon Skeet said:
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.



Ditto whether or not it's an extension method.


The difference is the query syntax guarantees binding tot he most derived
extension.


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) ;)

Now that's a more practical consideration :)

<g>
 
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.

<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
 
Jon Skeet said:
<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 :)
 

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

Back
Top