tags:

views:

119

answers:

2

I have an private List and I want to expose the ability to query the List and return a new List with new cloned items. I can pass in delegates for filtering and sorting, which works fine, but being able to use Linq expressions would be much more elegant.

I've added an simplified example of what I'm trying to do, which might help as I don't think I've explained what I want to do very well.

public class Repository
{
    private List<SomeModel> _models;
    private object _lock;

    public List<SomeModel> GetModels(Func<SomeModel, bool> predicate)
    {
        List<SomeModel> models;
        lock (_lock)
        {
            models = _models.Where(m => predicate(m))
                            .Select(m => new SomeModel(m))
                            .ToList();
        }
        return models;
    }
}
+3  A: 

Why does your code involve locking? Assuming your "SomeModel" class has a copy constructor as your example suggests, this should work:

public List<SomeModel> GetModels(Predicate<SomeModel> predicate)
{
    return _models.Where(m => predicate(m))
                  .Select(m => new SomeModel(m))
                  .ToList();
}
Matt Hamilton
Thanks, locking is there as the list is updated by another process (not shown, but that's why I can't just expose the list). Yes the SomeModel ctor shown is a copy constructor. I would like to also apply ordering and paging, so I want more than just the predicate. I want to apply a Linq expression.
tarn
If you want ordering etc then (as per the answer below) you're better off not returning a list, and simply returning the Select clause. Then the caller can apply sorting/paging logic to the returned IQueryable/IEnumerable.
Matt Hamilton
Good call, that makes a lot of sense, will work fine and I'll probably actually do that. But I'll pend marking this as answered (for an hour or so) in hope someone will have a way of creating an expression outside the lock and executing it inside the lock.
tarn
I think mark @themissinglinq's answer as the accepted one. I will edit it to reflect Jon's comment.
Matt Hamilton
+1  A: 

You could expose the private collection as an iterator block by doing something like this:

public IEnumerable<Model> Models
{
    get
    {
        foreach (Model mod in this._models)
            yield return new Model(mod);

        // equivalent to:
        // return _models.Select(m => new Model(m));
        // as per Jon's comment
    }
}

Which would give you the ability to then write queries against it like any other IEnumerable datasource.

TheMissingLINQ
aka return _models.Select(m => new Model(m));
Jon Skeet
Nice answer, but I have a feeling its not good to yield from inside lock. I'll look into it and will happily mark this as the answer if it works safely.
tarn
Couldn't you just do `return _models;`? As far as I can see, because of the return type IEnumerable<Model> and the fact that a list is enumerable, just doing `return _models;` would expose it as an enumerable... (or have I missed something?)
Svish
You can't use yield if the inner list is modified concurrently, remember that iterator code is deferred until the iterator is iterated by someone. You need to create a copy of the data and return the copy of use a immutable list.
Pop Catalin