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!
Related
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>.
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.
Why does the C# compiler not allow polymorphic type (T) parameters in generic collections (ie, List[T]) ?
Take class 'A' and 'B' for example, where 'B' is a subclass of 'A'
class A { }
class B : A { }
and consider a function that takes a list of type 'A'
void f(List<A> aL) { }
that gets called with a list of type 'B'
List<B> bL = new List<B>();
f(bL);
The following error is given
ERROR: cannot convert from List<B> to List<A>
What semantic rule is being violated ?
Also is there an "elegant" mean to this end, aside from looping through and casting each element (I want some sugar please) ? Thanks.
List<B> simply is not a subtype of List<A>. (I'm never sure about what "covariant" and what "contravariant" is in this context so I'll stick with "subtype".) Consider the case where you do this:
void Fun(List<A> aa) {
aa(new A());
}
var bb = new List<B>();
Fun(bb); // whoopsie
If what you want to do was allowed it would be possible to add an A to a list of Bs which is clearly not type-safe.
Now, clearly it's possible to read elements from the list safely, which is why C# lets you create covariant (i.e. "read-only") interfaces - which let the compiler know it's not possible to cause this sort of corruption through them. If you only need read access, for collections, the usual one is IEnumerable<T>, so in your case you might just make the method:
void Fun(IEnumerable<A> aa) { ... }
and use the Enumerable methods - most should be optimised if the underlying type is List.
Unfortunately, because of how the C# generics stuff works, classes can't be variant at all, only interfaces. And as far as I know, all the collection interfaces "richer" than IEnumerable<T> are "read-write". You could technically make your own covariant wrapper interface that only exposes the read operations you want.
Take this little example as to why this cannot work. Imagine we have another subtype C of A:
class A {}
class B : A {}
class C : A {}
Then obviously, I can put a C object in a List<A> list. But now imagine the following function taking an A-list:
public void DoSomething (List<A> list)
{
list.Add(new C());
}
If you pass a List<A> it works as expected because C is a valid type to put in a List<A>, but if you pass a List<B>, then you cannot put a C into that list.
For the general problem that’s happening here, see covariance and contravariance for arrays.
There's nothing inherently wrong with passing a collection of B to a method that expects a collection of A. However, there are many things that can go wrong depending on what you are going to do with the collection.
Consider:
void f(List<A> aL)
{
aL.(new A()); // oops! what happens here?
}
Obviously there is a problem here: if aL were allowed to be a List<B> then this implementation would result in some type of runtime error, either on the spot or (much worse) if later on the code handles the A instance we put in as a B.
The compiler does not allow you to use a List<B> as a List<B> in order to preserve type safety and guarantee that your code will not need runtime checks to be correct. Note that this behavior is different than what (unfortunately) happens with arrays -- the language designer's decision is a tradeoff, and they decided differently on different occasions:
void f(A[] arr)
{
arr[0] = new A(); // exception thrown at runtime
}
f(new B[1]);
I think you may be looking for 'out' generic modifiers, which allow for covariance between two generic types.
http://msdn.microsoft.com/en-us/library/dd469487.aspx
An example as posted on that page:
// Covariant delegate.
public delegate R DCovariant<out R>();
// Methods that match the delegate signature.
public static Control SampleControl()
{ return new Control(); }
public static Button SampleButton()
{ return new Button(); }
public void Test()
{
// Instantiate the delegates with the methods.
DCovariant<Control> dControl = SampleControl;
DCovariant<Button> dButton = SampleButton;
// You can assign dButton to dControl
// because the DCovariant delegate is covariant.
dControl = dButton;
// Invoke the delegate.
dControl();
}
I'm not sure whether C# currently supports covariance for its current collections.
You error is that B inherits from A; but List<B> don't inherits from List<A>. List<A> != A;
You can do this:
List<A> aL = new List<A>();
aL.Add(new B());
f (aL)
You can detect the type in void f(List<A> list)
foreach(A a in list)
{
if (a is B)
//Do B stuff
else
//Do A stuff
}
your question is very similar to mine:
the answer is that you can't do that cast because thoose are diferent types create by a template class and they do not inherit. what you can do is:
f(bL.Cast<A>());
Why does list.Add(new B()) compile and list.Add(new Wrapper<B>()) not compile? I thought that either both or neither would compile because I thought the compiler would be able to figure out that the implicit cast of B returns a Wrapper<B> which is that same type produced from new Wrapper<B>(). I'm using C# 4 in VS 2012.
class Wrapper<T> where T : new()
{
public static implicit operator Wrapper<T>(T obj)
{
return new Wrapper<T>();
}
public static implicit operator T(Wrapper<T> obj)
{
return new T();
}
}
class A { }
class B : A { }
class MyClass
{
public static void Main(string[] args)
{
List<Wrapper<A>> list = new List<Wrapper<A>>();
//This line compiles and runs successfully
list.Add(new B());
//This line doesn't compile
list.Add(new Wrapper<B>());
}
}
It seems from your question that you think that adding an instance of B to a list of Wrapper<A> works because B is implicitly cast to a Wrapper<B>, which is somehow added to the list. However, this isn't what happening. In fact, the compiler cannot cast from Wrapper<B> to Wrapper<A> in one step.
The reason adding an instance of B to a list of Wrapper<A> works is because the compiler sees that B extends A and that there is a user-defined implicit cast from A to Wrapper<A>.
You might think that you can also add a Wrapper<B> to a list of Wrapper<A> because there is a user-defined implicit cast from Wrapper<B> to B and B extends A and there is a user-defined implicit cast from A to Wrapper<A>. However, you cannot chain together user-defined implicit casts in this way, according to the specs (section 6.4.4 detailed here). In fact, the minimal example need not even deal with generics. Consider this simple example of the problem:
class A
{
public static implicit operator B(A a) { return default(B); }
}
class B
{
public static implicit operator C(B a) { return default(C); }
}
class C
{
public static void Method(C c) { }
}
public static void Main()
{
C.Method(new A());
}
Of course, if you explicitly do the cast, then it works:
public static void Main()
{
C.Method((B)new A());
}
list.Add(new B()); is not adding a new Wrapper<B> to the list. It's adding a new Wrapper<A> to the list.
The compiler is able to determine that the type expected is Wrapper<A>, and that there's an implicit conversion from what's there new B() to a Wrapper<A> because the operand for a Wrapper<A> needs to accept an A object, and new B() is a type of A.
list.Add(new Wrapper<B>()); doesn't work because you're not allowed to add a Wrapper<B> to a List<Wrapper<A>> because List is not covariant.
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());