I have a type that wraps an IEnumerable<T> and contains some additional logic, implementing an interface which we'll call IMyIterable<T>. On one hand, I want people to use LINQ methods to filter and process IMyIterable<T> (by actually applying them to the underlying object), but on the other hand, I still want to fluently expose IMyIterable<T>'s custom instance functionality (e.g state information) after the LINQ methods have been applied. e.g.
var query = myIterable.Select(x => x.whatever);
//query is now of a completely different type than IMyIterable<T>
var result = query.MyCustomThingy();
//But I really want MyCustomThingy to work too.
I know a solution would be:
var query = myIterable.Linq(x => x.Select(y => y.Whatever));
But the extra brackets are unseemly to me. Are there any other ways I could do this? I'm not actually asking for the method Select() to return a custom class (though if there are neat ways to do this, e.g. involving code generation, I'm definitely aboard), just some neater syntax.
LINQ is not tied to IEnumerable. You can implement the LINQ methods you need on IMyIterable itself (either as instance methods or as extension methods). This way you can even use the linq syntax directly on a variable of type IMyIterable.
E.g.:
public interface IMyIterable<T>
{
IMyIterable<TOut> Select<TOut>(Func<T, TOut> selector);
IMyIterable<T> Where(Func<T, bool> predicate);
// ...
}
This would allow you to write something like this:
IMyIterable<T> source = ...;
var query = from item in source
where item.X == Y
select new {...}
Could you implement the methods on IMyIterable as extension methods to IEnumerable instead ? Not knowing exactly what this interface does makes it hard to come up with a solution.
Instead of deriving from IEnumerable, you could create a bunch of equivalent methods on IMyIterable (returning IMyIterable) and forward them to the inner IEnumerable instance.
However this seems like a lot of work to go around a cast. I guess your suggested .Linq() solution would work OK.
Related
I'm currently working on a little program, which should get the information scanned by a barcode scanner (a so-called "HID") via the Raw-Input API.
I've read many tutorials about that and i think I'm understanding how it works. I'm using an IEnumerable to enumerate the input-devices. But now the compiler screams that the Where-Method is not known for an IEnumerable.
I've going through the MSDN-Articles regarding the IEnumerable and if I have understand the articles right, the Where-Method should be part of it.
Below a little snippet with the place i want to use the Where:
var rawInputDevice in rawDeviceEnumerator.Devices
.Where(d => d.DeviceType == Win32.RawInputDeviceType.Keyboard)
Can someone please give me an approach?
I think its just a little thing I'm overseeing.
The problem you are noting typically comes from older Collection types before .net 3.0, which introduced generic types.
The method you want to use is Enumerable.Where(this IEnumerable<T> enumerable, Func<T,bool> predicate). However rawDeviceEnumerator.Devices seems to be an IEnumerable and NOT IEnumerable<T>. Assuming you are using the RawInputDeviceEnumerator from http://www.news2news.com/vfp/?example=571&ver=vcs&PHPSESSID=5f4393ed0b6c7c205851a834e657e8be, then you have several options.
First. Change the code from
public IEnumerable Devices
{
get
{
return this._devices;
}
}
To
public IEnumerable<RawInputDevice> Devices
{
get
{
return this._devices;
}
}
Or you can use
var rawInputDevice in rawDeviceEnumerator.Devices
.Cast<RawInputDevice>()
.Where(d => d.DeviceType == Win32.RawInputDeviceType.Keyboard)
I think you mean Enumeration.Where which is an extension method. It appears to 'add' methods to existing classes bases on the type of the class and it's base classes or interfaces.
If you include System.Linq as namespace in your code files, you will see this extension method will appear on every object that implements IEnumerable<TSource>, for example List<T> or int[].
I was trying to make a generic method for paging both IEnuemrable<T> and IQueryable<T>
Like this:
public T Paginate<T, TS>(T list) where T : IEnumerable<TS>
{
CheckValidityAndClamp();
return (T)(list.Skip(Page*PageSize).Take(PageSize));
}
When I passed in a List<int> instance it compiles fine.
But when run it gives a cast exception:
System.InvalidCastException : An object of type '<TakeIterator>d__3a`1[System.Int32]' can not be converted to the type 'System.Collections.Generic.List`1[System.Int32]'.
Why is that? A List<int> implements IEnumerable<int> so why the cast exception?
You're not returning a list. Take is implemented with an iterator block, which means that the actual type is a type with no compile time identifier (which is why it looks so weird) and it implements IEnumerable. It is not a List, which you're trying to cast it to.
On top of that, your method can't actually work for any IQueryable objects. It's calling the implementation of Skip that accepts an IEnumerable (because that's what the generic constraint tells the compiler it must implement) so if it were an IQueryable (even if you resolved the messy cast issue) you wouldn't be using the query provider to translate the Skip call. You need to have two overloads here, one for IQueryable and one for IEnumerable; this is pretty much inherent to the problem given that there are two Skip and Take methods that you need to call, one for each of IEnumerable and IQueryable.
To expand on Servy's (correct) answer - another issue is that Skip and Take are extension methods, which is syntactic sugar for calling static methods. Since static methods are bound at compile time, the best information that the compiler has is that T is an IEnumerable<TS>, so the compiler binds Skip and Take to the static methods of Enumerable. There's no way to dynamically bind static methods at run-time. That is why there are two different classes for the extension methods on IEnumerable and IQueryable (Enumerable and Queryable)
You need two overloads.
You get this as you cannot cast the IEnumerable to List (This is why LINQ has .ToList() extension). I think what you want is something like this:
public IEnumerable<T> Paginate<T>(IEnumerable<T> list)
{
return list.Skip(Page * PageSize).Take(PageSize);
}
Which will work with any source that implements IEnumerable (List, T[], IQueryable)
You can then call it like this:
IEnumerable<int> list = someQueryResult;
IEnumerable<int> page = class.Paginate<int>(list);
As pointed out in other answers. IQueryable has it's own set of extension methods. So all though this will work, the paging will not be done as the data-source, as it will use the IEnumerable extensions
Just out of curiosity:
Many LINQ extension methods exist as both generic and non-generic variants, for example Any and Any<>, Where and Where<> etc. Writing my queries I usually use the non-generic variants and it works fine.
What would be the cases when one has to use generic methods?
--- edit ---
P.S.: I am aware of the fact that internally only generic methods are called and the compiler tries to resolve the content of the generic brackets <> during compilation.
My question is rather what are the cases then one has to provide the type explicitly and not to rely on the compiler's intuition?
Always. The C# compiler is smart enough to infer what the type of the method is based on the parameters. This is important when the type is anonymous, and thus has no name.
obj.SomeMethod(123); //these calls are the same
obj.SomeMethod<int>(123);
obj.SomeMethod(new { foo = 123 }); //what type would I write here?!
Edit: To be clear, you are always calling the generic method. It just looks like a non-generic method, since the compiler and Intellisense are smart.
Edit: To your updated question, you would want to be specific if you want to use a type that is not the type of the object you are passing. There are two such cases:
If the parameter implements an interface, and you want to operate on that interface, not the concrete type, then you should specify the interface:
obj.DoSomething<IEnumerable<Foo>>( new List<Foo>() );
If the parameter is implicitly convertible to another type, and you want to use the second type, then you should specify it:
obj.DoSomethingElse<long> ( 123 ); //123 is actually an int, but convertible to long
On the other hand, if you need a cast to do the conversion (or you insert one anyway), then you don't need to specify:
obj.DoYetAnotherThing( (Transformed)new MyThing() ); // calls DoYetAnotherThing<Transformed>
One example I ran into today:
ObjectSet<User> users = context.Users;
var usersThatMatch = criteria.Aggregate(users, (u, c) => u.Where(c));
The above code won't work because the .Where method doesn't return an ObjectSet<User>. You could get around this one of two ways. I could call .AsQueryable() on users, to make sure it's strongly typed as an IQueryable, or I could pass specific type arguments into the Aggregate method:
criteria.Aggregate<Func<User, bool>, IEnumerable<User>>(
PersonSet, (u, c) => u.Where(c));
Another couple of more common examples are the Cast and OfType methods, which have no way to infer what type you want, and in many cases are being called on a non-generic collection in the first place.
In general, the folks that designed the LINQ methods went out of their way to avoid the need to use explicit types in these generic methods, and for the most part you don't need to. I'd say it's best to know it's an option, but avoid doing it unless you find it necessary.
I'm not for sure how the ControlCollection of ASP.Net works, so maybe someone can shed some light on this for me.
I recently discovered the magic that is extension methods and Linq. Well, I was very sad to find that this isn't valid syntax
var c=Controls.Where(x => x.ID=="Some ID").SingleOrDefault();
However from what I can tell, Controls does implement the IEnumerable interface which provides such methods, so what gives? Why doesn't that just work? I have found a decent work around for this issue at least:
var list = (IEnumerable<Control>)Controls;
var this_item = list.Where(x => x.ID == "Some ID").SingleOrDefault();
No, IEnumerable doesn't have many extension methods on it: IEnumerable<T> does. They are two separate interfaces, although IEnumerable<T> extends IEnumerable.
The normal LINQ ways of converting are to use the Cast<T>() and OfType<T>() extension methods which do extend the nongeneric interface:
IEnumerable<TextBox> textBoxes = Controls.OfType<TextBox>();
IEnumerable<Control> controls = Controls.Cast<Control>();
The difference between the two is that OfType will just skip any items which aren't of the required type; Cast will throw an exception instead.
Once you've got references to the generic IEnumerable<T> type, all the rest of the LINQ methods are available.
This is just because the ControlCollection class came around before generics; so it implements IEnumerable but not IEnumerable<Control>.
Fortunately, there does exist a LINQ extension method on the IEnumerable interface that allows you to generate an IEnumerable<T> through casting: Cast<T>. Which means you can always just do this:
var c = Controls.Cast<Control>().Where(x => x.ID == "Some ID").SingleOrDefault();
In addition to the answers provided by Jon Skeet and Dan Tao, you can use query expression syntax by explicitly providing the type.
Control myControl = (from Control control in this.Controls
where control.ID == "Some ID"
select control).SingleOrDefault();
Linq utilized Generic Collections. ControlsCollection implements IEnumerable not IEnumberable<T>
If you notice this will not work
((IEnumerable)page.Controls).Where(...
However, this does
((IEnumerable<Control>)page.Controls).Where(...
You can either cast to Generic IEnumerable<T> or access an extension method that does, like so:
page.Controls.OfType<Control>().Where(c => c.ID == "Some ID").FirstOrDefault();
I am trying to do the following Linq query on a Visio Masters collection:
List<string> allMasterNames = (from master in myStencil.Masters select master.NameU).ToList<string>
I get the following error:
Could not find an implementation of
the query pattern for source type
'Microsoft.Office.Interop.Visio.Masters'.
'Select' not found. Consider
explicitly specifying the type of the
range variable 'master'.
From reading around this error seems to occur when the queried object does not implement IEnumerable<T> or IQueryable<T>. Is that the case here, or is it something else?
Yes, it is because it's not IEnumerable<T>, IQueryable<T> and it doesn't have its own custom Select method written.
Contary to popular believe you don't have to implement those interfaces to have LINQ support, you just need to have the methods they compile down to.
This is fine:
public class MyClass {
public MyClass Select<MyClass>(Func<MyClass, MyClass> func) { ... }
}
var tmp = from m in new MyClass()
select m;
Sure a .ToList() wont work though ;)
As you solving your problem try using the Cast<T>() extension method, that'll make it an IEnumerable<T> and allow you to LINQ to your hearts content.
As the exception message suggests, consider explicitly specifying the type of the range variable 'master'.
from Visio.Master master in myStencil.Masters select master.NameU
According to Reflector ...
public class MastersClass : IVMasters, Masters, EMasters_Event, IEnumerable
{
// more
}
None of the others are IEnumerable<T> either. But, good ol IEnumerable is there, so you can for each.
I cannot try this piece of code.
(from master in myStencil.Masters.Cast<Masters> select master.NameU).ToList<string>)
The gist is to use Enumerable.Cast method, to convert a non-generic list to a generic one.
Does that work?