differences between the conditional operator and an if-else: - c#

I read this question and I found Tim Schmelter's answer:
By the way, that's one of the differences between the conditional operator and an if-else
you can check the answer for this question, and I can't found the reason ?
if conditional operator work like if-else why if else don't need the cast
question :
int? l = lc.HasValue ? (int)lc.Value : null;
"Tim Schmelter" answer :
You have to cast null as well:
int? l = lc.HasValue ? (int)lc.Value : (int?)null;
By the way, that's one of the differences between the conditional operator and an if-else:
if (lc.HasValue)
l = (int)lc.Value;
else
l = null; // works

The literal null alone does not have a type, but it is implicitly convertible to any reference type and to any Nullable<> type. In the expression:
x = null
where x is assigned to null, the compiler can easily infer from the type of the variable (or field or property or parameter or whatever) x what the null literal shall be converted into. For example if x is of type string, the null shall represent the null reference, while if x is of type int?, the null shall represent an instance of Nullable<int> where HasValue is false.
If x is of type int, no implicit conversion exists, and the above expression shall not compile.
(The declaration var x = null; with var is not legal since null has no type in itself.)
On the other hand, in the expression:
someBoolean ? 42 : null /* illegal */
the compiler cannot figure out what type to convert null into. Remember that int is neither reference type, nor Nullable<> type.
If you meant wrapping into a nullable, use:
someBoolean ? (int?)42 : null
or:
someBoolean ? 42 : (int?)null
In both cases the compiler will automatically see that the other operand (on the other side of the colon :) must also be implicitly converted to int?.
If you meant boxing into some base class or interface of int, write that:
someBoolean ? (object)42 : null
or:
someBoolean ? 42 : (object)null
Now, the expressions above could be sub-expressions of a greater containing expression, but the compiler will still need the type of the ?: expression to be clear by itself. For example in:
int? x;
x = someBoolean ? 42 : null; // still illegal!
even if the sub-expression someBoolean ? 42 : null appears inside a larger expression x = someBoolean ? 42 : null where x does have a type, the sub-expression must still acquire its type "intrinsically". The type of x cannot "leak onto" the sub-expression. This "grammar" seems to be a surprise to many new C# developers. Questions like yours are often seen, see e.g. Nullable type issue with ?: Conditional Operator and the threads linked to it.

The if statement doesn't yield a value, so the statements in the "then" and "else" parts don't need to be type compatible in any way.
The conditional operator yields a value, and therefore both parts must be type compatible in some way in order for the compiler to determine the type of the expression.

MSDN says for the conditional 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.*
This is the reason. In the if-else constructor the code in else block is independent from the code in if block so the type-casting can be inferred by the compiler in each case.

Related

Coalescing operator vs Ternary operator [duplicate]

This question already has answers here:
Conditional operator assignment with Nullable<value> types?
(6 answers)
Why doesn't this C# code compile?
(4 answers)
Closed 9 years ago.
I just came across a weird error:
private bool GetBoolValue()
{
//Do some logic and return true or false
}
Then, in another method, something like this:
int? x = GetBoolValue() ? 10 : null;
Simple, if the method returns true, assign 10 to the Nullableint x. Otherwise, assign null to the nullable int. However, the compiler complains:
Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between int and <null>.
Am I going nuts?
The compiler first tries to evaluate the right-hand expression:
GetBoolValue() ? 10 : null
The 10 is an int literal (not int?) and null is, well, null. There's no implicit conversion between those two hence the error message.
If you change the right-hand expression to one of the following then it compiles because there is an implicit conversion between int? and null (#1) and between int and int? (#2, #3).
GetBoolValue() ? (int?)10 : null // #1
GetBoolValue() ? 10 : (int?)null // #2
GetBoolValue() ? 10 : default(int?) // #3
Try this:
int? x = GetBoolValue() ? 10 : (int?)null;
Basically what is happening is that conditional operator is unable to determine the "return type" of the expression. Since the compiler implictitly decides that 10 is an int it then decides that the return type of this expression shall be an int as well. Since an int cannot be null (the third operand of the conditional operator) it complains.
By casting the null to a Nullable<int> we are telling the compiler explicitly that the return type of this expression shall be a Nullable<int>. You could have just as easily casted the 10 to int? as well and had the same effect.
Try this:
int? result = condition ? 10 : default(int?);
Incidentally, the Microsoft implementation of the C# compiler actually gets the type analysis of the conditional operator wrong in a very subtle and interesting (to me) way. My article on it is Type inference woes, part one (2006-05-24).
Try one of these:
int? x = GetBoolValue() ? (int?)10 : null;
int? x = GetBoolValue() ? 10 : (int?)null;
The problem is that the ternary operator is inferring type based on your first parameter assignment...10 in this case, which is an int, not a nullable int.
You might have better luck with:
int? x = GetBoolValue() (int?)10 : null;
int? x = GetBoolValue() ? 10 : (int?)null;
The reason you see this is because behind the scenes you're using Nullable and you need to tell C# that your "null" is a null instance of Nullable.
Just add an explict cast.
int? x = GetBoolValue() ? 10 : (int?)null;
It is the ternary operator that gets confused - the second argument is an integer and so is the third argument exspected to be an integer, too, and null does not fit.
It's because the compiler determines the type of the conditional operator by its second and third operand, not by what you assign the result to. There is no direct cast between an integer and an null reference that the compiler can use to determine the type.

Conditional Operator ?: with Nullable type Casting

From the MSDN documentation, the following two snippets are equal:
bool value;
int x = (value) ? 0 : 1;
And
bool value;
int x;
if (value)
x = 0;
else
x = 1;
Great, wonderful. I use it all the time. Terse and effective.
If we try this with a nullable type, like so:
int? x = (value.HasValue) ? value.Value : null;
We get a compile-time error:
The type of conditional expression cannot be determined
because there is no implicit conversion between '{NullableType}' and null.
This compiles fine:
int? value;
int? x;
if (value.HasValue)
x = value.Value;
else
x = null;
So, I understand that the compiler requires an explicit cast in the way of (int?)null to compile the first statement. What I don't understand is why it is required in that statement, but not the If Else block.
null can represent any object-based datatype. You need to cast null as a datatype so it know what you are talking about.
int? x = (value.HasValue) ? value.Value : (int?)null;
I know, it sounds a bit strange.
To answer the questions in the comments:
Why is it not implicit though?
Yeah, I get that. But why do I not have to cast it in a If Else block?
Let's walk through the code.
Your else statement looks like this:
else x = null;
This means you are assigning the value of null to x. This is valid, because x is a int?, which takes nulls.
The difference comes when you have the ternary operator. It says: "assign the value of the operator into x". The question (and the reason for your error) is, what datatype is the result of the ternary operator?
From your code, you can't be sure, and the compiler throws its hands up.
int? x = (value.HasValue) ? value.Value : null;
// int? bool int ??
What datatype is null? You are quick to say "well it's a int?, because the other side is a int and the result is a int?". The problem is, what about the following:
string a = null;
bool? b = null;
SqlConnectionStringBuilder s = null;
This is also valid, which means null can be used for any object-based datatype. This is why you have to explicitly cast null as the type you want to use, because it can be used for anything!
Another explanation (and possible more accurate):
You can't have an implicit cast between a nullable and a non-nullable value.
int is not-nullable (it's a structure), where null is. This is why in Habib's answer you can put the cast on either the left or right side.
For Condtional operator MSDN states:
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.
So in you case your first_expression and second_expression are:
int? x = (value.HasValue) ? value.Value : null;
^^^^^^^^ ^^^^^
first exp 2nd Exp
Now if you see, your First Expression is of type int, and second expression is null and both are not same and there is no implicit conversion. So casting any of them to `int? solves the problem.
So:
int? x = (value.HasValue) ? (int?) value.Value : null;
Or
int? x = (value.HasValue) ? value.Value : (int?) null;
are fine.
Now why it is not required with if-else, because there multiple statements involved and it not a single statement assigning the value.
var x = value.HasValue ? value.Value : default(int?);
works too.
The documentation for the ?: operator states that the type of the expression b ? x : y is determined by examining the types of x and y:
If X and Y are the same type, then this is the type of the conditional expression.
Otherwise, if an implicit conversion (Section 6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
Otherwise, if an implicit conversion (Section 6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error occurs.
In your example
int? x = (value.HasValue) ? value.Value : null;
there is no implicit conversion between int and null so the last bullet applies.
The reason is that the type of conditional expression is determined by the second and third operators of the conditional operator (?:) .
Since null has no type, the compiler cannot determine the type of the overall expression so it emits a compiler error.
The reason it works with the simple assignment operator (=) is because the left side of the operator determines the the type. Since in the If statement, the type of x is already known, the compiler doesn't complain.
See section 7.14 (Conditional operator) and section 7.17.1 (Simple assignment) for further explanations.

Why must I cast to (int?) in C# when variable type is [int?]

Can someone please explain me the logic reason why I must cast null to int?
When the left argument type can have their both types ?
Insted of doing
int? k = (DateTime.Now.Ticks%5 > 3 ? 1 : null);
I must do
int? k = (DateTime.Now.Ticks%5 > 3 ? 1 : (int?) null);
although int? k = null is perfectly valid.
An opposite example :
I didn't have to do it in:
string k = (DateTime.Now.Ticks%5 > 3 ? "lala" : null);
int? k = (DateTime.Now.Ticks%5 > 3 ? 1 : (int?) null);
In this case what we have is 1 is int and null is actually null
Now the confusion is the ternary operator is confused as what is the return type int or well null and since its int it won't accept null
So you would need to cast it to a nullable int
Now in the other case you have a string and null is perfectly acceptable for a string
Further explanation can be found at Type inference - Eric
The second and third operands of the ?: operator control the type of
the conditional expression. Let X and Y be the types of the second and
third operands. Then,
If X and Y are the same type, then this is the type of the conditional expression.
Otherwise, if an implicit conversion exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
Otherwise, if an implicit conversion exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error occurs.
Because the compiler doesn't use the type of the variable in the left hand side to determine the type of the expression on the right hand side. First it determines what type the expression is, then it determines if it's possible to put it in the variable.
There is no type close enough that is common between an int and a null value. You have to either make the int value nullable or the null value "intable" for the compiler to find a common ground for the values.
When you have a string and a null value the compiler can simply use one of the types, because a string is already nullable.

C#.net can't assign null to a variable in an inline if statement

I was just wondering why the following code doesn't work (please keep in mind that I set age to be nullable):
myEmployee.age = conditionMet ? someNumber : null;
Yet the following works fine:
if(conditionMet)
{
myEmployee.age = someNumber;
}
else
{
myEmployee.age = null;
}
Why can't I set the value to null in a conditional operator?? All these if statements in my code is not nice.
Thanks.
The types on both sides have to be the same (or be implicitly convertible):
myEmployee.age = conditionMet ? someNumber : (int?)null;
From the docs:
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.
You can, just be clear about the type that null should be interpreted as:
myEmployee.age = conditionMet ? someNumber : (int?)null;
Look at what the documentation has to say:
[MSDN] The default value for a nullable type variable sets HasValue to false. The Value is undefined.
The default is thus null. Which means that you can simply do:
if (conditionMet)
myEmployee.age = someNumber;
There is no need for more obscure code syntax.
If required you can initialize it with null in advance, if it somehow was not the default, like so:
myEmployee.age = null;
if (conditionMet)
myEmployee.age = someNumber;
The types of the parts of the ternary statement must be implicitly castable to a common base. In the case of int and null, they aren't. You can solve this by making age a Nullable<int> (or int?) and casting someNumber to int?.
EDIT to clarify: you can set the value to null with a ternary statement with proper casting. The problem here isn't that you're trying to set a value to null with a ternary statement. It's that the compiler-inferred return types of the two expressions involved in the ternary statement cannot be implicitly casted to a common base type.

Casting with conditional/ternary ("?:") operator

I have this extract of C# source code:
object valueFromDatabase;
decimal result;
valueFromDatabase = DBNull.Value;
result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0);
result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0);
The first result evaluation throws an InvalidCastException whereas the second one does not.
What is the difference between these two?
UPDATE: This question was the subject of my blog on May 27th 2010. Thanks for the great question!
There are a great many very confusing answers here. Let me try to precisely answer your question. Let's simplify this down:
object value = whatever;
bool condition = something;
decimal result = (decimal)(condition ? value : 0);
How does the compiler interpret the last line? The problem faced by the compiler is that the type of the conditional expression must be consistent for both branches; the language rules do not allow you to return object on one branch and int on the other. The choices are object and int. Every int is convertible to object but not every object is convertible to int, so the compiler chooses object. Therefore this is the same as
decimal result = (decimal)(condition ? (object)value : (object)0);
Therefore the zero returned is a boxed int.
You then unbox the int to decimal. It is illegal to unbox a boxed int to decimal. For the reasons why, see my blog article on that subject:
Representation and Identity
Basically, your problem is that you're acting as though the cast to decimal were distributed, like this:
decimal result = condition ? (decimal)value : (decimal)0;
But as we've seen, that is not what
decimal result = (decimal)(condition ? value : 0);
means. That means "make both alternatives into objects and then unbox the resulting object".
The difference is that the compiler can not determine a data type that is a good match between Object and Int32.
You can explicitly cast the int value to object to get the same data type in the second and third operand so that it compiles, but that of couse means that you are boxing and unboxing the value:
result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0);
That will compile, but not run. You have to box a decimal value to unbox as a decimal value:
result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0M);
The type of the operator will be object and in case the result must be 0 it will be implicitly boxed. But 0 literal is by default has int type so you box int. But with explicit cast to decimal you try to unbox it which is not permitted (boxed type must much with the one you cast back to). That is why you can get exception.
Here is an excerpt from C# Specification:
The second and third operands of the ?: operator control the type of the conditional expression. Let X and Y be the types of the second and third operands. Then,
If X and Y are the same type, then this is the type of the conditional expression.
Otherwise, 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.
Otherwise, 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.
Otherwise, no expression type can be determined, and a compile-time error occurs.
Your line should be:
result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m;
0m is the decimal constant for zero
Both parts of a conditional operator should evaluate to the same data type
The x : y part need a common type, the database's value is likely some kind of float and 0 is an int. This happens before the cast to decimal. Try ": 0.0" or ": 0D".
Unless I'm mistaken (which is very possible) its actually the 0 that's causing the exception, and this is down to .NET (crazily) assuming the type of a literal so you need to specify 0m rather than just 0.
See MSDN for more info.
There are two different types for the compiler to decide (at compile time) which one to cast to decimal. This it can't do.
Your answer would work if you combined both:
result = (decimal)(valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0);
At least, a similar situation casting into a parameter for me.

Categories

Resources