开发者

In a generic list, is there a way to copy one property to another in a declarative /LINQ manner?

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜