I've these two classes
public class A {}
public class B : A {}
And the cast from class A to class B works fine.
B testB1 = new B();
A testA1 = testB1;
B testB2 = (B)testA1; //this works
But: Why is this cast not working?
List<B> testB1List = new List<B>();
List<A> testA1List = ((IEnumerable<A>)testB1List).ToList();
List<B> testB2List = ((IEnumerable<B>)testA1List).ToList(); //not working
The solution is:
List<B> testB1List = new List<B>();
List<A> testA1List = ((IEnumerable<A>)testB1List).ToList();
List<B> testB2List = testA1List.Cast<B>().ToList();
But why is it like this?
Well the Cast extension method casts each member of the list to the specified type so in your example it can then be assigned as a List<B> because all list members have been cast to B.
But in the first example, you are casting the IEnumerable itself, not the members of the list:
(IEnumerable<B>)testA1List
So it fails because it is trying to cast List<A> to IEnumerable<B>.
Related
I have such classes:
public class A
{
public int AProperty { get; set; }
public List<A> Children;
}
public class B:A
{
public string Name { get; set; }
}
I cannot do this:
A a = new A();
B b = (B)a;//SystemCastInvalidException
I can do this:
B bCanDo= new B();
bCanDo.Children.Add(new B());
foreach (var c in bCanDo.Children)
{
B notExpected = (B)c;//OKAY. Why?
}
What I miss out? Why I can downcast in foreach? yeah, it is all logically correct, but where I can read info about it?
This is a runtime error, not a compiler error, so let's look at what you're doing here:
In the first example you're constructing an object of type A, then try to cast it to type B. This is illegal since A does not inherit from B. It is not illegal because the compiler thinks this, it compiles the code, then it crashes at runtime because this is definitely an invalid cast.
In the second example, however, you're constructing an object of type B and then adding it to a list that can hold objects of type A. Since B inherits from A this is legal. You then pick the first object back out and cast it to B. This is also legal since the underlying object is actually of type B.
Basically, here's your two examples with more minimal steps:
A a = new A();
B b = (B)a; // fails with InvalidCastException
A a = new B();
B b = (B)a; // works OK
So this has nothing to do with foreach, it has everything to do with your two examples doing different things. To see the same code fail with foreach, try this:
B bCanDo= new B();
bCanDo.Children.Add(new A()); // <-- notice new A() here
foreach (var c in bCanDo.Children)
{
B notExpected = (B)c; // crash
}
So while you can downcast an object reference to a more derived type this will only work if the actual object being reference is that derived type (or an even more derived type down that path). Basically this is a reference reinterpretation, you're just putting on new glasses while looking at the same object.
You cannot downcast an object instance to a more derived type if it isn't of that derived type, however, this would be a conversion and requires explicit support or code to be written to do this.
In the cannot-case, a contains an object of Type A which cannot be cast to B since it A is not of type B.
In the can-case the an object of type B is added to the children, since B is a sub-type of A you can do this but it remains an object of type B. When you loop the children and cast it to B, you are only able to do so because it already is a B. Would you add b.Children.Add(new A()); it would fail again.
What you need to understand is that casting doesn't change the underlying object in any way.
So if I have these classes:
public class Animal { }
public class Dog : Animal { }
...and I write this code:
Dog d = new Dog();
Animal a = (Animal)d;
The a variable is still a Dog, it's just being acted upon as if it were just an Animal.
If I defined this class:
public class Cat : Animal { }
...and I tried to then write this code:
Dog d = new Dog();
Cat c = (Cat)d;
...I get an error, but not because I can't change a Dog to a Cat, instead it is because the object d is always a Dog and I can't treat it as if it were a Cat. A Dog can never be a Cat.
So in your code when you write:
A a = new A();
B b = (B)a;//SystemCastInvalidException
...the same applies - a A can never be a B.
But in your code a B can be an A.
So if I re-write your code slightly as this:
B bCanDo = new B();
bCanDo.Children.Add(new B());
foreach (A a in bCanDo.Children)
{
B notExpected = (B)a;
}
...you can see that even though the Children of bCanDo are type A you can add children of type B - a B can be an A. So when you iterate through the Children the types of the children never change so, even though the members of Children are A, if a B was added you can always cast it back to B. This is why the foreach cast works.
I have 2 classes B and D. I have to create an array with 4 elements 2 of type B and 2 of type D. How do I do it?
B o1 = new B();
D o2 = new D();
B o3 = new B();
D o4 = new D();
The array should be something like this:
array[0] = o1; array[1] = o2; array[2] = o3; array[3] = o4;
If there is no common base class other than object, you just need:
object[] array = new object[4];
array[0] = o1;
// etc
Or in a single shot:
object[] array = { o1, o2, o3, o4 };
To use the members specific to B or D, you'd need to cast when you retrieved the values from the array, e.g.
B b = (B) array[0];
b.SomeMethodDeclaredOnB();
If B and D have common methods, you could declare those in an interface which both classes implemented, and change the type of the array:
IBD[] array = new IBD[4];
array[0] = o1;
...
array[0].SomeMethodDeclaredInIBD();
Or:
IBD[] array = { o1, o2, o3, o4 };
...
array[0].SomeMethodDeclaredInIBD();
Finally, to address this:
I am new to c#. I don't like it but I have to learn it. In PHP it's 10000 times easier;
I'm sure if I tried to use PHP I'd find the same experience in the exact opposite direction. Don't assume that C# is "worse" or "harder" than PHP - it's just different, and you're bound to find it harder to use a language you're not familiar with than your "comfort zone" language.
or use array object[] array of objects
or more OOP approch:
public class B : IHolder {
}
public class D : IHolder {
}
IHolder[] arrays of IHolders
I am new to c#. I don't like it but I have to learn it. In PHP it's 10000 times easier;
You left out a part of the last sentence: "to make mistakes". Strong-typedness has its strengths.
You can let the classes B and D inherit from a common base class or interface like such:
interface ISomeInterface
{
string CommonProperty { get; }
}
class B : ISomeInterface
{
public string CommonProperty { get; }
}
class D : ISomeInterface
{
public string CommonProperty { get; }
}
ISomeInterface[] array = new ISomeInterface[]
{
o1,
o2,
o3,
o4
}
I'm targeting version 4.0 of the .Net framework. I wrote the following simple code:
public class A
{
public A()
{
}
}
public class B
{
public B()
{
}
public static implicit operator A(B b)
{
return new A();
}
}
Then I created a generic list:
var mylist = typeof(List<>).MakeGenericType(typeof(A)).GetConstructor(Type.EmptyTypes).Invoke(new object[]{});
When I want add a new instance of B the following code works:
((IList<A>)mylist).Add(new B());
However if I run the below code the following exception is thrown:
The value "B" is not of type "A" and cannot be used in this generic collection.
Parameter name: value
((IList)mylist).Add(new B());
The implicit conversion happens only in the first Add as you are adding an object of type B to a IList<A>. In the second case, you are adding it to the non-generic IList, which is a collection of objects and the implicit conversion doesn't take place and bombs when the object is actually added to your List<A>
Even this fails:
var mylist = new List<A>();
((IList<A>)mylist).Add(new B());
((IList)mylist).Add(new B())
You can do this, however:
((IList)mylist).Add((A)new B());
The problem is that when you cast your list to IList<A> and call Add, the compiler (implicitly) adds a cast to your B instance to A. In the second case, IList accepts only an object in the Add method. B is an object, so it's not cast to A. Therefore, when the method tries to add it internally to the underlying data structure, it raises the exception.
You can even test it through this code:
var fails = (A) (object) new B();
Since you ask how to solve the problem (although I don't see the necessity of casting your type to IList in this case), you can just explicitly cast your B to A when adding it. Example:
mylist.Add((A) new B());
I deserialized json string to List<ClassB> and now I want to cast it to List<ClassA> before I return it from
BindModel method. I need casting because the methods expects to get List<ClassA>.
Why I get error while casting? After all, ClassB inherits from ClassA. What should I do?
P.S. this question is extended from this post. In line new DataContractJsonSerializer(typeof(List<ClassB>)); instead of List<ClassB> the type will be constructed at runtime.
public override object BindModel(...)
{
var serializer = new DataContractJsonSerializer(typeof(List<ClassB>));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes("[{\"id\":\"1\",\"name\":\"name\"}]"));
var list = serializer.ReadObject(ms);
return (List<ClassA>)list;
}
[KnownType(typeof(ClassA))]
[DataContract]
public class ClassA
{
public ClassA();
}
[KnownType(typeof(ClassB))]
[DataContract]
public class ClassB : ClassA
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "name")]
public string CategoryName { get; set; }
}
You can use
Cast<T>()
For example:
List<A> listOfA = new List<B>().Cast<A>();
This in fact is inferior to Linq and is implemented on IEnumerable rather than IEnumerable<T> but still is useful. It is not efficient since as I said, it tries to cast it.
Remember List does not allow for covariance which is a nuisance. It is preferable to use IEnumerable<T> as interface rather than List.
You can say:
IEnumerable<B> listOfB = new List<B>();
IEnumerable<A> listOfA = listOfB; // no casting required
You can use the Cast Method.
return list.Cast<ClassB>();
Have a look at this question about Co and Contra Variance
I would cast the lists like this:
var listB = GetListOfB(); // returns List<B>
var listA = listB.Select(q => (A)q).ToList();
Will that work for you?
You can't cast a List of SubType to a List of SuperType. Suppose I have a list of Tortoises, and I was able to cast it to a list of Animals. Then I could add a lion, to a list of Tortoises, but a Lion isn't of the correct type.
With enumerables, you can do this, however. The previous posters are quite correct in saying that you can cast a List of SubType to an IEnumerable of SuperType. In fact, in C# 4, and IEnumerable SubType is an IEnumerable of SuperType. This is because the generic parameter is specified as an out parameter.
To create a List<BaseClass> from a List<DerivedClass>, a cast will not suffice, as other answers have noted. You need to construct a new list.
I'm not particularly happy with the code that other answers have suggested, though, so I'm offering my own solution. I would do it this way:
var baseClassList = new List<BaseClass>(derivedClassList.Cast<BaseClass>());
Some might prefer this:
var baseClassList = derivedClassList.Cast<BaseClass>().ToList();
I prefer the first one because it makes it easy to change the type from List<T> to any other collection with a constructor that takes an IEnumerable<T>.
I have an interface A, class B inherits from interface A.
I have a list of objects:
List<B> myB;
List<A> myA;
I want to assign myB to myA but I get a error "Cannot implicit convert type 'B' to 'A':
myA = myB;
Please help me. Thanks.
You need to convert each element of the list. It cannot be automatically converted for you. Easiest would be Linq:
myA = myB.Cast<A>().ToList();
Update: This question: Why is this cast not possible? discusses it in more detail.
It might help you: Cast List<int> to List<string> in .NET 2.0
IList<T> is not covariant, where as IEnumerable<T> is, you can do the following..
void Main()
{
IEnumerable<B> myB= new List<B>();
IEnumerable<A> myA = myB;
}
public interface A
{
}
public class B :A
{
}
see this previous SO Question
You need to make a way to convert between type A and type B.
There is no way to assign a list of one type to another, unless the type B is the same as type A.
You can use the Cast<T> operator for derived types:
class A {}
class AA : A {}
List<AA> aas = new List<AA> {new AA()};
List<A> bunchofA = aas.Cast<A>().ToList();
This only works when casting to less derived types (from descendant to ancestor). This won't work:
List<A> bunchofA = new List<A> {new A()};
List<AA> aas = bunchofA.Cast<AA>.ToList();
Because the compiler cannot know what to do to make the extra bits that AA has from A.
You can also, in a rather contrived way, use implicit conversion:
class A
{
}
class B
{
public static implicit operator B(A a)
{
return new B();
}
public static implicit operator A(B a)
{
return new A();
}
}
List<B> bs = new List<B>{new B()};
List<A> bunchOfA = bs.Select(b => (A)b).ToList();
This will work in either direction, but might cause confusion, so it is better to create explicit conversion methods and use those.
That is correct. List is a list of Apples and List is a list of .. err .. batmans! You cannot try to put one into the other.
Technically, you cannot refer to one as the other!