

LINQ is better than foreach - kodefuguru
http://www.kodefuguru.com/post/2010/05/04/LINQ-is-Better-Than-ForEach.aspx

======
logicalmind
I use LINQ as much as possible in my code. Any time I do any type of looping
construct I re-evaluate whether I should be writing it using LINQ.

Most C# developers think LINQ is some fancy new concept but in reality they
are just an implementation of Haskell list comprehensions ported to C# by Erik
Meijer. I love watching the the imperative and functional languages converge
like this.

~~~
DrJokepu
You (and the writer of the article), of course, mean Linq to Objects. Linq
providers are free to compile Linq structures to whatever they want to, in the
case of objects that means compiling to list comprehensions, but for example
in the case of Linq to SQL it all compiles down to SQL queries.

(Sorry for nipticking, the reason I'm pointing this out because people get
confused about the unfortunate Linq terminology all the time).

~~~
kodefuguru
IQueryable's get converted to expression trees which then get converted to
sql. It's more of a "translated" to sql rather than a "compiled" to sql. The
power of IQueryable is that the expression trees can be converted to so many
different kinds of expressions, and you can write your own.

IEnumerable's extensions are mostly iterators over sequences.

~~~
btilly
_IQueryable's get converted to expression trees which then get converted to
sql. It's more of a "translated" to sql rather than a "compiled" to sql._

You've just described how compilers work, minus optional attempts at
optimization and optional excess passes. Most of which take place either after
the expression tree has been generated, or somewhere during the conversion
process. Therefore I'd call what you've described "compilation".

------
Zak
The Linq version is clearly superior to the imperative version, but I find the
following even easier to understand:

    
    
      (into [] (map full-name
                    (sort-by :last-name (filter #(= (:role %) 'developer) employees))))
    

It's good to see mainstream languages adding declarative constructs though.

~~~
rayvega
I personally like in simple scenarios using List<T>.ConvertAll:

    
    
        var foo = new List<int>{ 1, 2, 3};
        var bar = foo.ConvertAll(x => x * 2);
    

over the the Linq equivalent:

    
    
        var foo = new List<int>{ 1, 2, 3};
        var bar = from x in foo select x * 2

~~~
WorkerBee
that's the same as

var foo= new List<int> {1, 2, 3};

var bar = foo.Select(x => x*2);

~~~
nlawalker
Yep, one thing to realize is that LINQ does not simply refer to the query
syntax - it refers to a set of technologies, including expression trees and
the ability to compile them to alternate languages like TSQL, the query
syntax, and the extension methods like Select.

One small difference with your example though is that Select requires "using
System.Linq", and ConvertAll is defined on List<T>.

~~~
bluetech
And of course, the LINQ version returns an IEnumerable<TResult>, while the
List version return List<TResult>. You could chain a .ToList() there to make
them equivalent (if needed).

------
enntwo
One small caution when using LINQ, especially in performance critical areas
(XNA for instance): .Count and .Count() are dramatically difference in
performance, when simply looking for the size of an array or list, be sure to
use the .Count property as it is orders of magnitude faster. If you are using
LINQ else where, .Count() will achieve the same behavior but will be much
slower, so it can be a tough optimization to track down.

~~~
chrisb
The .Count() method in LINQ to objects checks to see if the IEnumerable
implements ICollection, and if so then just calls the .Count property.

Of course, calling the .Count property yourself is more efficient, but not by
orders of magnitude.

~~~
enntwo
Thank you for this, this was apparently fixed in .NET 4.0 which I was unaware
of.

I think there is still some validity in my post in confusing whether you are
using native accessing or routing through an additional tool/library, but the
fix definitely elimates the performance hit I was discussing, so much of my
post was in error.

edit: Also 3.5 it seems, I need to be more up-to-date with my concerns.

~~~
kodefuguru
Everyone get's that wrong, including Microsoft's documentation ;). It's a good
optimization though, in spite of the LSP violation.

------
boblol123
The number of objects is far too small to provide a very meaningful result.

~~~
kodefuguru
I changed it to run with one million names and ran with the optimized foreach.
I only did one pass.

foreach 00:00:01.7348710 linq 00:00:01.8607973

------
axod
FWIW I find the LINQ far harder to read.

    
    
      var developerNames = employees.AsParallel().AsOrdered()
                                  .Where(e => e.Role == Role.Developer)
                                  .OrderBy(e => e.LastName)
                                  .Select(e => e.FullName)
                                  .ToArray();
    

That's just gross IMHO. AsParallel() yuck. AsOrdered() eugh why are these
functions being used to set parameters.

~~~
mquander
I think it only looks gross because they're methods on an object, and you're
used to that meaning "imperative mutable thing." Compare this:

    
    
        employees.AsParallel().AsOrdered()
    

to the more-traditional-C-style

    
    
        MaintainOrder(Parallelize(employees))
    

or the admittedly more attractive

    
    
        (maintain-order (parallelize employees))
    

and suddenly it seems perfectly sensible. I thought it was ugly first, too,
but now I'm used to it.

~~~
wlievens
How is

    
    
        (maintain-order (parallelize employees))

vs

    
    
        MaintainOrder(Parallelize(employees))
    

"admittedly more attractive"? I enjoy functional constructs, but now you're
just talking syntax right?

~~~
cema
I agree, this is a strange claim. On the other hand (as long as we are with
Clojure), something like

    
    
      (->> employees parallelize maintain-order)

might be said to be more attractive because it looks more linear. The
sequential order of application is thus maintained, and the parentheses which
are perceived as additional levels of hierarchy are removed.

Of course, the dot-dot-dot style in Java/C#/etc is doing the same:

    
    
      employees.Parallelize().MaintainOrder() 

is also linear, the parentheses are only used to specify parameters.

------
nlawalker
I really like the LINQ extension methods and the functionality they provide,
but I don't really like the query syntax. Method calls and lambdas seem so
expressive and concise to me that I don't know why I'd start pretending like I
was using TSQL to do operations on enumerables. I can definitely see its draw
for some people though.

------
btilly
Any time you see toy benchmarks, ask yourself what was left out. The potential
performance issue with LINQ comes when you are accessing database data over a
connection with non-trivial latency. Then it is easy to wind up accidentally
issuing large numbers of queries, with a lot of round trips, that winds up
being slow and unnecessarily hard on the database.

LINQ is hardly unusual in having this risk. It is an easy mistake to make in
many environments with many toolsets, and it is not particularly hard to avoid
it with LINQ. But the toy benchmark doesn't address it. And the article makes
fun of a software architect who has likely had bad experiences with developers
messing up on this.

~~~
WorkerBee
_accessing database data over a connection with non-trivial latency. ... But
the toy benchmark doesn't address it_

I disagree entirely. I know when I'm using LINQ to SQL, and when I'm using
LINQ to objects. My experience is that LINQ to objects is more common and very
useful. He's using larger data volumes than I generally am, so there's
absolutely nothing "toy" about it.

~~~
btilly
Toy benchmarks frequently use large data volumes. That is easy to do. What is
harder to do in a benchmark is providing realistically complex logic to go
with those volumes. Many systems work well on the simple cases, but have bad
edge cases that can get tickled by a more complex problem.

~~~
WorkerBee
But the logic that I have successfully used LINQ on:

\- operates on lists of objects, not database queries.

\- operates on small quantities of data. It doesn't matter if there are only
five items in a list. If you have to filter them, you have to filter them

\- is not _that_ complex.

We use foreaches as well, or a combination. LINQ is great for automating
simple things, e.g. turn a loop to find an item into a .FirstOrDefault(x =>
x.SomeCondition). It would be interesting to see if, as our grasp of LINQ
improves, we run into any of these supposed corner cases. But it hasn't
happened yet.

