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 (?)
}
Related
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 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.
We have a lot of this kind of code
if(object is ConcreteType))
{
object = object as ConcreteType;
}
else if(object as ConcreteType2))
{
object = object as ConcreteType2;
}
Is there a better way to write this kind of code to avoid repetition?
Yes,
IMyInterface myInterface = (IMyInterface) object;
//...
myInterace.DoTheWork1WhichWasDifferentInTheSpecificObjects();
myInterace.DoTheWork2WhichWasDifferentInTheSpecificObjects();
//...
EDIT:
Ignore my answer. It is flawed. Use Andrej's answer below about interfaces. You could also use inherited classes, but you won't need my ugly line with the ?? operator.
I guess I haven't had enough coffee yet.
Original answer below
If they form an object hierarchy as so:
class A
{
}
class B : A
{
}
class C : B
{
}
You can do something like this:
A y = new C();
var x = y as C ?? y as B ?? y as A;
This will get the most specific class. If you need to take different action depending on type, then you should have a virtual method in A that is overridden in the other two.
If my assumptions on what you are doing is wrong, then you should re-think your architecture.
The background is that in NHibernate mapping, as the collection field is declared in base class and dynamically proxied at runtime.
As there is some proxy class which need refer to the collection field during my coding, however, as NHiberate reassign the field with its own collection instance, the proxy loses the reference.
Tried use ref to "track" the reference, but failed with weired outcome.
Can some experter help explain about the root cause with both C# ref keyword and NHiberate collection proxy if any.
Thanks.
class Program
{
static void Main(string[] args)
{
var v = new D();
}
}
class A
{
}
class B : A
{
}
class C
{
public A a = new A();
public Proxy p;
public C()
{
p = new Proxy(ref a);
p.Out();
}
}
class D : C
{
public D()
{
a = new B();
Console.WriteLine(a.GetType().Name);
p.Out();
}
}
class Proxy
{
public A a;
public Proxy(ref A a)
{
this.a = a;
}
public void Out()
{
Console.WriteLine(a.GetType().Name);
}
}
Expected output:
A
B
B
Actual output:
A
B
A
Tried use ref to "track" the reference, but failed with weired outcome.
There's nothing weird about this outcome - you just don't understand ref sufficiently. ref only affects the parameter it's declared on.
So on this line:
this.a = a;
you're just copying the current value of the a parameter into the a field. Any later changes to the value of the parameter within the constructor (not that there are any) will not be reflected in the field - and any changes to the variable which was used as the argument again won't be reflected in the field.
So changing the a field within the C class (it would really have helped if you'd used different names for the fields and parameters) doesn't change the value within the Proxy instance's a field at all.
Note that this has nothing to do with NHibernate, either - it's just the standard C# behaviour.
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.