I have a class with two properties, say
public class Book {
public string 开发者_JAVA技巧TitleSource { get; set; }
public string TitleTarget { get; set; }
}
I have an IList<Book>
where the TitleTarget
is null and for each item in the list, I need to copy the TitleSource
property to the TitleTarget
property. I could do this through a loop, sure, but it seems like there's a LINQ or nice declarative way to do this. Is there?
Linq was designed as a way to consume things. If you look at web discussions about why there is no IEnumerable.ForEach(...) extension, you'll see that the Linq designers purposefully avoided Linq to Object scenarios where the methods were designed to change object values.
That said, you can cheat by "selecting" values and not using the results. But, that creates items which are thrown away. So, a foreach loop is much more efficient.
Edit for people who really want something besides foreach
Another "cheat" that wouldn't produce a new list would be to use a method that does little work of it's own, like Aggregate, All, or Any.
// Return true so All will go through the whole list.
books.All(book => { book.TitleTarget = book.TitleSource; return true; });
It's not LINQ as such, but there's:
books.Where(book => book.TitleTarget == null).ToList()
.ForEach(book => book.TitleTarget = book.TitleSource);
The main point is the ToList
method call: there's no ForEach
extension method (I don't think?) but there is one on List<T>
directly. It wouldn't be hard to write your own ForEach
extension method as well.
As to whether this would be better than a simple foreach
loop, I'm not so sure. I would personally choose the foreach
loop, since it makes the intention (that you want to modify the collection) a bit clearer.
@John Fisher is correct, there is no IEnumerable.ForEach
.
There is however a ForEach on List<T>
. So you could do the following:
List<Book> books = GetBooks();
books.ForEach(b => b.TitleTarget = b.TitleSource);
If you wanted a IEnumerable.ForEach it would be easy to create one:
public static class LinqExtensions
{
public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
foreach (var item in source)
{
action(item);
}
}
}
You can then use the following snippet to perform your action across your collection:
IList<Book> books = GetBooks();
books.ForEach(b => b.TitleTarget = b.TitleSource);
If you can use .NET 4.0, and you are using a thread-safe collection then you can use the new parallel ForEach construct:
using System.Threading.Tasks;
...
Parallel.ForEach(
books.Where(book => book.TitleTarget == null),
book => book.TitleTarget = book.TitleSource);
This will queue tasks to be run on the thread pool - one task that will execute the assignment delegate for each book in the collection.
For large data sets this may give a performance boost, but for smaller sets may actually be slower, given the overhead of managing the thread synchronization.
books.Select(b => b.TitleTarget = b.TitleSource);
This doesn't create any 'new items', just a query that you won't enumerate. That doesn't seem like a big deal to me.
精彩评论