Linq with custom base collection - c#

I often find linq being problematic when working with custom collection object.
They are often defened as
The base collection
abstract class BaseCollection<T> : List<T> { ... }
the collections is defined as
class PruductCollection : BaseCollection<Product> { ... }
Is there a better way to add results from a linq expession to this collection than
addrange or concat?
var products = from p in HugeProductCollection
where p.Vendor = currentVendor
select p;
PruductCollection objVendorProducts = new PruductCollection();
objVendorProducts.AddRange(products);
It would be nice if the object returned form the linq query was of my custom collection type. As you seem to need to enumerate the collection two times to do this.
EDIT :
After reading the answers i think the best solution is to implementa a ToProduct() extention.
Wonder if the covariance/contravariance in c#4.0 will help solve these kinds of problems.

The problem is that LINQ, through extension methods on IEnumerable<T>, knows how to build Arrays, Lists, and Dictionaries, it doesn't know how to build your custom collection. You could have your custom collection have a constructor that takes an IEnumerable<T> or you could write you. The former would allow you to use the LINQ result in your constructor directly, the latter would allow you to decorate the LINQ statement with your extension and get back the collection you desire. Either way you'll need to do some sort of conversion from the generic collection to your specialized collection -- either in the constructor or in the extension. Or you could do both...
public static class MyExtensions
{
public static ProductCollection
ToProducts( this IEnumerable<Product> collection )
{
return new ProductCollection( collection );
}
}
public class ProductCollection : BaseCollection<Product>
{
...
public ProductCollection( IEnumerable<Product> collection )
: base( collection )
{
}
...
}
var products = (from p in HugeProductCollection
where p.Vendor = currentVendor
select p).ToProducts();

I can suggest you a way in that you don't have to enumerate the collection 2 times:
abstract class BaseCollection<T> : List<T>
{
public BaseCollection(IEnumerable<T> collection)
: base(collection)
{
}
}
class PruductCollection : BaseCollection<Product>
{
public PruductCollection(IEnumerable<Product> collection)
: base(collection)
{
}
}
var products = from p in HugeProductCollection
where p.Vendor = currentVendor
select p;
PruductCollection objVendorProducts = new PruductCollection(products);

Could this work with a BindingList ? as BindingList does not have a constructor which would take an IEnumerable but does implement it.
BindingList : Collection, IBindingList, IList, ICollection, IEnumerable, ICancelAddNew, IRaiseItemChangedEvents

Related

Converting Generic List of object to defined collection class with Lambda and C#

This is an issue I have seen in two different jobs that use a 3-tier structure and haven't found a clean way around it. This also applies to using LINQ statements I believe.
I have 2 classes, one is an object and the other is a defined collection of those objects that might have some additional functionality in it:
public class TestObject
{
public Int32 id {get; set;}
public string value {get; set;}
}
public class TestObjectCollection: List<TestObject>
{
public TestObject Get(Int32 id)
{
return this.FirstOrDefault(item => item.id==id);
}
}
Say I use a lambda expression like:
List<TestObject> result = data.Where(item => item.id > 0).ToList();
Is there an easy way to convert that list of objects to my defined collection without doing something like this:
TestObjectCollection resultAsCollection = new TestObjectCollection()
resultAsCollection.AddRange(result);
It seems like there should be a way to cast my GenericList returned by the Lambda expression to my TestObjectCollection without the added step looping through my returned results.
No, there isn't. ToList creates a List<T> - and there's no way of casting a plain List<T> to a TestObjectCollection without creating a new TestObjectCollection.
Personally I'd avoid creating a collection deriving from List<T> at all (I'd almost always use composition instead) but if you really want to have that collection, the simplest approach is to create your own extension method:
public static class TestEnumerable
{
public static TestObjectCollection ToTestObjectCollection(this IEnumerable<TestObject> source)
{
return new TestObjectCollection(source);
}
}
... and implement the appropriate constructor, of course, which can probably just chain to the List(IEnumerable<T>) constructor. Then you can write:
var resultAsCollection = data.Where(item => item.id > 0).ToTestObjectCollection();
It seems like there should be a way to cast my GenericList returned by
the Lamda expression to my TestObjectCollection without the added step
looping through my returned results
There isn't because with generic collections you can use covariance and not contravariance.
Other than Jon's solution (which is good) you can create constructor overload:
public class TestObjectCollection: List<TestObject>
{
public TestObjectCollection(IEnumerable<TestObject> list) { AddRange(list);}
...
}
Usage:
var resultAsCollection = new TestObjectCollection(data.Where(item => item.id > 0));

List<T> and GetRange() issue in derived Class

I have a class that implements a list of MyItems.
I want a method cutting of some elements from that list and it should return the removed items.
This is what I tried:
public class MyItemList : List<MyItem>
{
...
public MyItemList cutOff(int count)
{
MyItemList result = this.GetRange(0, count);
this.RemoveRange(0, count);
return result;
}
Unfortunately the GetRange() returns List but not MyItemList :(
How can I handle this in a better way?
Casting doesn't work.
There must be an elegant way to solve this very simple problem and stay inside the type MyItemList (without dirty hacks).
Thanks in advance!
This should do the trick, but I strongly suggest to redesign towards composition, where you will store List internally
public class MyItemList : List<MyItem>
{
public MyItemList(){}
public MyItemList(IEnumerable<MyItem> sequence): base(sequence) {}
public MyItemList cutOff(int count)
{
MyItemList result = new MyItemList(this.GetRange(0, count));
this.RemoveRange(0, count);
return result;
}
}
Also consider creating open generic type of your list like MyList<T> : List<T> or MyList<T> : List<T> where T : MyItem so that client of that class can take advantage of generics
Edit: ok, I've implemented generic version for List<T> as extension method, this will help you to more logic general to Lists outside your MyItemList class
public static class ListExtensions
{
public static List<T> CutOff<T>(this List<T> list, int count)
{
var result = list.GetRange(0, count);
list.RemoveRange(0, count);
return result;
}
}
now you can
var list = new List<int> {1,2,3,4,5,6};
Console.WriteLine ("cutted items:");
Console.WriteLine (string.Join(Environment.NewLine, list.CutOff(2)));
Console.WriteLine ("items in list:");
Console.WriteLine (string.Join(Environment.NewLine, list));
prints:
cutted items:
1
2
items in list:
3
4
5
6
Another note:
I suggest to do this
public class MyItemList<T> : IList<T> where T : MyItem
{
private List<T> list;
//here will be implementation of all methods required by IList
//that will simply delegate to list field
}
note that if all logic in MyItemList is general-purpose (that can be applied to List<T>, like Cutoff method), you probably don't need separate class. Also where T : MyItem is optional, only need if you access methods defined in MyItem at MyItemList
You could just return a List<MyItem> or MyItem[] with the removed items.
or Use the List<> constructor that takes an ienumerable.
Haven't compiled this - but should be ok
public class MyItemList : List<MyItem>
{
// def ctor
public MyItemList() {}
public MyItemList(IEnumerable<MyItems> items): base(items) {}
public MyItemList cutOff(int count)
{
MyItemList result = new MyItemList(this.GetRange(0, count));
this.RemoveRange(0, count);
return result;
}
}
Don't inherit from List<MyItem> (unless all you're trying to do is avoid typing angle brackets. Rather, encapsulate a List<MyItem> as your class' backing store and expose only the specific/methods and properties required by your problem domain. By inheriting from List<MyObject>, your are leaking implementation details and tying yourself to a particular type of backing store. You want to maintain the minimal public surface area required to get the job done. Doing so facilitates testing, and makes future change much easier.
If you want interoperability with standard SCG collection types, implement only the interfaces you need — preferably explicitly.

Casting an object to a base collection

I'm not totally convinced this is possible, but here goes. I have a method returning an object, although the actual type is Collection. Now, I can easily cast the object into the collection using
var myCollection = myObject as Collection<MyClassA>;
However the problem I have is that Collection<MyClassA> could alternatively be Collection<MyClassB> or Collection<MyClassC>. All of these MyClassX's are inherited from MyBaseClass, so ideally I would like to be able to do something like
var myCollection = myObject as Collection<MyBaseClass>;
However this throws an exception when casting. Is it possible to do this in anyway? I understand that it may be within .Net 4?
Thanks for the help.
EDIT:
OK - The answers so far are very useful, however they only solve the second part of the solution - converting/casting collections.
I am still unsure as to how I should initially cast the object to a collection (without the use of a huge if statement for each of the possible types)
This is only supported with IEnumerable<T> in .NET 4. Check out the difference in the signatures:
IEnumerable<T>:
public interface IEnumerable<out T> : IEnumerable
Collection<T>:
public class Collection<T> : IList<T>,
ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
That out keyword in the type parameter is what tells .NET to support variance.
Before I had access to .NET 4 I wrote an extension method that achieved this:
public static IEnumerable<U> CastCollection<T, U>(this IList<T> items) where U : class
{
var collection = new List<U>();
foreach (var item in items)
{
if (item is U)
{
var newItem = item as U;
collection.Add(newItem);
}
}
return collection;
}
You would use it like this:
var myCollection = myObject.CastCollection<MyClassA, MyBaseClass>();
myCollection will be an IEnumerable<MyBaseClass> in this case.
Alternate solution: you could use interfaces and generics to get what you want.
public interface IMyClass
{
}
public class MyClassA : IMyClass
{
}
public class MyClassB : IMyClass
{
}
public class MyClassC : IMyClass
{
}
static void Main(string[] args)
{
var listA = new List<IMyClass>{new MyClassA{}, new MyClassA{}};
var listB = new List<IMyClass> { new MyClassB { }, new MyClassB { } };
var listC = new List<IMyClass> { new MyClassC { }, new MyClassC { } };
List<IMyClass> genericList = listA.Cast<IMyClass>().ToList();
}
Something like this will compile properly and also allow you to assign different lists of any types that implement the common interface, to the same variable (in this case genericList.
This cannot be done by casting the collection as a whole. However, you can cast the individual elements to a new collection. Look at LINQ's Cast<> extension method.

Using a class derived from generic list with LINQ

I have two classes, CheckboxItemsList which extends a generic list, and CheckboxItems, which contains a list of objects of type CheckboxItem.
I want to use LINQ to be able to filter CheckboxItemsList based on properties of its CheckboxItems objects. The return type is always a generic list, though, but I want it to be a CheckboxItemsList.
So I guess the basic question is, can linq be made to return a list of the same type that it starts with? Since I can't cast a base class to a derived class, do I have any option other than iterating through the results of the linq query and rebuilding the derived list object row by row? Not that this is the end of the world, but I'm relatively new to linq and was wondering it there is a better way to do it.
What I want:
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed);
(obviously doesn't work since the query will return List<CheckboxItems>, not CheckboxItemsList)
The objects, generally:
public class CheckboxItemsList: List<CheckboxItems>
{
// does not add any fields, just access methods
}
public class CheckboxItems : IEnumerable<CheckboxItem>
{
public long PrimaryKey=0;
protected CheckboxItem[] InnerList;
public bool Changed
{
get {
return (InnerList.Any(item => item.Changed));
}
}
....
}
No, this is not possible out of the box. You'll need to add code to do this.
For example, you can add a constructor like so:
public CheckboxItemsList(IEnumerable<CheckboxItems> checkboxItems) {
// something happens
}
Then you can say
CheckboxItemsList newList = new CheckboxItemsList(
MyCheckboxItemsList.Where(item => item.Changed)
);
Additionally, you could add an extension method like so
static class IEnumerableCheckboxItemsExtensions {
public static ToCheckboxItemsList(
this IEnumerable<CheckboxItems> checkboxItems
) {
return new CheckboxItemsList(checkboxItems);
}
}
and then
CheckboxItemsList newList =
MyCheckboxItemsList.Where(item => item.Changed)
.ToCheckboxItemsList();
LINQ works on IEnumerable<T> and IQueryable<T> and the result types of all LINQ operations (Where, Select) etc, will return one of those. The standard ToList function returns a concrete list of type List<T>, you may need to come up with an extension method, e.g.:
public static CheckboxItemsList ToItemList(this IEnumerable<CheckboxItem> enumerable)
{
return new CheckboxItemsList(enumerable);
}
No, there's no built-in way to do this. You have two main options:
Add a constructor to your CheckboxItemsList class that takes an IEnumerable<CheckboxItems> or similar. Pass that collection on to the base List<T> constructor that takes an IEnumerable<T>. That base constructor should then populate the list for you:
var newList =
new CheckboxItemsList(MyCheckboxItemsList.Where(item=>item.Changed));
// ...
public class CheckboxItemsList : List<CheckboxItems>
{
public CheckboxItemsList(IEnumerable<CheckboxItems> collection)
: base(collection)
{
}
}
Create an extension method that takes an IEnumerable<CheckboxItems> or similar and returns a populated CheckboxItemsList:
var newList = MyCheckboxItemsList.Where(item=>item.Changed)
.ToCheckboxItemsList();
// ...
public static class EnumerableExtensions
{
public static CheckboxItemsList ToCheckboxItemsList(
this IEnumerable<CheckboxItems> source)
{
var list = new CheckboxItemsList();
foreach (T item in source)
{
list.Add(item);
}
return list;
}
}
(Of course, for completeness you could implement both of these options. The extension method would then just pass its IEnumerable<CheckboxItems> argument on to the constructor rather than manually looping and adding each item.)
You can also use "Conversion Operator", as below:
public class CheckboxItemsList: List<CheckboxItems>
{
public static implicit operator CheckboxItems(IEnumerable<CheckboxItems> items)
{
var list = new CheckboxItemsList();
foreach (var item in items)
{
list.Add(item);
}
return list;
}
}
Now, the below code would work.
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed);
From MSDN:
A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. This is described further in Section 6.1.
A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Explicit conversions can occur in cast expressions, and are described further in Section 6.2.
Here is what I came up with, building on the various suggestions of others. A generic extension method:
public static T ToList<T>(this IEnumerable baseList) where T : IList,new()
{
T newList = new T();
foreach (object obj in baseList)
{
newList.Add(obj);
}
return (newList);
}
So now I can do what I want:
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed)
.ToList<CheckboxItemsList>();
Another pretty obvious solution occurred to me, which is also useful for situations where the derived list class has field properties that I need to maintain in the new list.
Just create a new instance of my derived list class, and use AddRange to populate it.
// When created with a CheckboxItemsList parameter, it creates a new empty
// list but copies fields
CheckboxItemsList newList = new CheckboxItemsList(OriginalList);
newList.AddRange(OriginalList.Where(item => item.Changed));

Generic method to create deep copy of all elements in a collection

I have various ObservableCollections of different object types. I'd like to write a single method that will take a collection of any of these object types and return a new collection where each element is a deep copy of elements in the given collection. Here is an example for a specifc class
private static ObservableCollection<PropertyValueRow> DeepCopy(ObservableCollection<PropertyValueRow> list)
{
ObservableCollection<PropertyValueRow> newList = new ObservableCollection<PropertyValueRow>();
foreach (PropertyValueRow rec in list)
{
newList.Add((PropertyValueRow)rec.Clone());
}
return newList;
}
How can I make this method generic for any class which implements ICloneable?
You could do something like this:
private static ObservableCollection<T> DeepCopy<T>(ObservableCollection<T> list)
where T : ICloneable
{
ObservableCollection<T> newList = new ObservableCollection<T>();
foreach (T rec in list)
{
newList.Add((T)rec.Clone());
}
return newList;
}
Note that you could make this more general by taking IEnumerable<T>, and LINQ makes it even easier:
private static ObservableCollection<T> DeepCopy<T>(IEnumerable<T> list)
where T : ICloneable
{
return new ObservableCollection<T>(list.Select(x => x.Clone()).Cast<T>());
}
private static ObservableCollection<T> DeepCopy<T>(ObservableCollection<T> list)
where T : ICloneable
{
ObservableCollection<T> newList = new ObservableCollection<T>();
foreach (T rec in list)
{
newList.Add((T)rec.Clone());
}
return newList;
}
I use a very similar function which works with all ICollections which can be constructed (e.g. many standard collections):
public static TContainer CloneDeep<TContainer, T>( TContainer r )
where T : ICloneable
where TContainer: ICollection<T>, new()
{
// could use linq here, but this is my original pedestrian code ;-)
TContainer l = new TContainer();
foreach(var t in r)
{
l.Add( (T)t.Clone() );
}
return l;
}
Unfortunately the compiler isn't able to deduce the types so that one must pass them explicitly. For more than a handful calls I write a specialization. Here is an example for Lists (which itself can be called with implicitly deduced T).
public static List<T> CloneListDeep<T>( List<T> r ) where T : ICloneable
{
return CloneDeep<List<T>, T>( r );
}
I use this function extensively in order to create copies of lists serving as datasources for datagridviews on dialogs which can be canceled. The modified list is simply discarded when the dialog is cancelled; when the dialog is OKed the edited list simply replaces the original. Prerequisite for this pattern is, of course, to have a semantically correct and well maintained T.clone().

Categories

Resources