Using a class derived from generic list with LINQ - c#

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));

Related

IEnumerable<IEnumerable>> and Extension Method

I was wondering if someone could help me understand the following behavior. In the following code, I am creating a CustomObject instance, which contains a single Property of type IEnumerable<IEnunumerable>>.
I also have an extension Method on IEnumerable<T> called AsDataTable.
public class CustomObject
{
public IEnumerable<IEnumerable> Collection {get;set;}
}
public static class ExtensionMethods
{
public static bool AsDataTable<T>(this IEnumerable<T> list)
{
Console.Write("In extension method");
return default(bool);
}
}
void Main()
{
var ttObject = new CustomObject()
{
Collection = new List<IEnumerable>
{
new List<int>{1,2,3},
new [] {new{A="abc",B="def"}}
}
};
var dummy = new []{new {a='r'}}.AsDataTable();
foreach(var item in ttObject.Collection)
{
var temp = item.AsDataTable();
Console.WriteLine($"Item is IEnumerable : {item is IEnumerable}");
}
}
What makes me wonder if the following line of code works (or rather compiles)
var dummy = new []{new {a='r',b='3'}}.AsDataTable();
while when I loop over the Collection Property of CustomObject and then do the same it doesn't allow me to compile.
var temp = item.AsDataTable(); // this doesn't work
Curiously the following line returns true reconfirming 'item' is indeed IEnumerable.
Console.WriteLine($"Item is IEnumerable : {item is IEnumerable}");
I guess it is because the extension method is written over Generic version IEnumerable<T>, but then how is it that it works over the anonymous type array (outside the CustomObject).
IEnumerable<T> implements IEnumerable, not vice versa.
Through a bit of runtime hacking, SomeType[] actually does implement IEnumerable<SomeType>. On the other hand, IEnumerable doesn't - and overload resolution is done at compile time, so the compiler has no idea that your IEnumerable items in the collection actually also implement IEnumerable<int>.
If you need to work with IEnumerable, you need to use that in your extension method.

C# collection initializer – is it possible to add an element optionally, based on a condition?

Right now my code looks like this:
var ids = projectId.HasValue ? new List<Guid> { projectId.Value } : new List<Guid>();
Is there a more succinct way of creating a list in one line of code, with one element added optionally?
Another idea for an extension method (the name could definitely be improved, maybe PossiblyCreateSingletonList?):
public static class NullableExtensions
{
public static List<T> SingletonList<T>(this Nullable<T> item) where T : struct
{
return item.HasValue ? new List<T> { item.Value } : new List<T>();
}
}
Usage:
Guid? projectId = null;
List<Guid> projectIds = projectId.SingletonList(); // empty list
I would solve this using a extension method like this:
public static void AddIfNotNull<T>(this List<T> list, T? value) where T : struct
{
if(value != null)
{
list.Add(value.Value);
}
}
Than it could be used like this:
var ids = new List<Guid>();
ids.AddIfNotNull(projectId);
Maybe not as "crafty" (and not a one-liner) as your proposal, but in my opinion it is much easier to read and understand. If desired to be used as a one-liner you could modify the return type of the extension to be the list. That would make it possible to be used something like var ids = new List<Guid>().AddIfNotNull(projectId);
This probably isn't a good idea, but in C# 6, collection initializers also work when Add() is an extension method.
This means you can write the extension Add() like this:
public static void Add<T>(this List<T> list, T? item) where T : struct
{
if (item.HasValue)
{
list.Add(item.Value);
}
}
And then this code will do what you want:
var list = new List<Guid> { projectId };
Note that this will only work for value types (because of the T/T? distinction) and there is no simple way to make it work for reference types.
Also, I would find the line above very surprising, being more succinct is not always better. Which is why I actually wouldn't use this code.
That's pretty succinct, but another option would be to use LINQ:
var ids = new[] { projectId }.Where(x => x.HasValue).Select(x => x.Value).ToList();
If you're going the extension method route, it would have to look something like:
public static void AddIfNotNull<T>(this List<T> list, T? value)
where T : struct
{
if (value.HasValue)
{
list.Add(value.Value);
}
}
You'd have to build a second extension method for reference types (where T : class) if you needed.

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));

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().

Method-Chaining in C#

I have actually no idea of what this is called in C#.
But i want to add the functionallity to my class to add multiple items at the same time.
myObj.AddItem(mItem).AddItem(mItem2).AddItem(mItem3);
The technique you mention is called chainable methods. It is commonly used when creating DSLs or fluent interfaces in C#.
The typical pattern is to have your AddItem() method return an instance of the class (or interface) it is part of. This allows subsequent calls to be chained to it.
public MyCollection AddItem( MyItem item )
{
// internal logic...
return this;
}
Some alternatives to method chaining, for adding items to a collection, include:
Using the params syntax to allow multiple items to be passed to your method as an array. Useful when you want to hide the array creation and provide a variable argument syntax to your methods:
public void AddItems( params MyItem[] items )
{
foreach( var item in items )
m_innerCollection.Add( item );
}
// can be called with any number of arguments...
coll.AddItems( first, second, third );
coll.AddItems( first, second, third, fourth, fifth );
Providing an overload of type IEnumerable or IEnumerable so that multiple items can be passed together to your collection class.
public void AddItems( IEnumerable<MyClass> items )
{
foreach( var item in items )
m_innerCollection.Add( item );
}
Use .NET 3.5 collection initializer syntax. You class must provide a single parameter Add( item ) method, implement IEnumerable, and must have a default constructor (or you must call a specific constructor in the initialization statement). Then you can write:
var myColl = new MyCollection { first, second, third, ... };
Use this trick:
public class MyClass
{
private List<MyItem> _Items = new List<MyItem> ();
public MyClass AddItem (MyItem item)
{
// Add the object
if (item != null)
_Items.Add (item)
return this;
}
}
It returns the current instance which will allow you to chain method calls (thus adding multiple objects "at the same time".
"I have actually no idea of what this is called in c#"
A fluent API; StringBuilder is the most common .NET example:
var sb = new StringBuilder();
string s = sb.Append("this").Append(' ').Append("is a ").Append("silly way to")
.AppendLine("append strings").ToString();
Others have answered in terms of straight method chaining, but if you're using C# 3.0 you might be interested in collection initializers... they're only available when you make a constructor call, and only if your type has appropriate Add methods and implements IEnumerable, but then you can do:
MyClass myClass = new MyClass { item1, item2, item3 };
Why don't you use the params keyword?
public void AddItem (params MyClass[] object)
{
// Add the multiple items
}
You could add an extension method to support this, provided your class inherits from ICollection:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void CanChainStrings()
{
ICollection<string> strings = new List<string>();
strings.AddItem("Another").AddItem("String");
Assert.AreEqual(2, strings.Count);
}
}
public static class ChainAdd
{
public static ICollection<T> AddItem<T>(this ICollection<T> collection, T item)
{
collection.Add(item);
return collection;
}
}
How about
AddItem(ICollection<Item> items);
or
AddItem(params Item[] items);
You can use them like this
myObj.AddItem(new Item[] { item1, item2, item3 });
myObj.AddItem(item1, item2, item3);
This is not method chaining, but it adds multiple items to your object in one call.
If your item is acting as a list, you may want to implement an interface like iList or iEnumerable / iEnumerable.
Regardless, the key to chaining calls like you want to is returning the object you want.
public Class Foo
{
public Foo AddItem(Foo object)
{
//Add object to your collection internally
return this;
}
}
Something like this?
class MyCollection
{
public MyCollection AddItem(Object item)
{
// do stuff
return this;
}
}

Categories

Resources