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());
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.
Last night I learned about this wonderful operation of casting by example: a very cool way to generate a collection of some Type using a reference to an existing instance.
My problem is that although this works when you explicitly create the instance, the Type of collection produced is inaccurate if you use activator to instantiate from a Type.
class TestCollectionContent
{
public int id { get; private set; }
}
[Test]
public void TestListCastCreation()
{
var explicitCast = new TestCollectionContent (); //This casts as TestCollectionContent
var explicitList = MakeList (explicitCast); //This casts as List<CommandWithExecute>
explicitList.Add (new TestCollectionContent ());
Type clazz = typeof(TestCollectionContent);
var implicitCast = Activator.CreateInstance (clazz);//This casts as TestCollectionContent
var implicitList = MakeList (implicitCast); //This casts as List<object>
implicitList.Add (new TestCollectionContent ());
Assert.AreEqual (explicitCast.GetType (), implicitCast.GetType ()); //Succeeds
Assert.AreEqual (explicitList.GetType (), implicitList.GetType ()); //FAILS!
}
public static List<T> MakeList<T>(T itemOftype)
{
List<T> newList = new List<T>();
return newList;
}
For my purpose it is imperative that the collection be correctly cast. Any thoughts?
Note that I'm using C# with Unity3D (which uses something akin to .Net 3.5).
Activator.CreateInstance always returns an object, so you will not get any static type information from it when using it. This will make the variable implicitCast of type object although its value is of a more specialized type.
Now when using generics, only the type that is available for static typing is taken into account. So when passing implicitCast to MakeList, all that method sees is an object. As such, the method will be called as MakeList<object> and will return a List<object>, which is of course not of the same type as explicitList.
Unfortunately (or fortunately?) you cannot really do this any better. Generics are supposed to be something for use in a static typing environment, and if you start to create types dynamically, you will lose this ability.
You could however use Activator.CreateInstance for the list creation just as well by doing something like this:
public static IList MakeList(object itemOftype)
{
Type listType = typeof(List<>).MakeGenericType(itemOfType.GetType());
return (IList) Activator.CreateInstance(listType);
}
Of course, this will also just return an object, so you will have to cast it to a more specialized type, or use the non-generic IList interface to have at least some access to it.
This code behaves this way because T is inferred at compile time, not run time. Since implicitCast is of type object, it compiles with MakeList<object>.
var implicitList = MakeList (implicitCast); // equivalent to
List<object> implicitList = MakeList<object>(implicitCast);
var explicitList = MakeList (explicitCast); // equivalent to
List<TestCollectionContent> explicitList =
MakeList<TestCollectionContent>(explicitCast);
If you want it to use the runtime type, you can use reflection or dynamic.
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>());
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!
I have two classes
public class A
{
public A()
{
}
}
public class B:A
{
public B()
{
}
}
and it the code in Main is as follows
A oa = new B();
B ob = new A();
Here line 1 compiles successfully while line 2 displays typecasting error. Why this happens. What exactly happens when new B() and new A() gets called?
You have declared a variable of type B, and then attempted to assign a value of type A to it. You have defined B to be a kind of A, but that doesn't mean that all A's are B's.
Think of it like this:
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
You can do Animal rex = new Dog(), because all dogs are animals, but not Dog fido = new Animal(), because not all animals are dogs.
What exactly happens when new B() and
new A() gets called?
new A() constructs an object of type A on the heap and returns a reference to it.
new B() constructs an object of type B on the heap and returns a reference to it.
Here line 1 compiles successfully
while line 2 displays typecasting
error. Why this happens.
Since B subclasses A, it is valid for a reference of type A to refer to an object of run-time type B. After all, B is simply a "special case" of A.
However, the converse is not true, because not all As can be considered Bs.
Although this is strictly enforced by C#'s safe type-system even if there is no "real" incompatibility, the reasons for such restrictions are natural. Imagine, for example, that B declared a property public int Foo {get; set;}.
How would you expect this to behave:
B ob = new A();
ob.Foo = 5;
This is clearly illogical: the real object that the reference is referring to has no such property. Consequently, the compiler prohibits such constructs.
Now imagine you changed your code to:
B b = (B)new A();
Here, you are telling the compiler that the object created, will, at run-time, be assignable to a reference of type B. This will compile fine, but since the assertion is clearly incorrect, a run-time InvalidCastException will be thrown.
To summarize, C#'s type system (if you ignore dynamic and a few special cases) is both static and safe: you will not successfully be able to treat a concrete instance of A as though it were of type B.