How to TypeCast SelectMany Results - c#

I'm trying to flatten a list-of-lists, and at the same time ensure that the list of final objects is of the correct type. Here's an (abstract) example:
class Space
{
public List<Space> ContainedSpaces
{
get;
set;
}
}
class Library : Space
{
public void AddRoom(Room room)
{
ContainedSpaces.Add(room);
}
}
class Room : Space
{
}
class Test
{
public List<Room> ListOfRooms(List<Library> libraries)
{
return libraries.SelectMany(lib => lib.ContainedSpaces).ToList();
}
}
My error in this example, in ListOfRooms, is:
Cannot implicitly convert type 'System.Collections.Generic.List<SoftTech.Integration.Space>' to 'System.Collections.Generic.List<SoftTech.Integration.Room>'
Without arguing about the class design (it's an abstract example), how do I get SelectMany to typecast each item in the final list to a Room object?

return libraries.SelectMany(lib => lib.ContainedSpaces).OfType<Room>().ToList();

how do I get SelectMany to typecast each item in the final list to a Room object
Well, you can't safely cast because not every Space is a Room. Technically a Library could contain other types of Spaces (even though you only define two).
In your design this would be perfectly legal:
Library l = new Library();
l.ContainedSpaces = new List<Space>();
l.ContainedSpaces.Add(new Library());
You can try to cast using:
libraries.SelectMany(lib => lib.ContainedSpaces).Cast<Room>().ToList();
but if some of the Spaces are not Rooms then you'll gat an InvalidCastException.
Or if you want just the spaces that are rooms then you can use OfType as suggested earlier:
libraries.SelectMany(lib => lib.ContainedSpaces).OfType<Room>().ToList();

Related

How can i cast nested generic types?

First of all i'm using Unity, which won't support any of the latest features like Except or Cast of IEnumerables.
I have an abstract class Item and another class Sword and Hammer, which both inherit from Item.
I also have a struct ItemHolder.
public struct ItemHolder<ItemType> where ItemType : Item {
public ItemType Item {get; set;}
}
I have two lists List<ItemHolder<Sword>> swordList and other List<ItemHolder<Hammer>> hammerList and want to generate a list of type List<ItemHolder<Item>>, which contains all elements of the other lists casted to ItemHolder<Item>.
I tried this code snippet:
public List<ItemHolder<Item>> GetAllItems() {
List<ItemHolder<Item>> allItems = new List<ItemHolder<Item>>();
List<ItemHolder<Item>> swordItemList =
CastListElementsToType<ItemHolder<Sword>, ItemHolder<Item>>(swordList);
//do the same thing for the hammers and add all elements to allItems.
return allItems;
}
private List<To> CastListElementsToType<From, To>(List<From> list) where From : To {
List<To> result = new List<To>();
foreach (From element in list) {
result.Add((To) element);
}
return result;
}
but it tells me, that there is no boxing conversion avaliable from ItemHolder<Sword> to ItemHolder<Item>.
Is there any way to solve this or do i need a completely new appraoch?

Sort ArrayList with custom comparison

I am trying to sort an ArrayList using c#. When the ArrayList contains comparable objects, it is possible to sort with using list.Sort() but I need to sort an ArrayList which contains non-comparable objects. For example, let's say the object is Ring and it has an attribute property Price. Then I need to sort the ArrayList to the price order. If is is possible to select ascending or descending that will more helpful. Thank You!
Blockquote
arrAtdMon = **(ArrayList)**hashTb[unixMon];
if (arrAtdMon != null)
monCount = arrAtdMon.Count;
int[] arrayMax = { monCount, tueCount, wedCount, thuCount, friCount };
int maxValue = arrayMax.Max();
KidAttendance valMon = null;
string monTagName = string.Empty;
Blockquote
above array list is to be sorted it self.
You can do this by implementing IComparer interface:-
public class Ring : IComparer
{
public decimal Price { get; set; }
public int Compare(object x, object y)
{
return ((Ring)x).Price.CompareTo(((Ring)y).Price);
}
}
Working Fiddle.
First, you really should be using the List<T> class, not ArrayList. Doing so wouldn't solve your problem, but it would make the code less fragile and more easy to maintain.
As for the specific question, you want to do something like this…
Assume:
class Ring { public decimal Price { get; set; } }
Then:
ArrayList list = ...; // Initialized as some collection of Ring instances
list.Sort(Comparer.Create((r1, r2) => r1.Price.CompareTo(r2.Price)));
This creates a new Comparer instance using the Comparison<T> of (r1, r2) => r1.Price.CompareTo(r2.Price). That is, for each pair of objects being compared, compare the price of the first with the price of the second.
Assuming that these objects share a base class or an interface with the price property you should be able to do something like this:
// Base class with price property, could also be an shared interface
public abstract class Product
{
public decimal Price{get;set;}
}
public class Ring : Product
{
}
public class Bag : Product
{
}
// Some test data
var myUnsortedArray = new Product[]{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArray.OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArray.OrderByDescending(p => p.Price).ToArray();
UPDATE
I just realised that the question is about ArrayLists and have the changed solution below:
// Some test data
var myUnsortedArrayList = new ArrayList{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArrayList.OfType<Product>().OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArrayList.OfType<Product>().OrderByDescending(p => p.Price).ToArray();
To sort an set of objects, the object needs to be Comparable and you can set up the comparison you'd like in the CompareTo() method:
IComparable information here

How to check a list for a different instance of a class inheriting generic interface

I have a List<ICar<T>> cars. I want to add new ICar<T> Honda to the list only if it does not already exist in there. The structure is like:
class Honda : IHonda{}
interface IHonda : ICar<T>
class Ford : IFord{}
interface IFord : ICar<T>
I only want to put one of each type of car into the list, but only knowing that they are ICar<T>. I don't know ahead of time the list of possible cars. How can this be done?
I was thinking something like
cars.Any(i => i.GetType().GetInterfaces().Contains(
car
.GetType()
.GetInterfaces()
.Where(s =>!cars.GetType().GenericTypeArguments != s.GenericTypeArguments
))))
However, something is wrong with that and I can't quite see a path through this one.
If you have a Type instance corresponding to T then you can use:
Type t = //
Type carType = typeof(ICar<>).MakeGenericType(t);
bool exists = cars.Any(c => carType.IsAssignableFrom(c.GetType()));
I think that you can use
//get list of all interfaces in the currrent car list
//you will have to check the contents of yourList as it may contain some types
//such as IList / IEnumerable that you will want to filter out.
var yourList = list.SelectMany(car => car.GetType().GetInterfaces());
//check get all interfaces on your car and check if the list contains it
bool exists = yourCar.GetType().GetInterfaces().Any(c => yourList.Contains(c));
You may be able to compare the elements using the fully qualified name of the type.
CarTypeComparer compareCarTypes = new CarTypeComparer();
if (!List.contains(car, compareCarTypes))
{
// insert car into list
}
class CarTypeComparer : IEqualityComparer<ICar<T>>
{
public bool Equals(ICar<T> car1, ICar<T> car2)
{
if (car1.GetType().FullName == car2.GetType().Fullname)
{
return true;
}
else
{
return false;
}
}
// implement GetHashCode() ....
}
Add a CarModel property inside of your ICar interface,and then implement it in your classes.For example in Honda class:
public string CarModel { get { return "Honda"; } }
you can check CarModel property in your query and ofcourse you can group your cars by CarModel,i hope this helps..

Use inherit class for instance List in C#

Hello stackoverflow community!
Let's start!
I have one small class Person:
class Person
{
public string name { get; set; }
}
Аnd it has a descendant Employee:
class Employee : Person
{
public int salary { get; set; }
}
And second descendant is Guest:
class Guest: Person
{
public int id { get; set; }
}
Ok! looks good :)
Now I want to display a list of all employees OR guests in a single control ListView
I made a class (it really necessary) for list management PeopleList:
class PeopleList
{
public List<Person> People { get; set; }
...
public void LoadListFromFile()
{
// Load logic
this.People = new List<?>(source);
}
}
Do you see this question mark? No? Look at the code again!
How to create an instance of List that I can use my class something like this:
// This instance with the list of Person objects
PeopleList list = new PeopleList();
foreach (Employee e in list.People)
{
Debug.WriteLine(e.salary.toString());
}
// This instance with the list of Guest objects
PeopleList list = new PeopleList();
foreach (Guest g in list.People)
{
Debug.WriteLine(g.id.toString());
}
P.S. I'm new in c# and I think that I have a problem in architecture. And maybe you point me to the pattern solves my problem. I really need your help! Thank you!
I think you're after OfType, in the System.Linq library:
foreach (Employee e in personList.OfType<Employee>())
{
Debug.WriteLine(e.salary.toString());
}
foreach (Guest g in personList.OfType<Guest>())
{
Debug.WriteLine(g.id.toString());
}
The only field in Person that is shared with all of the decedents is the field 'name', therefore, if you are casting each item in your list to Person you will only have access to 'name'.
You have a few options to solve this issue. 1) you could move all of the common fields into the base class, but this is probably not want since it defeats the point of an object hierarchy, or 2) you can type check, using the "Is" keyword, each person in the list so see what type of person it is and then cast that Person to the appropriate decedent class before you operate on it.
For example:
foreach (Person p in list.People)
{
if(p is Employee)
{
Debug.WriteLine(((Employee)p).salary.toString());
}
if(p is Guest)
{
Debug.WriteLine(((Guest)p).Id.toString());
}
}
Here is an alternate more clear way of casting
foreach (Person p in list.People)
{
if(p is Employee)
{
Employee employee = p as Employee;
Debug.WriteLine(employee.salary.toString());
}
if(p is Guest)
{
Guest guest = p as Guest;
Debug.WriteLine(guest.Id.toString());
}
}
Additionally you can learn more about type checking using the "is" keyword here.
Also just for clarity, since it might not be obvious, some others have suggested that you use OfType in linq but this more of a way to filter like object from a list of mixed objects as opposed to actually type checking each one.
Enjoy!
As the comments are saying, you can do the following;
public void LoadListFromFile()
{
// Load logic
this.People = new List<Person>(source);
}
And then, cast where appropriate;
foreach (Person e in list.People.Where(p => p.GetType() == typeof(Employee)))
{
Debug.WriteLine(((Employee)e).salary.toString());
}
I think in your particular example, the inheritance structure is not adding much benefit, but I digress.
In your example, the easiest solution would be to override the ToString method in each one of your classes.
Having to know the exact type of the object before hand before you run some sort of calculation or display puts the onus of that calculation onto the calling code, if you make a 3rd type, you would have to update all possible references, giving you a maintenance nightmare.
The feel the logic of how to represent itself should be the responsibility (in your example) of the object itself.
This is what overriding ToString gives you, the responsibility of representing itself is then given to the thing that knows best (the object you're asking).

Single sorted c# list with different types?

Yes, I'm new to c#! :) i'm using .Net4 VS2010.
I have Three classes each one is used to build a list of objects of that type. All three inherit form a base class.
I want to combine the resulting three lists in to one and sort them on one of the base class elements.
Can this be done with lists of different types?
Simplified Example:
Each list is created
public List<TestOne> TestOne list;
public List<TestTwo> TestTwoList;
public List<object> BothLists;
Code to fill TestOne and TestTwo…
What/How do I combine both TestOne and TestTwo into BothLists and sort them on SeqNumber???
public class BaseClassTest
{
public string Loc { get; set; } // loc
// sequence number to order by will be assigned in the resulting class
public int SeqNumber { get; set; }
}
public class TestOne : BaseClassTest
{
public int Number { get; set; }
}
public class TestTwo : BaseClassTest
{
public string CatName { get; set; }
}
You should be able to do:
List<BaseClassTest> sorted = TestOneList.Cast<BaseClassTest>()
.Union(TestTwoList.Cast<BaseClassTest>())
.OrderBy(item => item.SeqNumber)
.ToList();
This will do your sort + union all at once.
"BothLists" should be a List<BaseClassTest>. That should let you sort on base class properties, using .OrderBy(x => x.SequenceNumber).
EDITED TO ADD:
Following up from comments, this should work to combine the lists:
BothLists = TestOneList.OfType<BaseClassList>().Concat().(TestTwoList.OfType<BaseClassList>()).ToList();
Generally, .OfType<>() is preferable to .Cast<>() because it simply filters based on type, rather than forcing a cast.
Given your example this is quite easy:
public List<BaseClassTest> BothLists;
Then you can sort:
BothLists.Sort((a, b) => a.SeqNumber.CompareTo(b.SeqNumber));
If a class inherits from a base, it counts as the base class for most operations. Thus:
List<BaseClassTest> AllTests;
Should get you what you need.
Make TestOne and TestTwo implement the IComparable interface. See here. Then you can combine the lists into an ArrayList and use the Sort method.
List<BaseClassTest> list3 = new List<BaseClassTest>();
list3.AddRange(list1);
list3.AddRange(list2);
var sorted = list3.OrderBy(e => e.SequenceNum).ToList(); //if you really need a list back
This is all assuming you have Linq available. Of course if you do a Union statement might work also.

Categories