Ternary Operator Unexpected Result - c#

Can someone comment on the issue I'm having. A ternary operator throws an error and the argument here is that if it evaluates to null then it should ignore the part after the colon. The Watch set up for this indicates an exception:
Int32.Parse(SQLDataReader["TrayId"].ToString())' threw an exception of Type 'System.FormatException
suggesting that it can't convert a null to a string. Is this how it works?
ShipmentId = SQLDataReader["ShipmentId"] == DBNull.Value ? 0 : Int32.Parse(SQLDataReader["ShipmentId"].ToString()),

The column ShipmentId is an integer, but it is also nullable, which means that the c# type is int?.
The error boils down to this code:
Int32.Parse((string)null)
The parsing gives up because you can't turn null into an int.
To fix this, use TryParse.
int ShipmentId = 0;
int.TryParse(SQLDataReader["ShipmentId"].ToString(), out ShipmentId);
This will cover if the value is null, or if for some strange reason the value can't actually be converted to an int (like "asdf121343fds").

It's recommended that for comparisons with DBNull, you use the DBNull.Value.Equals method, as described on this page: http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx
ShipmentId = DBNull.Value.Equals(SQLDataReader["ShipmentId"]) ? 0 : Int32.Parse(SQLDataReader["ShipmentId"].ToString());

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.

How to send in "NULL/DbNull" into a table-column with the datatype: nullable int

I got a table in sql-server 2012 with a column that is the datatype int but also nullable.
I've a textbox that when left empty should insert NULL into the nullable int cell. But I'm having trouble with what type to send in to be translated as null. What I want is a datatype of int but also null (int?) to send into the database.
var tbl_row = db.table.Where(n => n.key.Equals(key)).SingleOrDefault();
tbl_row.nullable_int = this.tb.Text == "" ? [null int value] : int.Parse(this.tb.Text);
You should write:
tbl_row.nullable_int = this.tb.Text == "" ? (int?)null : int.Parse(this.tb.Text);
for the conditional operator expression to be valid.
According to the documentation:
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.
In your case, null and an expression evaluated as an int confused the compiler. By explicitly casting the first expression to int?, there is now a way for it to figure out how to evaluate the conditional expression.
There is a singleton instance of a DBNull class. Try assingning DBNull.Value

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.

Type result with conditional operator in C#

I am trying to use the conditional operator, but I am getting hung up on the type it thinks the result should be.
Below is an example that I have contrived to show the issue I am having:
class Program
{
public static void OutputDateTime(DateTime? datetime)
{
Console.WriteLine(datetime);
}
public static bool IsDateTimeHappy(DateTime datetime)
{
if (DateTime.Compare(datetime, DateTime.Parse("1/1")) == 0)
return true;
return false;
}
static void Main(string[] args)
{
DateTime myDateTime = DateTime.Now;
OutputDateTime(IsDateTimeHappy(myDateTime) ? null : myDateTime);
Console.ReadLine(); ^
} |
} |
// This line has the compile issue ---------------+
On the line indicated above, I get the following compile error:
Type of conditional expression cannot be determined because there is no implicit conversion between '< null >' and 'System.DateTime'
I am confused because the parameter is a nullable type (DateTime?). Why does it need to convert at all? If it is null then use that, if it is a date time then use that.
I was under the impression that:
condition ? first_expression : second_expression;
was the same as:
if (condition)
first_expression;
else
second_expression;
Clearly this is not the case. What is the reasoning behind this?
(NOTE: I know that if I make "myDateTime" a nullable DateTime then it will work. But why does it need it?
As I stated earlier this is a contrived example. In my real example "myDateTime" is a data mapped value that cannot be made nullable.)
The compiler does not infer the type of the result of the conditional operator from the usage of the result, but from the types of its arguments. The compiler fails when it sees this expression because it cannot deduce the type of the result:
IsDateTimeHappy(myDateTime) ? null : myDateTime;
Since null and DateTime are not compatible, you need to tell the compiler what the type should be. A cast should do the trick:
DateTime? x = IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime;
OutputDateTime(x);
Now the compiler will have no problems. You can also write the above on one line if you prefer (but I would probably not do this):
OutputDateTime(IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime);
Eric Lippert has a good answer that is also relevant here and goes into more details about how the compiler determines types.
The reason is the ternary operator expects both the operands to be of the same type. The whole operator get worked out BEFORE it is assigned to a result (in this case passed into a function), so the compiler can't know what the result type is.
IsDateTimeHappy(myDateTime) ? null : myDateTime
In the above case there is no conversion path between null and DateTime. As soon as you cast one of them to DateTime?, the compiler can convert the other one:
IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime
//OR
IsDateTimeHappy(myDateTime) ? null : (DateTime?)myDateTime
The fist line of code above works because the compiler can convert DateTime to DateTime? via an implicit conversion operator:
//In Nullable<T>
public static implicit operator T?(T value);
The second line works because null can be assigned to DateTime? since the latter is a reference type.
The implicit conversion is not allowed by the return statement. If you had
if (condition)
return first_expression;
else
return second_expression;
Then you'd be comparing apples to apples. And you'd have no problems - as you stated.
In your case, you're allocated so much space on the stack for a DateTime - which is a non-nullable value type. So you're making a statement that doesn't make any sense to the compiler. If you say, I'm going to pass you an A or a B, then the A and the B need to be the same thing. In your case, the B can never be an A.
What the compiler is saying is:
If IsDateTimeHappy(myDateTime) is false, then I need to return a value of type DateTime equal to myDateTime. If it is true, then I need to return a value equal to null, but you haven't told me what type it should be!
That's why Mark's answer is the solution. After you provide a cast telling the compiler what type of value will be returned if the condition is true, it can check whether the true and false return values can be converted to (or are of) the same type.
Cheers Mark! ;-)
Instead of null use default(DateTime?) and then both sides of the ternary will have compatible types.

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