I have a class that extends List:
public class MyObjectList : List<MyObject>
{
...
}
Currently, there is a LINQ statement that groups by a key value:
MyObjectList objects = new MyObjectList(); //initializes and loads list
var objectsByKey = objects.GroupBy(obj => obj.MyKey)
.Select(objs => new {MyKey = objs.Key, MyObjs = objs.ToList()})
.ToList();
In the output, MyObjs is of type List< MyObject>, and it lists the correctly grouped objects.
When I try to cast it as MyObjectList, MyObjs ends up being null.
var objectsByKey = objects.GroupBy(obj => obj.MyKey)
.Select(objs => new {MyKey = objs.Key, MyObjs = objs.ToList() as MyObjectList})
.ToList();
How can I get MyObjs to be of type MyObjectList, with the correclty grouped objects?
In your MyObjectList class, provide a constructor like so:
public class MyObjectList : List<MyObject>
{
public MyObjectList(IEnumerable<MyObject> list)
:base(list)
{
}
}
Then, when selecting out your objects, do it like this:
var objectsByKey = objects.GroupBy(obj => obj.MyKey)
.Select(objs => new {MyKey = objs.Key, MyObjs = new MyObjectList(objs)})
.ToList();
Enumerable.ToList (the extension method that you are using) does not know about your custom list type. Casting will not convert it.
You need to instantiate and fill the list yourself. There are at least two ways of doing it:
Constructor
public MyObjectList(IEnumerable<MyObject> collection) : base(collection) {}
You'd have to replace the objs.ToList() call with new MyObjectList(objs)
Extension method
public static class MyObjectListExtensions
{
public static MyObjectList ToList(this IEnumerable<MyObject> collection)
{
//You could also call the constructor described above
var list = new MyObjectList();
list.AddRange(collection);
return list;
}
}
With this, you don't have to change your code, as long as you include the namespace of MyObjectListExtensions in your file. Why? Because being non-generic, MyObjectListExtensions.ToList takes precedence over Enumerable.ToList.
If you don't want MyObjectList to be instantiated always, you could use a different name.
Related
I have a class A from which B and C inherit.
I have two lists: listB and listC, of the respective types.
I want to make a method that returns the two lists inside an array, like so:
public override List<A>[] GetAllItems()
{
return new List<A>[2]
{
listB,
listC
};
}
However, when I try this approach, I get the following error, because I try to convert the inherited types incorrectly.
Cannot implicitly convert type 'System.Collections.Generic.List<Lae.B>' to 'System.Collections.Generic.List<Lae.A>' [Assembly-CSharp]csharp(CS0029)
Is there some way to create this array without converting the elements?
Note: I am not using a struct or class, because I want the array to be of various sizes based on logic above.
public List<A>[] GetAllItems()
{
var result = new List<A>[2] {
listB.Cast<A>().ToList(),
listC.Cast<A>().ToList(),
};
return result;
}
If you need to return array of Lists - easiest way is to use Cast linq extension method.
In reference to the comments you have to remember that if you modify listB or listC, the change won't be reflected in the casted collections.
Anyway, if you need to have an access to the original listB / listC collections references, you can use IEnumerable instead of List in order to not be forced to "materialize" the results. Example:
public IEnumerable<A>[] GetAllItems()
{
return new IEnumerable<A>[] {
listB,
listC,
};
}
Now when you access eg. allItems[0] it will reference to the original listB collection.
You can't do that because instance of B is an also A but instance of List of B is not also List of A.
You should box the types in the collection, you can use Cast function of in the Linq namespace.
using System.Linq;
List<A>[] GetAllItems()
{
var result = new List<A>[2] {
listB.Cast<A>().ToList(),
listC.Cast<A>().ToList(),
};
return result;
}
Or you can do that manualy.
List<A>[] GetAllItems()
{
var boxedListB = new List<A>();
var boxedListC = new List<A>();
foreach (var item in listB)
{
boxedListB.Add(item);
}
foreach (var item in listC)
{
boxedListC.Add(item);
}
var result = new List<A>[2] {
boxedListB,
boxedListC
};
return result;
}
Or you can use Select function in System.Linq namespace.
List<A>[] GetAllItems()
{
var result = new List<A>[2] {
listB.Select(x=> x as A).ToList(),
listC.Select(x=>x as A).ToList()
};
return result;
}
You can check this document for more information about boxing/unboxing.
I have following TracefieldPartProgramClass
public class TracefieldPartProgramClass
{
public TracefieldPartProgramClass() { }
public ObservableCollection<Tuple<string, object, object>> obcTraceFieldPartProgram = new ObservableCollection<Tuple<string, object, object>>();
}
I use it to make the following collection:
ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>();
now after having filled it I want to be able to sort as I want (say on the Tracefield[0]).
So I implemented this:
private ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> SortOnTracefield(ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> obcToSort)
{
var obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>();
obcSorted = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString());<--- this is where I get the error
return obcSorted;
}
but when I do it, I get this error:
Error CS0266 Cannot implicitly convert type 'System.Linq.IOrderedEnumerable' to 'System.Collections.ObjectModel.ObservableCollection'. An explicit conversion exists (are you missing a cast?)
Try this one:
private ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> SortOnTracefield(ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> obcToSort)
{
var sorted = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString();
return new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(sorted);
}
You can pass an IEnumerable to the observable collection's constructor.
However, that returns a new instance of the collection. So, if the unsorted instance was bound to the GUI, that may not update your GUI. Another approch to support sorting is to use the ICollectionView.
The SO question how-do-i-sort-an-observable-collection may be intersting for you too. The following extension method can be used for sorting ObservableCollections without recreation:
static class Extensions
{
public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable
{
List<T> sorted = collection.OrderBy(x => x).ToList();
for (int i = 0; i < sorted.Count(); i++)
collection.Move(collection.IndexOf(sorted[i]), i);
}
}
However, your TracefieldPartProgramClass class needs to implement IComparable or you'll need to implement a custom IComparer<TracefieldPartProgramClass> and pass it the Sort method.
As the error says, you are trying to assign an IOrderedEnumerable to a variable of type ObservableCollection, which is not possible.
But luckily, ObservableCollection has a constructor that takes an IEnumerable and prefills it with the items of that IEnumerable.
So, in order to make that error disappear, use that:
var sortedEnumerable = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString());
obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(sortedEnumerable );
Enumerable.OrderBy doesn't return an ObservableCollection but an IEnumerable<T>, you can create a new with the List<T> constructor:
var obcSortedList = obcToSort
.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString())
.ToList();
var obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(obcSortedList);
This seems like it should be easy, but I can't figure out a tidy way to do it.
With the class
class SomeType
{
public int SomeInteger {get;}
public string SomeString {get;}
public object SomeObject {get;}
}
Assuming that a list of SomeType is retrieved from somewhere, but then I need to remove the SomeString field from every item in the list. So I iterate over the list, create an anonymous type and add it to the new list.
List<SomeType> list = GetList();
var newList = new List<dynamic>();
list.ForEach(item => newList.Add(new { SomeInteger = item.SomeInteger, SomeObject = item.SomeObject });
Is there a better way of doing this that doesn't require me to create an empty list of type dynamic? I could be wrong, but it feels like it's not the best way to do this.
You can create a list of anonymous types directly, and this will be strongly-typed:
list.Select(item => new
{
SomeInteger = item.SomeInteger,
SomeObject = item.SomeObject
}).ToList();
You do not need an extra statement to declare newList. The c# compiler can determine the type automagically. See Implicitly typed local variables for more details.
var newList = GetList()
.Select(x => new { SomeInteger = x.SomeInteger, SomeObject = x.SomeObject})
.ToList();
I have a function, where i could send all objects of all types of my project and it should iterate over properties and output their values:
public void ShowMeAll(IEnumerable<object> items);
IEnumerable<Car> _cars = repository.GetAllCars();
ShowMeAll(_cars);
IEnumerable<House> _houses = repository.GetAllHouses();
ShowMeAll(_houses);
Ok, for example, it's so. Now, i'd like to send into my ShowMeAll function a property, which over i'd like to OrderBy my items and then output. What is the most correct way to do this with a parameter of function?
Easiest way is to let LINQ do that for you, via the OrderBy() method. For example:
IEnumerable<Car> _cars = repository.GetAllCars();
ShowMeAll(_cars.OrderBy(car => car.Make));
IEnumerable<House> _houses = repository.GetAllHouses();
ShowMeAll(_houses.OrderBy(house => house.SquareFootage));
That way, you remove the requirement for ShowMeAll to be aware of the properties of the objects passed in. Since you're passing List<object>, I assume that's desired. :)
private static void ShowMeAll<TClass>(IEnumerable<TClass> items, string property)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(TClass));
PropertyDescriptor targetProperty = properties.Find(property, true);
if (targetProperty == null)
{
// Your own error handling
}
IEnumerable<TClass> sortedItems = items.OrderBy( a => targetProperty.GetValue(a));
// Your own code to display your sortedItems
}
You would call the method like this:
ShowMeAll<Car>(_cars, "Make");
I've left out the error handling because I do not know what your requirements are
private static void ShowMeAll<TClass>(IEnumerable<TClass> items, string property )
{
// 1. Discover property type ONCE using reflection
// 2. Create a dynamic method to access the property in a strongly typed fashion
// 3. Cache the dynamic method for later use
// here, my dynamic method is called dynamicPropertyGetter
IEnumerable<TClass> sortedItems = items.OrderBy( o => dynamicPropertyGetter( o ) );
}
Dynamic methods (easier than they look, and 50-100x faster than reflection in my tests): http://msdn.microsoft.com/en-us/library/exczf7b9.aspx
Expression builders can also get the job done: http://msdn.microsoft.com/en-us/library/system.web.compilation.expressionbuilder.aspx
kind of this?
public void Test()
{
var o = new[] {new {Name = "test", Age = 10}, new {Name = "test2", Age = 5}};
ShowMeAll(o, i => i.Age);
}
public void ShowMeAll<T>(IEnumerable<T> items, Func<T, object> keySelector)
{
items.OrderBy(keySelector)
.ToList()
.ForEach(t => Console.WriteLine(t));
}
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));