This question already has answers here:
How can an object not be compared to null?
(5 answers)
C# okay with comparing value types to null
(11 answers)
Closed 9 years ago.
Int32 struct doesn't define operator overload method for == operator, so why doesn't the code cause compile time error:
if(1 == null) ... ;
Let's take a step back here. The question is confusing and the answers so far are not very clear as to what is going on here.
Shouldn't if(1 == null) cause an error?
No. That is legal, though dumb.
How does the compiler deal with operators like "=="? It does so by applying the overload resolution algorithm.
The first thing we must determine is whether this is a "user defined" equality operator or a "built in" equality operator. The left side is a built-in type. The right side has no type at all. Neither of those are user-defined types. Therefore no user-defined operator will be considered. Only built-in operators will be considered.
Once we know that, the question is "which built-in operators will be considered?" The built in operators are described in section 7.10 of the spec. They are equality operators on int, uint, long, ulong, decimal, float, double, any enum type, bool, char, object, string and any delegate type.
All of the equality operators on value types also have a "lifted" form that takes nullable value types.
We must now determine which of those operators are applicable. To be applicable, there must be an implicit conversion from both sides to the operator's type.
There is no implicit conversion from int to any enum type, bool, string or any delegate type, so those all vanish from consideration.
(There is not an implicit conversion from int to uint, ulong, etc, but since this is a literal one, there is an implicit conversion from 1 to uint, ulong, etc.)
There is no implicit conversion from null to any non-nullable value type, so those all disappear too.
What does that leave? That leaves the operators on object, int?, long?, uint?, ulong?, double?, float?, decimal? and char? the remaining nullable types.
We must now determine which one of those remaining applicable candidates is the unique "best" operator. An operator is better than another operator if its operand type is more specific. "object" is the least specific type, so it is eliminated. Clearly every nullable int can be converted to nullable long, but not every nullable long can be converted to nullable int, so nullable long is less specific than nullable int. So it is eliminated. We continue to eliminate operators in this manner. (In the case of the unsigned types we apply a special rule that says that if int? and uint? are both options then int? wins.)
I will spare you the details; ultimately that process leaves nullable int as the unique best operand type.
Therefore your program is interpreted as if((int?)1 == (int?)null), which clearly is legal, and will always be false.
Int32 struct doesn't define operator overload method for == operator
You are correct. What does that have to do with anything? The compiler is perfectly able to do the analysis without it. I don't understand the relationship you believe this fact has to your question. The fact is about a method that could be defined on a type, the question is about how overload resolution chooses a lifted built-in operator. Those two things are not related because "int" is not a user-defined type.
It is a value type rather than a reference type. So it will not need operators.
http://msdn.microsoft.com/en-us/library/s1ax56ch.aspx
The operators for primitive types (every numeric type except decimal) are defined by the language, not the runtime.
They compile to IL instructions rather than method calls (ceq for ==)
Have a look at MSDN blog. This contains an answer to your question.
Related
This question already has an answer here:
Casting an object using the "as" keyword returns null
(1 answer)
Closed 3 years ago.
long? i = 10000;
int? s = i as int?;
Why is it giving me a null in s variable? I know that casting it using int? s = (int?)i; will work fine but why can't I use an as operator?
From MSDN:
The code is equivalent to the following expression except that the expression variable is evaluated only one time.
expression is type ? (type)expression : (type)null
Since i is int? is false the as keyword will return (int?)null.
The types aren't equivalent and you get null, that is just how as works
However, you could just direct Cast
long? i = 10000;
var s = (int?)i;
// or even better make sure its *checked*
var s = checked((int?)i);
Why does this work?
C# Language Specification 11.6.1 Nullable Conversions
...
Evaluation of a nullable conversion based on an underlying conversion
from S to T proceeds as follows:
If the nullable conversion is from S? to T?:
If the source value is null (HasValue property is false), the result is the null value of type T?.
Otherwise, the conversion is evaluated as an unwrapping from S? to S, followed by the underlying conversion from S to T, followed by a
wrapping from T to T?.
...
as does not do this, it checks the run-time types, if they are not equal returns null T?
Additional Resources
checked (C# Reference)
The checked keyword is used to explicitly enable overflow checking for
integral-type arithmetic operations and conversions.
#Update from comments
I got that we can't convert and why we can't do I but why they are
suggesting it is there any scenario where it will be useful i'e some
random image link of Compiler Error CS0077 "The as operator must be used with a reference type or nullable type ('int' is a non-nullable value type)."
The reason is (in your image example) a value type can't be null it could never work with it. The thing you are missing is there are more complex examples of what could happen, you can define implicit operators on custom types, etc.
The compiler cannot implicitly convert 'long' to 'int'
And as per MSDN
The as operator is like a cast operation. However, if the conversion
isn't possible, as returns null instead of raising an exception.
ref: as (C# Reference)
So with your code, conversion is not possible, as is returning Null.
In C# there are 2 ways of casting:
foo as int
(int)foo
Why does the first line not compile and the second does?
Console.Write(49 as char);
Console.Write((char)49);
From MSDN:
You can use the as operator to perform certain types of conversions between compatible reference types or nullable types.
char is neither a reference type nor a nullable type. It can't set the output variable of 49 to null (when the conversion fails) since it isn't nullable. It would work with char? though, although useless in this situation.
This question already has answers here:
C# okay with comparing value types to null
(11 answers)
Closed 9 years ago.
This is not to solve any particular problem. Simply a compiler question.
Why does the following code not result in compile error? It's comparing a reference type to primitive type. Both null and false have to to be interpreted into something for the compiler to do comparison. Or is the parser simply scanning for such pattern and replace it with false?
if(null == false) { }
It's legal because the lifted comparison operator is used. If you compare a bool to a null, both the bool and null get implicitly converted to Nullable<bool> and the comparison operator for Nullable<bool> ends up being used. You get a warning because obviously, it is always false.
Tejas's answer is correct. To more specifically address some of your points:
Why does the following code not result in compile error?
The question is not answerable; it does not produce an error because it is legal code, but that's a tautology.
If your question is actually "which section of the C# specification makes this legal?", then that's an answerable question. The section on lifted equality operators makes it legal.
It's comparing a reference type to primitive type.
It is not. First off, avoid the term "primitive type"; the specification does not clearly define it and it is not a useful concept in C#. You meant to say I think that it is comparing a value of reference type to a value of value type.
Second, that's not correct either. The null literal is not of reference type or value type; it is of no type. It is convertible to any nullable value type or any reference type, but it is of no type just by itself.
In this case the null literal is converted to the nullable bool type.
Both null and false have to be interpreted into something for the compiler to do comparison.
Correct. They are interpreted as nullable bools.
is the parser simply scanning for such pattern and replace it with false?
No, but that's an excellent guess. The compiler would constant-fold, say, true == false down to false, but it does not do folding optimizations that involve nullable value types. The language could be redesigned to support constant folding on operations with nullable value type operands; had nullable value types counterfactually been in version one, the proposed feature likely would have been supported.
Section 7.10.6 of the language specification (Reference type equality operators) states:
The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.
This provision requires that null == false be false, and not a compiler error.
Why does the first and second Write work but not the last? Is there a way I can allow all 3 of them and detect if it was 1, (int)1 or i passed in? And really why is one allowed but the last? The second being allowed but not the last really blows my mind.
Demo to show compile error
using System;
class Program
{
public static void Write(short v) { }
static void Main(string[] args)
{
Write(1);//ok
Write((int)1);//ok
int i=1;
Write(i);//error!?
}
}
The first two are constant expressions, the last one isn't.
The C# specification allows an implicit conversion from int to short for constants, but not for other expressions. This is a reasonable rule, since for constants the compiler can ensure that the value fits into the target type, but it can't for normal expressions.
This rule is in line with the guideline that implicit conversions should be lossless.
6.1.8 Implicit constant expression conversions
An implicit constant expression conversion permits the following conversions:
A constant-expression (§7.18) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.
A constant-expression of type long can be converted to type ulong, provided the value of the constant-expression is not negative.
(Quoted from C# Language Specification Version 3.0)
There is no implicit conversion from int to short because of the possibility of truncation. However, a constant expression can be treated as being of the target type by the compiler.
1? Not a problem: it’s clearly a valid short value. i? Not so much – it could be some value > short.MaxValue for instance, and the compiler cannot check that in the general case.
an int literal can be implicitly converted to short. Whereas:
You cannot implicitly convert nonliteral numeric types of larger storage size to short
So, the first two work because the implicit conversion of literals is allowed.
I believe it is because you are passing in a literal/constant in the first two, but there is not automatic type conversion when passing in an integer in the third.
Edit: Someone beat me to it!
The compiler has told you why the code fails:
cannot convert `int' expression to type `short'
So here's the question you should be asking: why does this conversion fail? I googled "c# convert int short" and ended up on the MS C# page for the short keyword:
http://msdn.microsoft.com/en-us/library/ybs77ex4(v=vs.71).aspx
As this page says, implicit casts from a bigger data type to short are only allowed for literals. The compiler can tell when a literal is out of range, but not otherwise, so it needs reassurance that you've avoided an out-of-range error in your program logic. That reassurance is provided by a cast.
Write((short)i)
Because there will not be any implicit conversion between Nonliteral type to larger sized short.
Implicit conversion is only possible for constant-expression.
public static void Write(short v) { }
Where as you are passing integer value as an argument to short
int i=1;
Write(i); //Which is Nonliteral here
Converting from int -> short might result in data truncation. Thats why.
Conversion from short --> int happens implicitly but int -> short will throw compile error bcoz it might result in data truncation.
This question already has answers here:
Nullable types and the ternary operator: why is `? 10 : null` forbidden? [duplicate]
(9 answers)
Conditional operator cannot cast implicitly?
(3 answers)
Closed 9 years ago.
Assume two classes, both descendants of the same superclass, like this:
class MySuperClass{}
class A : MySuperClass{}
class B : MySuperClass{}
Then this assignment won't pass the compiler:
MySuperClass p = myCondition ? new A() : new B();
The compiler complains that A and B are not compatible (Type of conditional expression cannot be determined because there is no implicit conversion between 'A' and 'B' [CS0173] ). But they are both of type MySuperClass, so in my opinion this should work. Not that it's a big deal; a simple cast is all it takes to enlighten the compiler. But surely it's a snag in the C# compiler? Don't you agree?
The results of the conditional should be of the same type. They are not.
From MSDN, (?: Operator):
Either the type of first_expression and second_expression must be the same, or an implicit conversion must exist from one type to the other.
Since A and B are not the same type and you don't seem to have defined an implicit conversion, the compiler complains.
Look to section 7.14 of the language specification
The second and third operands, x and
y, of the ?: operator control the type
of the conditional expression.
· If x has type X and y has
type Y then
o If an implicit conversion (§6.1)
exists from X to Y, but not from Y to
X, then Y is the type of the
conditional expression.
o If an implicit conversion (§6.1)
exists from Y to X, but not from X to
Y, then X is the type of the
conditional expression.
o Otherwise, no expression type can
be determined, and a compile-time
error occurs.
· If only one of x and y has a
type, and both x and y, of
areimplicitly convertible to that
type, then that is the type of the
conditional expression.
· Otherwise, no expression type can be determined, and a compile-time error occurs.
Essentially, the operands must be convertible to one another, not mutually convertible to some other type.
It's why you need to make an explicit cast in your example or in cases such as nullables (int? foo = isBar ? 42 : (int?)null). The declaration type does not impact the evaluation, the compiler must figure it out from the expression itself.
Check out this blog for some interesting articles on why C# compiler does/doesn't do things that are 'obvious' to you. The blog is written by Eric Lippert, one of the C# compiler developers.
The compiler doesn't try to look for a common ancestor, so you need an explicit cast to show which ancestor you wanted to treat it as; In your case:
MySuperClass p = myCondition ? (MySuperClass)(new A()) : (MySuperClass)(new B());
This means that the conditional operator has both sides returnin gthe same type, which satisfies the compiler.
The conditional operator (as with any other operator) has to define the type that its expression represents. In the case of the conditional operator, it has a two-step process:
Are the operands of the same type? If so, that is the type of the expression.
Is there an implicit conversion from one of the operand types to the other (but not in both directions)? If so, then the "other" is the type of the expression.
There's no ancestry search, as implementing that could lead down a slippery slope of ambiguity in what you, as a developer, could specify in that expression. Should everything lead to object? What about value types, which would then be implicitly boxed? What about interfaces? If there's more than one common interface between the two types, which one should be chosen?
In your case, as you've discovered, you need to upcast one of the operands to the parent type. Once you do that, rule 2.) is satisfied (there is always an implicit conversion going from a more specific type to a less specific type).
Note that you only need to apply the cast to one of the operands, not both.
Rowland Shaw summed it up pretty well, but to see why a common ancestor isn't used implicitly, consider what would happen if both classes were also to implement a particular interface. The compiler wouldn't be able to work out which to use for the type of the conditional operator, and so it would be forced to use object instead.