I was wondering why some casts in C# are checked at compile-time whereas in other cases the responsibility is dumped on CLR. Like above both are incorrect but handled in a different way.
class Base { }
class Derived : Base { }
class Other { }
static void Main(string[] args)
{
Derived d = (Derived)new Base(); //Runtime InvalidCastException
Derived d = (Derived)new Other(); //Compile-time Cannot convert type...
}
While reading "C# in depth" I've found the information on this topic where autor says:
"If the compiler spots that it’s actually impossible for that cast to work, it’ll trigger a compilation error—and if it’s theoretically allowed but actually incorrect at execution time, the CLR will throw an exception."
Does 'theoretically' mean connected by inheritance hierarchy (some another affinity between objects ?) or it is compiler's internal business?
Upcasts can be checked at compile time - the type system guarantees that the cast succeeds.
Downcasts cannot (in general) be checked at compile time, so they are always checked at runtime.
Unrelated types cannot be cast to each other.
The compiler considers only the static types. The runtime checks the dynamic (runtime) type.
Looking at your examples:
Other x = new Other();
Derived d = (Derived)x;
The static type of x is Other. This is unrelated to Derived so the cast fails at compile time.
Base x = new Base();
Derived d = (Derived)x;
The static type of x is now Base. Something of type Base might have dynamic type Derived, so this is a downcast. In general the compiler can't know from the static type of x if it the runtime type is Base, Derived, of some other subclass of Base. So the decision of whether the cast is allowed is left to the runtime.
If your variable is of Base type, is can be theoretically constructed by Derived constructor, thus being a variable of Derived type actually. At compile time, compiler does not bother itself with trying to figure out whether in each particular case such downcast (representing a variable of Base type as an entity of Derived type) is possible.
Your sample is simple - you create a new class and cast it right away. But what if you get Base from somewhere else, e.g., some method call? Compiler just cannot "guess" what your method is going to return and therefore throw on not throw an error.
When you cast Other, compiler sees that there is no possibility that Other is actually Derived and throws an exception.
Related
Why does Console show type B not A, even though a2 was assigned to new B()? I cannot understand exactly what happens in A a2 = new B().
class A { }
class B : A { }
...
A a1 = new A();
A a2 = new B();
Console.WriteLine(a2.GetType());
A variable is just something that points to an object. You can refer an object through a variable of any type that it inherits from (or any interface it implements) but that doesn't change the type of the object itself - this is one of the forms of polymorphism in C#.
Because you have created instance of class B not A and you are able to hold in variable of type A due to inheritance feature of OOP as you are inheriting your B class from A.
But the actual type of the a2 is B not A though it can be represent as A as well, but the GetType() reutrns the run-time type which is B.
You can have a look at this SO post too which explains what the GetType is expected to return for an object and what is typeof() and how we can use is for inheritance hierarchy checking.
Hope it helps.
Just because you refer to it through type A doesn't mean that the actual type suddenly changes to A, it's still a B object. You decided to create one with new B().
You should be aware of the difference between compile time type and runtime type. The compile time type is the type the compiler knows about in this case the type you have declared - A. The runtime type is the type of the object that happens to be referenced by the variable in this case B. Compile time (what the compiler knows about) and runtime (what happens when the program is actually run) is a very important distinction that applies to types, errors and even calculations.
This question already has answers here:
Compile-time and runtime casting c#
(2 answers)
Closed 7 years ago.
I understand that a base class cannot be converted into a derived class. What I don't understand is why this isn't caught at compile time? For example:
class GradeBook
{
}
class ProfessorGradeBook : GradeBook
{
}
class Program
{
static void Main(string[] args)
{
ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();
//shouldn't this be a compile time error?
}
}
I've looked at other questions on stackoverflow but it still doesn't make sense to me why this would compile? ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook(); would never succeed under any circumstance (right?) so why is this a run-time error instead of a compile-time error?
EDIT:
I already knew why the compiler would never catch this:
GradeBook a = new ProfessorGradeBook();
ProfessorGradeBook b = (ProfessorGradeBook)a;
At run-time, a could be pointing to anything so the compiler should just trust you. I was more concerned with why the compiler would never catch this:
ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();
I guess the answer that makes the most sense is Eric Lippert's first comment, specifically "the vast majority of developers would never type that line of code" so the compiler team was never concerned with trying to make that an error.
This is a downcast. The compiler can't know if a less typed reference may or may not be castable to a more specialized type.
For example:
public class A {}
public class B {}
A a = new B();
B b = (B)a;
a is typed as A while the object stored there is of type B. BTW, when you try to cast it to B, since an instance of A can be A itself or any derived class of A (including B, but not only B), the compiler can't assume that a won't be castable to B because you're providing the assumption that it will be possible using an explicit cast.
At the end of the day, typing is metadata. If you declare a reference as A you're telling the compiler that whatever you set there will be A, assuming that you're losing compile-time metadata to access more specialized members from a possible derived type. In other words: you're telling the compiler that the reference is A and you don't care about the derived type metadata in compile-time and any downcast will be evaluated during run-time because the compiler can't prove a downcast unless the code is executed and runtime finds that the so-called downcast isn't possible since the explicitly provided type isn't part of the source type hierarchy.
Probably compilers could be able to catch invalid downcasts, but this could also increase build times...
You are right that it would never succeed in practice, but the new instruction is a run-time instruction, not a compile-time instruction, and you are doing an explicit cast to (ProfessorGradeBook) which is basically saying to the compiler: "hey compiler, just trust me it'll work".
And so the compiler does.
There are scenarios where one could use things like Fody or PostSharp to add conversion operators after compilation
The compiler does not catch all statically knowable errors. That runs into the halting problem. The compiler catches a well defined subset of all statically discoverable errors.
Also note, that the compiler is not allowed to be arbitrarily smart. The C# language spec says how smart exactly it must be so that all C# compilers behave the same way.
How smart do you like it? Do you want it to catch this as well?
static void Main(string[] args)
{
var g = new GradeBook();
ProfessorGradeBook a = (ProfessorGradeBook)g;
//shouldn't this be a compile time error?
}
That's harder. We can make it arbitrarily hard.
This question already has answers here:
Compile-time and runtime casting c#
(2 answers)
Closed 7 years ago.
You have these classes shown below:
public class A
{
}
public class B : A
{
}
You cast the base class to a type of the derived class
A w = (B) new A();
B x = (B) new A();
This will not work on the run time because you cannot really convert a base class to a derived class.
But why is there no compile time error? why does visual studio allowed me to reach run-time before throwing the error?
There are 2 types of casts
once that clearly not allowed when classes have no common base and hence cast have no chance to succeed. I.e. 'string' to 'int'. Such casts are caught by compiler and cause errors.
casts that have chance to succeed - base to derived have reasonable chance to succeed. Compiler allows such casts.
I believe the reason why (B)new A() is allowed at compile time even if cast is guaranteed to fail is because (B)someObjectOfTypeA can succeed and new A() is definitely one of such "object of type A". Compile time detection likely would require additional infrastructure and was not found beneficial (as this cast immediately fails at runtime hence have low chance to be missed by even most basic testing of your code).
In c# casting is in runtime for user defined classes, that is why compiler doesn't throw error. You can see this for more info. The casting from one basic type (string) to another basic type (int) is known as compile time, as compiler knows string can't be type casted to int! But in user defined classes, there is a chance! :)
I need to create a heterogeneous List of objects (custom classes). My first thought was to create a List<ISomeMarkerInterface> but I quickly learned that this is not what I want. My next thought was List<dynamic> and this didn't seem to be a bad idea. However, I was doing some research and came across this article about boxing and unboxing and in the example, they're doing basically what I want using List<Object>.
Aside from the fact that dynamic will be evaluated at runtime and Object at compile-time, what is the difference between List<dynamic> and List<Object>? Aren't they essentially the same thing?
There are 3 "general" types (although not all are real types) in C#: object, var and dynamic.
Object
An actual type, like any other type, with one special rule: if a type doesn't inherit, it inherits from object. From this, it follows that all types inherit from object, directly or indirectly.
Emphasis: object is a type. An object can be of type object, and the type has its methods, like ToString(). Since everything inherits from object, everything can be upcast into object. When you assign an object to an object reference, you are doing upcasting just like when you assign an Elephant type object to an Animal reference where Elephant inherits from Animal.
SomeType x = new SomeType();
object obj = x;
obj.DoSomething();
obj is treated as being of type object at compile time, and will be of type object at runtime (which is logical, since it is an actual type - obj is declared as object so can only be of that type)
obj.DoSomething() will cause a compile-time error, as object does not have this method, regardless of whether SomeType has it.
Var
This is not an actual type, it is merely shorthand for "compiler, figure out the type for me based on the right side of the assignment".
SomeType x = new SomeType();
var obj = x;
obj.DoSomething();
obj is treated as being of type SomeType at compile time, and will be of type SomeType at runtime, just as if you had written "SomeType" instead of "var".
if SomeType has a method DoSomething(), this code will work
if SomeType doesn't have the method, the code will cause a compile-time error
Dynamic
This is a type that tells the compiler to disable compile-time type checking on the variable. An object is treated as having the type dynamic at compile-time and run-time.
SomeType x = new SomeType();
dynamic obj = x;
obj.DoSomething();
obj is of type dynamic at compile and run time
if SomeType has a method DoSomething(), this code will work
if SomeType doesn't have the method, the code will compile, but throw an exception at run-time
note that dynamic can cause exceptions very easily if used carelessly:
public void f(dynamic x)
{
x.DoSomething();
}
This will throw an exception if x is of a type that doesn't have the DoSomething method, but it will still be possible to call it and pass any object as the parameter without a compile-time error, causing an error that only shows itself at run-time, and possibly only in specific circumstances - a potential bug. So if you use dynamic in any kind of public interface of a class, you should always manually type-check at runtime using reflection, carefully deal with exceptions, or not do it in the first place.
Note: the object being referred to never changes its type, of course. While obj may be object, the x that it refers to is still SomeType.
The difference is that if you use object and you try to access some member of your object it will be a compile time error (because object doesn't have this member). In order to work you need to know what the type is and cast it.
With dynamic you can access any member - no compile time error. If the member doesn't exist at runtime it would be a runtime error. This is the way to go if you know that your heretogeneous objects all have the same member for example.
However if this is the case there is another more clear solution: You can define an interface, with this member and then make all your heretogeneous objects implement it and your list can be List<IYourInterface>.
Keep in mind that dynamic's performance might be slightly worse, because of the, well, dynamic type resolution.
Suppose I have two classes:
class a
{
public void sayGoodbye() { Console.WriteLine("Tschüss"); }
public virtual void sayHi() { Console.WriteLine("Servus"); }
}
class b : a
{
new public void sayGoodbye() { Console.WriteLine("Bye"); }
override public void sayHi() { Console.WriteLine("Hi"); }
}
If I call a generic method that requires type 'T' to be derived from class 'a':
void call<T>() where T : a
Then inside that method I call methods on an instance of type 'T' the method call are bound to type 'a', as if the instance was being cast as 'a':
call<b>();
...
void call<T>() where T : a
{
T o = Activator.CreateInstance<T>();
o.sayHi(); // writes "Hi" (virtual method)
o.sayGoodbye(); // writes "Tschüss"
}
By using reflection I am able to get the expected results:
call<b>();
...
void call<T>() where T : a
{
T o = Activator.CreateInstance<T>();
// Reflections works fine:
typeof(T).GetMethod("sayHi").Invoke(o, null); // writes "Hi"
typeof(T).GetMethod("sayGoodbye").Invoke(o, null); // writes "Bye"
}
Also, by using an interface for class 'a' I get the expected results:
interface Ia
{
void sayGoodbye();
void sayHi();
}
...
class a : Ia // 'a' implements 'Ia'
...
call<b>();
...
void call<T>() where T : Ia
{
T o = Activator.CreateInstance<T>();
o.sayHi(); // writes "Hi"
o.sayGoodbye(); // writes "Bye"
}
The equivalent non-generic code also works fine:
call();
...
void call()
{
b o = Activator.CreateInstance<b>();
o.sayHi(); // writes "Hi"
o.sayGoodbye(); // writes "Bye"
}
Same thing if I change the generic constraint to 'b':
call<b>();
...
void call<T>() where T : b
{
T o = Activator.CreateInstance<T>();
o.sayHi(); // writes "Hi"
o.sayGoodbye(); // writes "Bye"
}
It seems that the compiler is generating method calls to the base class specified in the constraint, so I guess I understand what is happening, but this is not what I expected. Is this really the correct result?
Generics aren't C++ Templates
Generics are a general type: there will be only one generic class (or method) output by the compiler. Generics doesn't work by compile-time replacing T with the actual type provided, which would require compiling a separate generic instance per type parameter but instead works by making one type with empty "blanks". Within the generic type the compiler then proceeds to resolve actions on those "blanks" without knowledge of the specific parameter types. It thus uses the only information it already has; namely the constraints you provide in addition to global facts such as everything-is-an-object.
So when you say...
void call<T>() where T : a {
T o = Activator.CreateInstance<T>();
o.sayGoodbye();//nonvirtual
...then type T of o is only relevant at compile time - the runtime type may be more specific. And at compile time, T is essentially a synonym for a - after all, that's all the compiler knows about T! So consider the following completely equivalent code:
void call<T>() where T : a {
a o = Activator.CreateInstance<T>();
o.sayGoodbye();//nonvirtual
Now, calling a non-virtual method ignores the run-time type of a variable. As expected, you see that a.sayGoodbye() is called.
By comparison, C++ templates do work the way you expect - they actually expand the template at compile time, rather than making a single definition with "blanks", and thus the specific template instances can use methods only available to that specialization. As a matter of fact, even at run-time, the CLR avoids actually instantiating specific instances of templates: since all the calls are either virtual (making explicit instantiation unnecessary) or non-virtual to a specific class (again, no point in instantiating), the CLR can use the same bytes - probably even the same x86 code - to cover multiple types. This isn't always possible (e.g. for value types), but for reference types that saves memory and JIT-time.
Two more things...
Firstly, your call method uses Activator - that's not necessary; there's an exceptional constraint new() you may use instead that does the same thing but with compile-time checking:
void call<T>() where T : a, new() {
T o = new T();
o.sayGoodbye();
Attempting to compile call<TypeWithoutDefaultConstructor>() will fail at compile time with human-readable message.
Secondly, it may seem as though generics are largely pointless if they're just blanks - after all, why not simply work on a-typed variables all along? Well, although at compile-time you can't rely on any details a sub-class of a might have within the generic method, you're still enforcing that all T are of the same subclass, which allows in particular the usage of the well-known containers such as List<int> - where even though List<> can never rely on int internals, to users of List<> it's still handy to avoid casting (and related performance and correctness issues).
Generics also allow richer constraints than normal parameters: for example, you can't normally write a method that requires its parameter to be both a subtype of a and IDisposable - but you can have several constraints on a type parameter, and declare a parameter to be of that generic type.
Finally, generics may have run-time differences. Your call to Activator.CreateInstance<T>() is a perfect illustration of that, as would be the simple expression typeof(T) or if(myvar is T).... So, even though in some sense the compiler "thinks" of the return type of Activator.CreateInstance<T>() as a at compile time, at runtime the object will be of type T.
sayGoodbye is not virtual.
The compiler only "knows" T is of type a. It will call sayGoodbye on a.
On type b you redefine sayGoodbye, but the compiler is not aware of type b. It cannot know all derivates of a. You can tell the compiler that sayGoodbye may be overriden, by making it virtual. This will cause the compiler to call sayGoodbye on a special way.
Method hiding is not the same as polymorphism, as you've seen. You can always call the A version of the method simply by downcasting from B to A.
With a generic method, with T constrained to type A, there is no way for the compiler to know whether it could be some other type, so it would be very unexpected, in fact, for it to use a hiding method rather than the method defined on A. Method hiding is for convenience or interoperability; it has nothing to do with substituting behavior; for that, you need polymorphism and virtual methods.
EDIT:
I think the fundamental confusion here is actually Generics vs. C++ style templates. In .NET, there is only one base of code for the generic type. Creating a specialized generic type does not involve emitting new code for the specific type. This is different from C++, where a template specialization involves actually creating and compiling additional code, so that it will be truly specialized for the type specified.
The new keyword is kind of a hack in C#. It contradicts polymorphism, because the method called depends on the type of the reference you hold.