I stumbled across this odd case yesterday, where t as D returns a non-null value, but (D)t causes a compiler error.
Since I was in a hurry I just used t as D and carried on, but I am curious about why the cast is invalid, as t really is a D. Can anyone shed some light on why the compiler doesn't like the cast?
class Program
{
public class B<T> where T : B<T> { }
public class D : B<D> { public void M() { Console.Out.WriteLine("D.M called."); } }
static void Main() { M(new D()); }
public static void M<T>(T t) where T : B<T>
{
// Works as expected: prints "D.M called."
var d = t as D;
if (d != null)
d.M();
// Compiler error: "Cannot cast expression of type 'T' to type 'D'."
// even though t really is a D!
if (t is D)
((D)t).M();
}
}
EDIT: Playing around, I think this is a clearer example. In both cases t is constrained to be a B and is maybe a D. But the case with the generic won't compile. Does the C# just ignore the generic constraint when determining if the cast is legal? Even if it does ignore it, t could still be a D; so why is this a compile time error instead of a runtime exception?
class Program2
{
public class B { }
public class D : B { public void M() { } }
static void Main()
{
M(new D());
}
public static void M(B t)
{
// Works fine!
if (t is D)
((D)t).M();
}
public static void M<T>(T t) where T : B
{
// Compile error!
if (t is D)
((D)t).M();
}
}
In your second example you can change
((D)t).M();
to
((D)((B)t)).M();
You can cast from B to D, but you can't necessarily cast from "something that is a B" to D. "Something that is a B" could be an A, for example, if A : B.
The compiler error comes about when you are potentially jumping from child to child in the hierarchy. You can cast up and down the hierarchy, but you can't cast across it.
((D)t).M(); // potentially across, if t is an A
((D)((B)t)).M(); // first up the hierarchy, then back down
Notice also that you might still get a runtime error with ((D)((B)t)).M(); if t is not actually a D. (your is check should prevent this though.)
(Also notice that in neither case is the compiler taking into account the if (t is D) check.)
A last example:
class Base { }
class A : Base { }
class C : Base { }
...
A a = new A();
C c1 = (C)a; // compiler error
C c2 = (C)((Base)a); // no compiler error, but a runtime error (and a resharper warning)
// the same is true for 'as'
C c3 = a as C; // compiler error
C c4 = (a as Base) as C; // no compiler error, but always evaluates to null (and a resharper warning)
Change it to
public static void M<T>(T t) where T : D
This is the appropriate restriction if you want to mandate that T has to be of type D.
It won't compile if your restriction defines T as D of T.
Ex: You can't cast List<int> to int or vice versa
Your template function have a constraint that requires T to be B<T>.
So, when your compiler tries to convert the object t of type T to D it can't do it. Because T is guranteed to be B<T>, but not D.
If you add a constraint to require that T is D, this will work. i.e. where T: B<T>, D or simply where T: D which also guarantees that T is B<T>, beacuse of the inheritance chain.
And the second part of the question: when you call t as D it's checked at runtime. And, at runtime, using polymorphism, it's verified that t can be converted to D type, and it's done without errors.
NOTE: by the way, this code is sooooo strange. Are you sure about what you're doing?
Related
public class A
{
public void M1()
{
Console.Write("Print M1 of Class A");
}
}
public class B:A
{
public void M2()
{
Console.Write("Print M2 of Class B");
}
}
public class C : B
{
public void M3()
{
Console.Write("Print M3 of Class C");
}
}
In main method we create objects like:-
A objA = new B();
B objB = new C();
A a = new A();
C objC = (C)a; /* this line throws runtime error System.InvalidCastException: 'Unable to cast object of type 'A' to type 'C'.' but build works fine */
objA.M1();
objB.M1();
objB.M2();
objC.M1();
objC.M2();
objC.M3();
I could not cast C to A directly first. Hence tried creating object A then explicit casted it. The intellisense takes it as valid code and build is also fine but during console app execution it throws error "Unable to cast object of type 'A' to type 'C'."
It's true; you could cast a C to an A any time you want but the only time you can do it the other way round is if you've stored a C in a variable typed as A. The compiler lets you do this because C descends from A, so a variable of type A is capable of holding an instance of type C. Hence, you could have legitimately stored a C in an A and you're casting to get it back to being a C:
A reallyC = new C();
C gotBack = (C)reallyC;
The compiler doesn't look too hard at what you've done before; it wouldn't see code like this:
A reallyA = new A();
C willFail = (C)reallyA;
and say "hey; you've stored an A in reallyA, and it can't be cast to a C". It just sees you attempt to cast reallyA to a C and thinks "it could be a C, because a C is a B is an A, so they're related ok.. I trust the dev knows what they're doing and I'll permit it"
Well, a is of (base) type A and can't be cast to C since C is derived from A. The reverse is possible: an object c of derived class C can be cast to base class A:
C c = new C();
A objA = (A) c;
Often we can try to cast with a help of is:
if (a is C objC) {
// a can be cast to C, objC now holds the result of the cast
objC.M3();
}
else {
// the cast is impossible
}
I have a sequence of classes and interfaces as follows:
interface IA
{ }
class A : IA
{
public static implicit operator A(int v) => new A(v);
public A(int v)
{ ... }
}
class B
{
public static void Foo(IA a)
{ ... }
}
And I want to be able to call B.Foo(int).
B.Foo(1) does not compile, but B.Foo((A)1) does. I am able to call B.Foo(1) if the method signature is instead Foo(A a), as C# is able to figure out the chain of implicit casting, so I know that I can modify the code in the following way:
class B
{
public static void Foo(A a) => Foo((IA)a)
public static void Foo(IA a)
{ ... }
}
However, this seems like a waste of code and time as I would have to do this for every method which accepts IA. It would become even more inefficient if a method such as Bar(IA p1, IA p2, ...) exists, as I would need n! copies of the signature with all of the different permutations of A and IA.
What is the best way to hint to C# that any method which accepts IA can also accept int?
For a call like B.Foo(1), the compiler would have to search through every type that implements IA to find a type T such that there is an implicit conversion from int to T and then perform the following conversion,
int -> T -> IA
The "finding T" step could be a lot of work, so the compiler is designed to not do it.
You seem to want to find a way to tell it that there is one specific implementation of IA, namely A, that does have have an implicit conversion from int. Well, you have actually already said it in the question:
B.Foo((A)1)
This tells the compiler to convert the int to an A first. Notice how this is as if you have found the type T, doing the hard work for the compiler.
Say I have a generic class and some other code as below:
public class S<T>
{
public void Method1(T o)
{
}
}
public class A
{
public string Name { get; set; }
}
public static void Test()
{
var s = new S<A>();
var a = new A() { Name = "John" };
s.Method1(a);
}
When debugging s.Method1(a) and stepping into S<T>.Method1(T o), I found that I cannot evaluate the cast operation - (GenericRuntimeCastWhenDebugging.A)o in the DEBUG CONSOLE when using VSCode or in the Immediate window when using VS2017, it gives error:
(GenericRuntimeCastWhenDebugging.A)o
error CS0030: Cannot convert type 'T' to 'GenericRuntimeCastWhenDebugging.A'
but (o as GenericRuntimeCastWhenDebugging.A) works fine in this case and shows the result:
(o as GenericRuntimeCastWhenDebugging.A)
{GenericRuntimeCastWhenDebugging.A}
I would expect that in this case (GenericRuntimeCastWhenDebugging.A)o should also work fine! Anyone knows why?
Edit:
I understand that T o isn't allowed be casted to A by (A)o at compile time in the example I gave, and the difference between (A)o and (o as A) in C#. I was interested that during debugging the 1st way to cast (GenericRuntimeCastWhenDebugging.A)o raises error, but the 2nd way to cast (o as GenericRuntimeCastWhenDebugging.A) casts successfully and returns as the right A object back. So during debugging, running (o as GenericRuntimeCastWhenDebugging.A).Name in the DEBUG CONSOLE is also fine and does return value: John.
(o as GenericRuntimeCastWhenDebugging.A).Name
"John"
Further, turned out that at compile time i can actually write the following code, it runs fine and n2 is set to John. Don't need a trick like boxing/unboxing.
public class S<T>
{
public void Method1(T o)
{
// var n1 = ((A)o).Name; Cannot compile
var n2 = (o as A).Name;
}
}
The problem is that there are no explicit conversions defined for T -> A. This is because there is no constraint on your generic type. Using as works because it's explicitly asking whether or not o can be cast, as opposed to explictly saying that o can be cast.
You can restrict T to be either A or a subclass/implementation of A with the following.
public class S<T> where T : A
{
public void Method1(T o)
{
var a = (A)o;
}
}
This is now valid, as the compiler is satisfied this won't generate a run-time exception.
Alternatively, you could write:
public class S<T>
{
public void Method1(T o)
{
var a = (A)(object)o;
}
}
Which will compile, because anything1 is castable (or boxable) to an object, and you can cast (or unbox) an object to any type. Note however, this is probably a bad idea, as there's nothing stopping someone from writing new S<int>().Method1(5)
1 Anything is (un)boxable/(un)castable at compile time - this does not however, protect you from runtime exceptions.
There is a difference in return value between (A)o and o as A.
(A)o is casting. If it fails, throws exception and everything crashes. Here you are basically asserting that o must be of type A. When it is not, BOOM.
o as A is much more gentle. It does not go BOOM! when it fails to convert o to type A. It just evaluates to null. You can then check whether the conversion failed by checking the return value. However, this only works on reference types because value types can't have null values.
Edit:
Actually, (A)o can't even compile. The compiler thinks that 9 times out of 10, this conversion is gonna fail, so it does not even allow it. I don't know how did you get that to compile and see the error in the debug console.
I have following code snippet but I am getting wrong output.
class Program
{
static void Main(string[] args)
{
var i = 10000;
var j = 10000;
Console.WriteLine((object) i == (object) j);
}
}
I was expecting true but I am getting false
You are boxing the numbers (via the object cast) which creates a new instance for each variable. The == operator for objects is based on object identity (also known as reference equality) and so you are seeing false (since the instances are not the same)
To correctly compare these objects you could use Object.Equals(i, j) or i.Equals(j). This will work because the actual runtime instance of the object will be Int32 whose Equals() method has the correct equality semantics for integers.
You are using == that by default invokes ReferenceEquals. You should use:
Console.WriteLine(((object)i).Equals((object)j));
because you are comparing two differents objects, that the reason.
They are two different objects and will never be equal. Their values are equal.
Operators are overloaded, not overridden. This means that the actual method that will be invoked is determined at compile time instead of at runtime. To understand this difference, you should think of it like this:
Suppose you have the following code
public class MyBase
{
public virtual void DoSomething() { Console.WriteLine("Base"); }
}
public class A : MyBase
{
public override void DoSomething() { Console.WriteLine("A"); }
}
public class B : MyBase
{
public override void DoSomething() { Console.WriteLine("B"); }
}
Now you can create an instance of A and B:
MyBase a = new A();
MyBase b = new B();
a.DoSomething(); // prints "A"
b.DoSomething(); // prints "B"
What happens is that every instance of MyBase contains a hidden reference to a vtable that points to the actual implementation of DoSomething() that must be invoked. So when the runtime needs to invoke DoSomething() on a, it will look at the vtable of a and will find the version that prints "A". The important part is that this happens at runtime.
Now when you overload a method, you create a method with the same name, but with a different signature.
public class C
{
public void Print(string str) { Console.WriteLine("string: " + string); }
public void Print(int i) { Console.WriteLine("int: " + i); }
public void Print(object obj) { Console.WriteLine("object: " + obj.ToString()); }
}
var c = new C();
c.Print("1"); // string: 1
c.Print(1); // int: 1
c.Print((object)1); // object: 1
This time the compiler decides which method is invoked instead of the runtime. And the compiler isn't very smart. When it sees (object)1, it sees a reference to an object, and the compiler tries to find a version of Print() that can take an object as argument.
Sometimes the compiler finds two versions that could be used. For example, "1" is a string, but it is also an object, so both the first and the third version of Print() are capable of accepting a string. In this case the compiler chooses the version that has the most specific argument types. Sometimes the compiler can't choose, like in the following example:
public static void Print(string a, object b) { Console.WriteLine("V1"); }
public static void Print(object a, string b) { Console.WriteLine("V2"); }
Print("a", "b"); // does this print "V1" or "V2"?
In this case, none of the versions is more specific than the other, so the compiler produces a compiler error: The call is ambiguous between the following methods or properties: 'Test.Program.Print(string, object)' and 'Test.Program.Print(object, string)'
As I said in the beginning, the == operator is overloaded. This means the compiler chooses which version to use. The most generic version is the one where both operands are of type Object. This version compares references. This is why (object) i == (object) j returns false.
This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
C# okay with comparing value types to null
I was working on a windows app in a multithreaded environment and would sometimes get the exception "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." So I figured that I'd just add this line of code:
if(this.Handle != null)
{
//BeginInvokeCode
}
But that didn't solve the problem. So I dug a little further, and realized that IntPtr (the type that Form.Handle is) is a struct which can't be nullable. This was the fix that worked:
if(this.Handle != IntPtr.Zero)
{
//BeginInvokeCode
}
So then it hit me, why did it even compile when I was checking it for null? So I decided to try it myself:
public struct Foo { }
and then:
static void Main(string[] args)
{
Foo f = new Foo();
if (f == null) { }
}
and sure enough it didn't compile saying that "Error 1 Operator '==' cannot be applied to operands of type 'ConsoleApplication1.Foo' and ''". Ok, so then I started looking at the metadata for IntPtr and started adding everything to my Foo struct that was there in the IntPtr struct (ISerializable, ComVisible) but nothing helped. Finally, when I added the operator overloading of == and !=, it worked:
[Serializable]
[ComVisible(true)]
public struct Foo : ISerializable
{
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
throw new NotImplementedException();
}
#endregion
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public static bool operator ==(Foo f1, Foo f2) { return false; }
public static bool operator !=(Foo f1, Foo f2) { return false; }
}
This finally compiled:
static void Main(string[] args)
{
Foo f = new Foo();
if (f == null) { }
}
My question is why? Why if you override == and != are you allowed to compare to null? The parameters to == and != are still of type Foo which aren't nullable, so why's this allowed all of a sudden?
It looks like the issue is that when MS introduced nullable types, they made it so that every struct is implicitly convertable to its nullable type (foo?), so the code
if( f == null)
is equivalent to
if ( (Nullable<foo>)f == (Nullable<foo>)null)
Since MSDN states that "any user-defined operators that exist for value types may also be used by nullable types", when you override operator==, you allow that implicit cast to compile, as you now have a user-defined == -- giving you the nullable overload for free.
An aside:
Seems like in your example, there is some compiler optimization
The only thing that is emitted by the compiler that even hints there was a test is this IL:
ldc.i4.0
ldc.i4.0
ceq
stloc.1 //where there is an unused boolean local
Note that if you change main to
Foo f = new Foo();
object b = null;
if (f == b) { Console.WriteLine("?"); }
It no longer compiles. But if you box the struct:
Foo f = new Foo();
object b = null;
if ((object)f == b) { Console.WriteLine("?"); }
if compiles, emits IL, and runs as expected (the struct is never null);
This has nothing to do with serialization or COM - so it's worth removing that from the equation. For instance, here's a short but complete program which demonstrates the problem:
using System;
public struct Foo
{
// These change the calling code's correctness
public static bool operator ==(Foo f1, Foo f2) { return false; }
public static bool operator !=(Foo f1, Foo f2) { return false; }
// These aren't relevant, but the compiler will issue an
// unrelated warning if they're missing
public override bool Equals(object x) { return false; }
public override int GetHashCode() { return 0; }
}
public class Test
{
static void Main()
{
Foo f = new Foo();
Console.WriteLine(f == null);
}
}
I believe this compiles because there's an implicit conversion from the null literal to Nullable<Foo> and you can do this legally:
Foo f = new Foo();
Foo? g = null;
Console.WriteLine(f == g);
It's interesting that this only happens when == is overloaded - Marc Gravell has spotted this before. I don't know whether it's actually a compiler bug, or just something very subtle in the way that conversions, overloads etc are resolved.
In some cases (e.g. int, decimal) the compiler will warn you about the implicit conversion - but in others (e.g. Guid) it doesn't.
All I can think is that your overloading of the == operator gives the compiler a choice between:
public static bool operator ==(object o1, object o2)
and
public static bool operator ==(Foo f1, Foo f2)
and that with both to choose from it is able to cast the left to object and use the former. Certainly if you try to run something based on your code, it doesn't step into your operator overload. With no choice between operators, the compiler is clearly carrying out some further checking.
struct doesn't define the overloads "==" or "!=" which is why you got the original error. Once the overloads were added to your struct the comparision was legal (from a compiler prospective). As the creator of the operator overload is it your responsibility to handle this logic (obviously Microsoft missed this in this case).
Depending on your implementation of your struct (and what it represents) a comparison to null may be perfectly valid which is why this is possible.
I believe when you overload an operator you are explicitly subscribing to the notion that you will handle all of the logic necessary with the specific operator. Hence it is your responsibility to handle null in the operator overload method, if it ever gets hit. In this case as I am sure you've probably noticed the overloaded methods never get hit if you compare to null.
Whats really interesting is that following Henks answer here, i checked out the following code in reflector.
Foo f1 = new Foo();
if(f1 == null)
{
Console.WriteLine("impossible");
}
Console.ReadKey();
This is what reflector showed.
Foo f1 = new Foo();
Console.ReadKey();
Compiler cleans it up and hence the overloaded operator methods never even get called.
I recomend you to take a look to those pages:
http://www.albahari.com/valuevsreftypes.aspx
http://msdn.microsoft.com/en-us/library/s1ax56ch.aspx
http://msdn.microsoft.com/en-us/library/490f96s2.aspx