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.
Related
why casts don't work on constrained generic type as shown below?
class B { }
class B1 : B { }
class G<T> where T : B
{
void x()
{
T b1 = new B1(); // why implicit conversion doesn't compile?
T b2 = (T)new B1(); // why explicit conversion doesn't compile either?
T b3 = new B1() as T; // this works!
}
}
B1 is not constrained to be assignable to T. For example:
void Main()
{
new G<C1>().x();
}
class B { }
class B1 : B { }
class C1 : B { }
class G<T> where T : B
{
public void x()
{
T b3 = new B1() as T;
b3.Dump(); // null, because B1 cannot be converted to C1
}
}
Just because T is constrained to be a B doesn't mean you can cast any descendant of B to any possible T.
Why not allow that anyway? In my thinking, it doesn't make any sense. If you wanted to make sure B1 is assignable to T, you shouldn't be using generics. It's way too easy to make this kind of mistake, and if possible, you should avoid casting around generics in the first place. They're made (primarily) to make static typing more powerful, while keeping the type safety (and performance benefits).
However, there's definitely obviously wrong cases that don't get caught, because
T b2 = (T)new B();
does compile, even though it actually has the same problem and you will get a runtime cast error if T isn't B.
Of course, in cases like this, it's helpful to check the C# specification, and clearly enough, that says:
The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. The reason for this rule is to prevent confusion and make the semantics of such conversions clear.
While this only really seems to make sense for value-types, this explains both why you can do a direct cast of (T)new B();, and why you cannot do (T)new B1(); - even though both have the same problem with T not necessarily being B.
Remember, operators in C# are not virtual - they depend on the compile-time type of the expressions. For value type arguments, you actually get a variant for each value type you use (i.e. List<long> uses different code than List<int>) - so you get the correct cast, like when casting from int to long, you get a long with the same value as the int, rather than a casting error.
For reference types, this isn't true. In your case, you could have a custom cast operator from B to B that would actually be invoked in the (T) new B() case, but not a cast from B1 to B, because the reified generic type for G<B> and G<B1> is actually the same. Since this is an implementation detail that can change at any time, you really want to avoid the confusion and potential change of behaviour.
The answer is, because there are no guarantees you supplied B1 as the generic parameter, you can't do this with generics, let's explore the reason why:
Given
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
class Something<T> where T : Animal
{
public T Animal {get;set;}
void x()
{
Animal = (T)new Cat();
}
}
Now, what if you used your class like this:
var dog = new Something<Dog>();
dog.X() // internally you are trying to cast a Cat to a Dog
Dog dog = dog.Animal; // now you just tried to mash a Cat into a dog
Dog.Bark() // what the...
Constraints are a minimum contract, that's it. They allow you to use the generic parameter on that contract you specified.
It's exactly the same as why you can't do the following:
Dog dog = new Cat();
Even if they both inherit from Animal, does not mean they are the same. They have a different memory layout internally, they have different methods and properties, they can't be mashed together statically typed like this.
In short, you likely need to rethink your problem.
T b1 = new B1(); // why implicit conversion doesn't compile?
Just because both T and B1 are derived from B doesn't mean B1 is assignable to T. They both are assignable to B, so
B b1 = new B1(); // should work
.
T b2 = (T)new B1(); // why explicit conversion doesn't compile either?
As explained above, two classes with a common base class do not ensure type compatibility so explicit casting won't work either
T b3 = new B1() as T; // this works!
it works but you probably assign null to b3 since B1 is not convertible to T. The as operator just returns null instead of emitting a compiler warning or throwing an exception.
Class A
{
string name = "a";
public virtual void Rename(){//rename name to aaa}
}
Class B:A
{
string name = "b";
public override void Rename(){//rename name to bbb}
}
void Main()
{
B objB = new B();
A objA = objB as A;
print(objB.name);// b
print(objA.name);// a
objA.Rename();
print(objB.name);// bbb
print(objA.name);// a
}
Why when I use objA.Rename(), it actually uses the override version written in class B. What is objA in the end. If it is A, then why it uses the override version. If it is B then why objA.name is a not bbb;
The object never changes. In memory, it is always a B, no matter how you choose to interact with it. The way to know this is the case is to just call .GetType() on your variables and they will all tell you each one is an instance of B.
B objB = new B();
A objA = objB as A;
Console.WriteLine(objB.GetType()) // B
Console.WriteLine(objA.GetType()) // B
The reason you get "a" at the end, is because you have two different declarations of name. meaning: A.name is a totally different address than B.name
To fix this, you could just remove string name = "b"; from B and instead put name = "b"; into it's constructor.
Class A
{
string name = "a";
public virtual void Rename(){//rename name to aaa}
}
Class B:A
{
public override void Rename(){//rename name to bbb}
public B() { name = "b"; }
}
Another way to think about this is that if you call base.name inside of B i am sure you will get the same result of "a", showing you that B actually has two separate declarations of name.
First of all, you must understand that casting in c# is a name that is used for very different conversions and can create confusion.
The casting you are asking about is technically named a reference conversion. Reference conversions are identity preserving conversions, this means that the object does not change, what changes is the type of the reference:
var s = “Hello”;
var o = (object)s;
var b = ReferenceEquals(s, o); //returns true
Here, s and o refer to the exact same object, the only thing that changes is the type of the reference.
It’s important to note that reference conversions are automatically provided by the compiler, you can’t define a user defined identity preserving conversion, you will get a compiler error.
What are the most common reference conversions you will encounter? Any conversion between types and subtypes or types and implemented interfaces, etc.
Now consider the following seemingly similar code:
var l = 1L;
var i = (int)l;
This code is radically different than the previous one. Why? Because the cast here is not a reference conversion. This actually calls a user defined explicit cast operator implemented in System.Int32. i and l are different objects.
Another important conversions in c# are boxing and unboxing conversions. These are not identity preserving but they do share common ”feature” as far as not being able to implement user defined boxing or unboxing conversions. Boxing refers to converting a value type into a assignment compatible reference type and unboxing is the opposite operation:
var i = 1;
var o = (object)i; //boxing
var ii = (int)o; //unboxing
How do you know at first glance if it’s a reference conversion or not? Well, as long as you know the hierarchy of the involved types. interfaces, etc. you should be able to reason it out.
I'm trying to make this:
class A {
//attibutes
}
class B : A {
public int classBAttribute = 5;
// other attributes and methods
}
My question is this if I have an instance of A class how can i get the instance of the B class or access his attributes?
B b = new B();
Application.Add(b);
//other form
A a = Application.GetA();
B b = getBFromA(a);// ??? Note: B b = a as B; does't work i tried
You cannot do this -- there is no magical way to create derived objects from base objects in general.
To enable such a scheme class B would need to define a constructor that accepts an A argument:
public B(A a)
{
// do whatever makes sense to create a B from an A
}
which you can then use as
var b = new B(a);
Of course after this a and b will be completely different objects; changing one will not affect the other.
You should also get the terminology right in order to avoid confusion: classBAttribute is not an attribute, it is a field.
My question is this if I have an instance of A class how can i get the
instance of the B class or access his attributes?
How would your program know an instance of A is actually of type B?
An instance of B could be used as A (as B is a specialization of A), but the opposite is not possible.
Maybe I don't fully understand the question or the answers, but...
Casting an A to a B should work (as long as the A is actually (also) a B).
A a = Application.GetA();
if(a is B)
{
B b = (B)a;
DoSomething(b.classBAttribute);
}
else
{
// TODO: Some fallback strategy or exception (?)
}
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 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.