Reputation: 4440
Some background info;
Dictionary<string, LanguageResource> _dict
_dict.Values.GetEnumerator()
I have a LanguageResourceCollection _resources that contains only LanguageEditorResource objects and want to use LINQ to enumerate those that are dirty so I have tried the following. My specific questions are in bold.
_resources.Where(r => (r as LanguageEditorResource).IsDirty)
neither Where not other LINQ methods are displayed by Intellisense but I code it anyway and am told "LanguageResourceCollection does not contain a definition for 'Where' and no extension method...".
Why does the way that LanguageResourceCollection implements IEnumerable preclude it from supporting LINQ?
If I change the query to
(_resources as IEnumerable<LanguageEditorResource>).Where(r => r.IsDirty)
Intellisense displays the LINQ methods and the solution compiles. But at runtime I get an ArgumentNullException "Value cannot be null. Parameter name: source".
Is this a problem in my LINQ code?
Is it a problem with the general design of the classes?
How can I dig into what LINQ generates to try and see what the problem is?
My aim with this question is not to get a solution for the specific problem, as I will have to solve it now using other (non LINQ) means, but rather to try and improve my understanding of LINQ and learn how I can improve the design of my classes to work better with LINQ.
Upvotes: 0
Views: 2672
Reputation: 110111
How can I dig into what LINQ generates to try and see what the problem is?
Linq isn't generating anything here. You can step through with the debugger.
to try and improve my understanding of LINQ and learn how I can improve the design of my classes to work better with LINQ.
System.Linq.Enumerable methods rely heavily on the IEnumerable< T > contract. You need to understand how your class can produce targets that support this contract. The type that T represents is important!
You could add this method to LanguageResourceCollection:
public IEnumerable<T> ParticularResources<T>()
{
return _dict.Values.OfType<T>();
}
and call it by:
_resources
.ParticularResources<LanguageEditorResource>()
.Where(r => r.IsDirty)
This example would make more sense if the collection class didn't implement IEnumerable< T > against that same _dict.Values . The point is to understand IEnumerable < T > and generic typing.
Upvotes: 1
Reputation: 1062865
It sounds like your collection implements IEnumerable
, not IEnumerable<T>
, hence you need:
_resources.Cast<LanguageEditorResource>().Where(r => r.IsDirty)
Note that Enumerable.Where
is defined on IEnumerable<T>
, not IEnumerable
- if you have the non-generic type, you need to use Cast<T>
(or OfType<T>
) to get the right type. The difference being that Cast<T>
will throw an exception if it finds something that isn't a T
, where-as OfType<T>
simply ignores anything that isn't a T
. Since you've stated that your collection only contains LanguageEditorResource
, it is reasonable to check that assumption using Cast<T>
, rather than silently drop data.
Check also that you have "using System.Linq" (and are referencing System.Core (.NET 3.5; else LINQBridge with .NET 2.0) to get the Where
extension method(s).
Actually, it would be worth having your collection implement IEnumerable<LanguageResource>
- which you could do quite simply using either the Cast<T>
method, or an iterator block (yield return
).
[edit]
To build on Richard Poole's note - you could write your own generic container here, presumably with T : LanguageResource
(and using that T
in the Dictionary<string,T>
, and implementing IEnumerable<T>
or ICollection<T>
). Just a thought.
Upvotes: 6
Reputation: 3984
In addition to Marc G's answer, and if you're able to do so, you might want to consider dropping your custom LanguageResourceCollection
class in favour of a generic List<LanguageResource>
. This will solve your current problem and get rid of that nasty .NET 1.1ish custom collection.
Upvotes: 1