I don't quite get casting in C# - c#

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.

Related

Casting of generic type does not compile

Please have a look at the following code. Why do I get a compile-error?
I don't get it!
Casting is a way of telling the compiler that I know more about the objects than it does. And in this case, I know for fact, that "x" does actually contain an instance of "SomeClass". But the compiler seems to be unwilling to accept that information.
https://dotnetfiddle.net/0DlmXf
public class StrangeConversion
{
public class SomeClass { }
public interface ISomeInterface { }
public class Implementation : SomeClass, ISomeInterface { }
public void Foo<T>() where T : class
{
T x = (T)Factory();
//Compile-error: Cannot convert type 'T' to 'SomeClass'
SomeClass a = (SomeClass)x;
//This is perfectly fine:
SomeClass b = (SomeClass)(object)x;
if (x is SomeClass c)
{
//This works as well and 'c' contains the reference.
}
}
private object Factory()
{
return new Implementation();
}
}
Edit:
#Charles Mager has the correct answer in the comment: There does not seem to be a valid reason. The language designers just didn't want to allow this cast.
I fixed using the as casting e.g.
SomeClass a = x as SomeClass;
This Answer explains is very well https://stackoverflow.com/a/132467/16690008
Essentially it's because it would throw an exception if T is not type of that class
It's hard to make sense of exactly what you're trying to achieve, but it seems like a generic constraint is what you're after
public void Foo<T>()
where T : SomeClass // constrain T to be inheriting from SomeClass
{
T x = Factory<T>(); // x is guaranted to be SomeClass
}
private T Factory<T>()
where T : SomeClass // same here
{
return new Implementation();
}
You constrain the generic to only reference types by specifying where T : class, but the compiler needs to know with certainty if the cast is possible. This means you are asking the compiler to trust that SomeClass can be cast from any reference type you pass to it, which is something it won't do. The microsoft docs state that for the class generic type constraint:
The type argument must be a reference type. This constraint applies also to any class, interface, delegate, or array type.
Its important to note that SomeClass b = (SomeClass)(object)x; works because of the cast to object which is the root of the object hierarchy. But as you can see from the list of supported reference types, SomeClass a = (SomeClass)x; has to account for things such as delegates, array types, etc., at which point the compiler will throw you the error
Don't do SomeClass b = (SomeClass)(object)x;, it is much cleaner to make proper use of type constraints along with the as & is operators which were designed for this exact purpose of type checking and safe casting
Short answer:
This behaviour is correct according to the spec. The spec is just bad here since this might convert a compile-error into a runtime-error.
Long answer:
I did some more research on the matter. This is an oversight in the language's spec.
C# uses the same syntax for two totally different things:
int i = (int)1.9
This converts the double 1.9 to an integer. The value is actually changed.
object o = "abc";
string s = (string) o;
This looks the same, but does not change the object referenced by "o" at all. It does only convert the type of the reference.
When it comes to generics, this kind of ambiguity is an issue:
function f(T x) {
var x = (string) x;
}
What should the language do if T is "int"?
That's why the spec forces the developer to cast to object first:
function f(T x) {
var x = (string)(object)x;
}
Now, the behaviour is clear: X might still be a value-type. But if it is, it will be converted to a reference-type first.
This ambiguity does not exist in my example, since T is guaranteed to be a reference type:
public void Foo<T>() where T : class
Thus the cast to object is not necessary. It could even be harmful if the "where" specifies an actual type. In that case, the forced cast to object might convert a compile-time-error (impossible cast) to a runtime-error.
Unfortunately, the people who created the spec, did not see this issue and did not include it.

Cannot cast to base generic type in C# but can use 'as' operator

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.

C# type cast from my type to my other type

My code gets an object with everything in it. And as per different needs, it needs to be cast to different individual type.
class A
{
public static explicit operator B (A value)
{
....
return new B();
}
}
class B
{
...
}
public static T Get<T>(...)
{
A a = new A();
return (T)A;
}
var b = Get<B>(...); // cannot convert a type of A to B
Any ideas?
You can't do this, as the compiler doesn't know (and there's no way to tell it) that there's a conversion from A to T. You'd need to constrain T in some way to indicate that, and you just can't do that - in the same way that you can't constrain T to have a + operator etc. (The C# team has been considering how what that might look like and how it might be implemented, but it's definitely not available at the moment.)
You need a way of telling the compiler how to convert from an A to a T - you could add a parameter to Get to tell it that:
public static T Get<T>(Func<A, T> converter)
{
A a = new A();
return converter(a);
}
You'd then call it with:
var b = Get<B>(a => (B) a);
for example.
C# doesn't support duck typing, but you could serialize your class A into a json string, then deserialize it into class B, any any properties that have the exact same names and types (or via a mapping you set up using JSON attributes or JSON settings) will make their way into class B as desired.

Type casting error and constructor

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.

Is there a workaround for overloading the assignment operator in C#?

Unlike C++, in C# you can't overload the assignment operator.
I'm doing a custom Number class for arithmetic operations with very large numbers and I want it to have the look-and-feel of the built-in numerical types like int, decimal, etc. I've overloaded the arithmetic operators, but the assignment remains...
Here's an example:
Number a = new Number(55);
Number b = a; //I want to copy the value, not the reference
Is there a workaround for that issue?
you can use the 'implicit' keyword to create an overload for the assignment:
Suppose you have a type like Foo, that you feel is implicitly convertable from a string.
You would write the following static method in your Foo class:
public static implicit operator Foo(string normalString)
{
//write your code here to go from string to Foo and return the new Foo.
}
Having done that, you can then use the following in your code:
Foo x = "whatever";
It's still not at all clear to me that you really need this. Either:
Your Number type should be a struct (which is probable - numbers are the most common example of structs). Note that all the types you want your type to act like (int, decimal etc) are structs.
or:
Your Number type should be immutable, making every mutation operation return a new instance, in which case you don't need the data to be copied on assignment anyway. (In fact, your type should be immutable whether or not it's a struct. Mutable structs are evil, and a number certainly shouldn't be a mutable reference type.)
You won't be able to work around it having the C++ look, since a = b; has other semantics in C++ than in C#. In C#, a = b; makes a point to the same object like b. In C++, a = b changes the content of a. Both has their ups and downs. It's like you do
MyType * a = new MyType();
MyType * b = new MyType();
a = b; /* only exchange pointers. will not change any content */
In C++ (it will lose the reference to the first object, and create a memory leak. But let's ignore that here). You cannot overload the assign operator in C++ for that either.
The workaround is easy:
MyType a = new MyType();
MyType b = new MyType();
// instead of a = b
a.Assign(b);
Disclaimer: I'm not a C# developer
You could create a write-only-property like this. then do a.Self = b; above.
public MyType Self {
set {
/* copy content of value to this */
this.Assign(value);
}
}
Now, this is not good. Since it violates the principle-of-least-surprise (POLS). One wouldn't expect a to change if one does a.Self = b;
Instead of making a copy of the data when passing the reference you could make the class immutable. When the class is immutable having multiple references to it isn't a problem since it can't be changed.
Operations that changes the data would of course return new instances.
An earlier post suggested this:
public static implicit operator Foo(string normalString) { }
I tried this approach... but to make it work you need this:
public static implicit operator Foo(Foo original) { }
and the compiler won't let you have an implicit conversion function from your exact type, nor from any base type of yourself. That makes sense since it would be a backdoor way of overriding the assignment operator, which C# doesn't want to allow.
Here is a solution that worked for myself :
public class MyTestClass
{
private int a;
private string str;
public MyTestClass()
{
a = 0;
str = null;
}
public MyTestClass(int a, string str)
{
this.a = a;
this.str = str;
}
public MyTestClass Clone
{
get
{
return new MyTestClass(this.a, this.str);
}
}
}
Somewhere else in the code :
MyTestClass test1 = new MyTestClass(5, "Cat");
MyTestClass test2 = test1.Clone;
Maybe what you're looking for can be solved using C# accessors.
http://msdn.microsoft.com/en-us/library/aa287786(v=vs.71).aspx

Categories