1

I'm new to LINQ and I'm doing a simple project to learn the features of the technology.

Currently I've got a static class that wraps an array of object (a kind of simple factory). Let's say it looks like the following:

public static class Factory
{
    private static Item[] items = new Item[]
    {
        // items are created here
    };

    // ...
}

Now I can add some functions to my Factory that allow me to query the inner array, e.g.

    public static Item GetByID(ItemID id)
    {
        var query = 
            from item in items
            where item.ID == id
            select item;

        return query.First();
    }

However, this requires me to modify the internals of the Factory class. Is there a way to write such queries from the 'outer world' instead ?

public class OtherClass
{
    var result = from it in Factory select ...
}

?

6
  • If it's a public property, you can access it. Factory.WhicheverCollection.Where(item => item.id == whicheverid) What's the problem? Commented Feb 4, 2014 at 22:16
  • I meant using the inner array without exposing it. Something similar to adding an [] operator, which allows a user to use the object as a container. Commented Feb 4, 2014 at 22:23
  • You could do that if the class can be iterated on. The way you should do that is to make the class implement IEnumerable. From memory, Linq does something odd and can act on anything with a GetIterator or something, but you probably shouldn't trust that. Commented Feb 4, 2014 at 22:33
  • Understood - in your opinion which approach is better ? The one with making the inner collection public or the one with implementing IEnumerable ? Commented Feb 4, 2014 at 22:41
  • 2
    A public get with a private set for your property is definitely the way I'd do it. Interfaces declare intent to a rather large extent. If you want people thinking of a Factory as a list, then IEnumerable is a good choice. IMO, nothing named Factory should ever be a list, because no one would ever expect that. Commented Feb 4, 2014 at 23:08

4 Answers 4

3

Yes, you can. Just use linq on the Factory from the 'outer world':

public class OtherClass
{
    public Item Get(ItemId id)
    {
       return Factory.Items.SingleOrDefault(i => i.ID == id);
    }
}

Of course, to do this, you'd need to change the access modifier of the items array to be public.

Sign up to request clarification or add additional context in comments.

6 Comments

That's the point - can this be done without exposing the inner collection, but using the wrapping class instead ?
You would have to add a Get method to your Factory, that was public, that returned you the array items, which the 'outer world' could then use linq to operate upon. Realize that this is only semantically different, but accomplishes the same thing.
Not so; he could make the class implement IEnumerable.
Which would be needlessly complex for what OP is trying to accomplish.
I agree. It's only where you say he 'would have to' do it your way that I disagree with. I'd do it your way.
|
3

There are so many options.

The easiest thing to do is just to expose a public property that allows just what you want to have allowed:

public static class Factory
{
    private static Item[] items = new Item[]
    {
        // items are created here
    };

    public static IEnumerable<IReadableItem> Items{ get { return items; } }
    // ...
}

The above code assumes that the Item class implements an IReadableItem interface that only has the methods and properties on it that you want to allow people to access. You could also clone the items list before returning it each time, if you're worried someone might re-cast the Items or try to modify it using reflection. Because the standard LINQ methods all work off of IEnumerable<>s, this would allow someone to effectively produce a LINQ query on your items, without exposing overmuch data.

List<string> bestItemNames = Factory.Items.Where(i => i.IsBest)
    .Select(i => i.Name)
    .ToList();

If you wanted to get really fancy, you could even implement your own LINQ provider. From a language perspective, LINQ expression syntax just maps to specific method names, so if you had a class that implemented a .Where() and a .Select() method, then you could implement that pretty much however you wanted, and people wouldn't know any different until they tried doing something that your methods didn't support.

Comments

2

One possibility is to implement IQueryable<T> for a non-static class:

public class Factory<T> : IQueryable<T>
{
    protected T[] _items = new T[]{};

    public Type ElementType
    {
        // or typeof(T)
        get { return _items.AsQueryable().ElementType; }
    }

    public System.Linq.Expressions.Expression Expression
    {
        get { return _items.AsQueryable().Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _items.AsQueryable().Provider; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ( IEnumerator<T> )_items.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _items.GetEnumerator();
    }
}

Derive non-generic class to populate array (if desired)

public class ItemFactory : Factory<Item>
{
    public ItemFactory()
    {
        // items are created here
    }
}

Create static instance

public static class Factories
{
    private static ItemFactory _itemFactory = new ItemFactory();
    public static ItemFactory ItemFactory { get { return _itemFactory; } }
}

Usage:

var specificItem = Factories.ItemFactory
    .Where( item => item.ID == id )
    .SingleOrDefault();

Comments

1

use an expression tree

public class OtherClass
{ 
    public Item Get(ItemId id)
    {
         return Factory.Get(i => i.id == id);
    }
}

and change the get method to

 public Item Get(Expression<Func<Item,bool>> filter)
 {
      return items.SingleOrDefault(filter);
 }

however, this approach makes little sense unless you are encapsulating some other logic in your factory class i.e. select only rows that are not soft deleted.

2 Comments

There is not need for expression trees here, delegates will suffice (and I think your code wouldn't actually compile).
You're absolutely right. I thought arrays implemented IQueryable<T>. A Predicate<Item> would be better suited here, however, Expression trees are composable which allows for optimization when communicating with other linq providers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.