Casting class to interface and back - c#

I have the following:
public interface ICartItem
{
string Name { get; set; }
}
public class CartItem : ICartItem
{
public string Name { get; set; }
}
I then create a List and cast it to an interface:
IList<CartItem> items = new List<CartItem>()
{
new CartItem() { Name = "MyItem" }
};
IList<ICartItem> cartItems = items.Cast<ICartItem>().ToList();
Is there a way to cast it back again like illustrated below?
IList<CartItem> newList = cartItems as IList<CartItem>;

Do you need a copy of the list?
If yes, and you are sure that there are only CartItems within the list you can do it with
IList<CartItem> newList = cartItems.Cast<CartItem>().ToList();
But i think you'd like it a little more robust. In that case you can try it with
cartItems.Where(item => item as CartItem != null).Cast<CartItem>().ToList();
But i think you need to create a new list. I can't think of a way to work on the same with an IList interface (IEnumerable<T> works as shown above).

This is not a logical issue because you cannot make sure that all the items in the 'cartItems' list can be casting to 'CartItem' type.

Basically, the answer is no, IList<ICartItem> cannot be cast back to IList since CartItem is not the only type that might implement ICartItem. The cast cannot be type checked by the compiler since it does't know what will be inside the list at runtime.
In C# 4, you can do this:
IEnumerable<CartItem> items = new List<CartItem>
{
new CartItem() { Name = "MyItem" }
};
IEnumerable<ICartItem> cartItems = items;
(i.e. no need for the use of .Cast<ICartItem>().ToList())
Note that the interface is IEnumerable<> and not IList<> since only some interfaces were made covariant in C# 4 (the full list cn be found here: http://msdn.microsoft.com/en-us/library/dd233059.aspx).
However, even in C# 4, adding the following line will cause the compiler to fail:
IEnumerable<CartItem> newList = cartItems;

Related

Unable to cast object when LINQ query ToList() cast to a specific List<T> subclass

I am getting a runtime exception and can't figure out why
Unable to cast object of type
'System.Collections.Generic.List`1[Foo.ElementNameViewModel]'
to type
'Foo.ElementNameList'.
The classes I have are
ElementNameList - a list of a particular type
namespace Foo
{
public class ElementNameList : List<ElementNameViewModel> {}
}
ElementNameViewModel - an item to be in a list
namespace Foo
{
public class ElementNameViewModel
{
public string Symbol { get; set; }
public string Name { get; set; }
}
}
and the exception occurs in a controller at
var elements = (ElementNameList) db.Elements
.Select(c => new ElementNameViewModel
{
Symbol = c.Symbol,
Name = c.Name
})
.OrderBy(e => e.Symbol)
.ToList();
Not sure how I should refactor the elements list if subclassing List is not done.
ToList() returns a real System.Collections.Generic.List instance, not your ElementNameList.
You can cast ElementNameList to IList<ElementNameViewModel> or to List<ElementNameViewModel>, but not the other way.
The reason is because your ElementNameList may have a property that the List<ElementNameViewModel> doesn't have. What would happen if you try to access that property after the cast?
public class ElementNameList : List<ElementNameViewModel> {
public int X { get; set; }
}
var list = new List<ElementNameViewModel>();
ElementNameList elementList = (ElementNameList) list;
int x = elementList.X; // List<ElementNameViewModel> doesn't have 'X' property, what would happen here?
As said in other answers like Optimistic Peachs' and lmcarreiro's one, the ToList() LINQ extension method returns an
basic List<T> object, not an object of your customized ElementNameList class.
To solve your immediate problem, I would write it as:
var qry = db.Elements
.Select(c => new ElementNameViewModel
{
Symbol = c.Symbol,
Name = c.Name
})
.OrderBy(e => e.Symbol);
var elements = new ElementNameList();
elements.AddRange(qry);
I don't have an Visual Studio at hand now to see if I need to
use qry.ToList() in the AddRange call, but I believe that
is enough.
By the way, why you had to subclass the list?
Casting to inherited/derived members only goes one way... And that is up the derived hierarchy.
For example:
class Animal{
int number_of_legs;
}
class Cat: Animal{
string current_sound = "meow";
}
If you create an animal (Which should really be abstract in this case, but ignore that) and want to convert it into a cat, and assign that to a variable mycat then what would the value stored in mycat.current_sound be? It cannot be inferred because Cat has all the members that Animal has and more, so the conversion from Cat to Animal would make you lose the members that Cat has and only be left with the members from Animal (Unless you cast it again but that's irrelevant...)
P.S. I've recently been coding in a different language so pardon my naming of variables and format of my code

IList<IList<T>> to IReadonlyCollection<IReadonlyCollection<T>>

I have a list of string array and I would like to make both collection read-only.
So I have this code:
public XmlPatternTree(IList<string> nodeNames, IList<IList<string>> attributeNames,
IList<IList<string>> attributeValues) : this()
{
NodeNames = new ReadOnlyCollection<string>(nodeNames);
AttributeNames = new ReadOnlyCollection<ReadOnlyCollection<string>>();
AttributeValues = attributeValues;
Depth = NodeNames.Count;
}
My issue is that AttributeNames and AttributeValues assignments causes a compilation error, it seems that I can create a ReadonlyCollection of ReadonlyCollection from a non-readonly collection of non-readonly objects.
Is there something I can do other than looping over all the values and add them in the list ?
Thanks
If you change your type from IList<string> to just List<string>, then this should work:
attributeNames.Select((x) => x.AsReadOnly()).ToList().AsReadOnly();
If you can't modify your method signature (i.e. you have to keep IList<string>), then you can do this:
attributeNames.Select((x) => x.ToList().AsReadOnly()).ToList().AsReadOnly();
If the version of the .net framework is greater then 4.0 the generic version of List<> implements the IReadOnlyCollection<> interface.
If it is more convenient for you, you can change your signature from IList<ILIst<>> to List<List<>> and should work fine.
AttributeNames = attributeNames;
AttributeValues = attributeValues;
Just a note on the covariance of the IReadOnlyList<out T> type (similar to vasil oreshenski's answer).
If you decide to have:
public XmlPatternTree(IReadOnlyList<string> nodeNames,
IReadOnlyList<IReadOnlyList<string>> attributeNames,
IReadOnlyList<IReadOnlyList<string>> attributeValues) : this()
{
NodeNames = nodeNames;
AttributeNames = attributeNames;
AttributeValues = attributeValues;
}
public IReadOnlyList<string> NodeNames { get; private set; }
public IReadOnlyList<IReadOnlyList<string>> AttributeNames { get; private set; }
public IReadOnlyList<IReadOnlyList<string>> AttributeValues { get; private set; }
public int Depth => NodeNames.Count;
in your class, then the covariance mentioned means you can use reference conversions, and not any wrapping inside another class, as in:
var nn = new List<string>();
var an = new List<string[]>();
var av = new List<string[]>();
// populate 'nn', 'an', and 'av'
// the following compiles with no wrapper class:
var tree = new XmlPatternTree(nn, an, av);
Of course, people can cast the interfaces back to the actual types, like List<string[]>, and modify the collections without using reflection, if they guess that the type is really that list of arrays. However, that would be quite malignant, so you could assume it is no problem if only "good" people use your class
PS! What I said and coded above with IReadOnlyList<out T> could just as well have been done with IReadOnlyCollection<out T> since it is covariant ("out") as well. You would just not have the indexer access on the properties (such as var name = tree.AttrbuteNames[idx1][idx2]). But then you could use HashSet<> and similar which are not IReadOnlyList<>.

Inheritance and return types [duplicate]

public interface IDic
{
int Id { get; set; }
string Name { get; set; }
}
public class Client : IDic
{
}
How can I cast List<Client> to List<IDic>?
You can't cast it (preserving reference identity) - that would be unsafe. For example:
public interface IFruit {}
public class Apple : IFruit {}
public class Banana : IFruit {}
...
List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());
// Eek - it's a banana!
Apple apple = apples[0];
Now you can convert a List<Apple> to an IEnumerable<IFruit> in .NET 4 / C# 4 due to covariance, but if you want a List<IFruit> you'd have to create a new list. For example:
// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();
// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();
But this is not the same as casting the original list - because now there are two separate lists. This is safe, but you need to understand that changes made to one list won't be seen in the other list. (Modifications to the objects that the lists refer to will be seen, of course.)
A Cast iterator and .ToList():
List<IDic> casted = input.Cast<IDic>().ToList() will do the trick.
Originally I said covariance would work - but as Jon has rightly pointed out; no it won't!
And originally I also stupidly left off the ToList() call
I too had this problem and after reading Jon Skeet's answer I modified my code from using List<T> to use IEnumerable<T>. Although this does not answer the OP's original question of How can I cast List<Client> to List<IDic>, it does avoid the need to do so and thus may be helpful to others who encounter this issue. This of course assumes that the code that requires the use of List<IDic> is under your control.
E.g.:
public void ProcessIDic(IEnumerable<IDic> sequence)
{
// Implementation
}
Instead of:
public void ProcessIDic(List<IDic> list)
{
// Implementation
}
If you can use LINQ then you can do this...
List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Its only possible by creating new List<IDic> and transfering all elements.
In .Net 3.5, you can do the following:
List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());
The constructor for List in this case takes an IEnumerable.
list though is only convertible to IEnumerable. Even though myObj may be convertible to ISomeInterface the type IEnumerable is not convertible to IEnumerable.
OfType
You can try something like:
using (var dbContext = YourDatabaseContext())
{
var list = dbContext.Clients.Where(x => x.Happy)
.OfType<IDic>()
.ToList();
}
See https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.oftype
If you want to process the original list without creating a separated reference, you could define the generic method like this:
public void DoIterate<T>(List<T> myCollection) where T : IDic
{
foreach (T item in myCollection)
{
//update a property of interface
item.Name = "new Name";
}
}
Calling this method above to process the list without having to cast specific object to interface:
List<Client> clients = new List<Client>();
DoIterate(clients);
If you don't need to modify the contents of the original list, you can implicitly convert a List into a IReadOnlyList which will let you iterate over it's contents as IDics without creating a new list.
List<Client> myClients = new List<Client>();
myClients.Add(new Client());
IReadOnlyList<IDic> castedClients = myClients;
foreach(IDic val in castedClients)
{
//do something;
}
The conversion can also occur while simply returning the list like so :
public IReadOnlyList<IDic> getClientsAsIDic()
{
return myClients;
}

Generic collection of generic classes?

I have a class that I fill from the database:
public class Option<T>
{
public T Value { get; set; }
public T DefaultValue { get; set; }
public List<T> AvailableValues { get; set; }
}
I want to have a collection of them:
List<Option<T>> list = new List<Option<T>>();
Option<bool> TestBool = new Option<bool>();
TestBool.Value = true;
TestBool.DefaultValue = false;
list.Add(TestBool);
Option<int> TestInt = new Option<int>();
TestInt.Value = 1;
TestInt.DefaultValue = 0;
list.Add(TestInt);
It doesn't seem to work. Ideas?
I suspect you really want a nongeneric base class - otherwise there's really nothing in common between the different Option<T> closed types.
I understand what you're trying to do, but .NET generics don't allow you to express that relationship. It's like trying to do a map from Type to an instance of that type... it just doesn't fly :(
You have to provide a type instead of your template parameter:
List<Option<T>> list = new List<Option<T>>();
becomes
List<Option<bool>> list = new List<Option<bool>>();
Adding items of type Option<int> to that same list won't work, but that is a separate issue than what I've addressed above.
Firts line you must say wich type T is.
List<Option<bool>> list = new List<Option<bool>>();
And also you can't put that TestInt in this list...
What you're doing only works with heterogeneous lists.
List<T> is an homogeneous list of type T, meaning all elements have to be of type T. Because Option<bool> and Option<int> do not have a common ancestor other than object you can't do that unless you use a List<object> or the old ArrayList, both of which act as heterogeneous lists.
Think of retrieving objects from that list:
list.Add(TestBool);
list.Add(TestInt);
for(int i = 0; i < list.Count; i++)
{
list[i].Value // <- what's the type of this?
}

Adding a list item within another list

Ok I have a class similar to the following...
public class Order
{
private Guid id;
[DataMember]
public Guid ID
{
get { return id; }
set { id = value; }
}
private List<Items> orderItems;
[DataMember]
public List<Items> OrderItems
{
get { return orderItems; }
set { orderItems= value; }
}
}
public class Items
{
private string itemName;
[DataMember]
public string ItemName
{
get { return itemName; }
set { itemName = value; }
}
}
When I reference in my code I have a method that takes in an "Order" list as the parameter.
ACME.Order newOrder = new ACME.Order();
ACME.Items newItems = new ACME.Items();
newOrder.ID = xxx
newItems.ItemName = xxx
SendOrderWithItemsFunction(newOrder)
The above works fine however I don't have an add function for my items so that I can do something like the following
newOrder.Items.Add(newItem);
and
newOrder.Items = newItems
will not work because it says that it can not implicitly convert newOrder.Items to newItems[].
What am Missing?
I think I might be missing something, but newOrder.OrderItems.Add(newItem) should work just fine, according to waht you have in your post.
Just some other nitpick things:
The pluralization of the "Items" class is wierd, if it is only a single Item. This is probably the reason that it looked "ok" to assign a single item to a List property.
You may have cut it out of your post, but every class that is being serialized by WCF must be marked as a "DataContract", not just the members of the class.
When initializing objects like this, I think it makes it a lot cleaer to use Type Initializers:
var NewOrder = new ACME.Order{
ID = xxx,
OrderItems = new List<ACME.Item>
{
new ACME.Item{
ItemName = xxx
}
}
};
What you do have is an add function in your Order.OrderItems property:
newOrder.OrderItems.Add(newItem);
you can even add a whole list of items to your OrderItems:
var someList = new List<Items>();
//populate someList here
newOrder.OrderItems.AddRange(someList);
You should be able to do:
newOrder.OrderItems.Add(newItem);
If your newItems[] is an array, you need to do this:
newOrder.OrderItems.AddRange(newItem.ToList<Items>());
You have declared newItems as an ACME.Items type, but the OrderItems property of your Order class is a List<Items>. Those types are not assignable from one to the other directly. So, an assignment of newOrder.OrderItems = newItems is like trying to sayList<Items> = Items. That isn't possible based on the classes you outlined above. Instead, you will need to add to the list.
When you have a list within a list, and the Add() method is missing, a workaround is to make a new list, add the items, then set the inner list to the new list. Instead of:
outerList.innerList.Add(item)
..use..
var newList = new List<ItemType>();
newList.Add(item);
outerList.innerList = newList;

Categories

Resources