I am trying to understand whats happening and why it doesnt work. I was trying to convert "34" to the int 34. I wish to know why there is a difference (and why my version didnt work):
My version:
var cell = GetSpecificString();
return (cell == "") ? null : int.Parse(cell);
SO version:
var cell = GetSpecificString();
if (int.TryParse(cell, out int i)) return i;
return null;
Any tips would be greatly appreciated, as I want to know whats happening here.
Does not work implies here that the Intellisense is complaining because there is no conversion between '' and 'int'. But I want to return a nullable int, so this seems weird to me.
The first version doesn't work because the conditional operator doesn't allow it whereas the second approach uses an if. The rules for the conditional operator are stricter. Both types must be compatible but null and int are not. You could achieve it by using:
return (cell == "") ? (int?)null : int.Parse(cell);
However, i'd really use int.TryParse since an empty string is not the only invalid value:
return int.TryParse(cell, out int parsedValue) ? parsedValue : new int?();
There is already good explanation on stack overflow for this problem:
For complete explanation refer Type of conditional expression cannot be determined because there is no implicit conversion between 'int' and <null>
The spec (§7.14) says that for conditional expression b ? x : y, there are three possibilities, either x and y both have a type and certain good conditions are met, only one of x and y has a type and certain good conditions are met, or a compile-time error occurs.
If only one of x and y has a type, and both x and y are implicitly convertible to that type, then that is the type of the conditional expression.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
This piece of code caught me out today:
clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value]
clientFile.Review month is a byte? and its value, in the failing case, is null.
The expected result type is string.
The exception is in this code
public static implicit operator string(LookupCode<T> code)
{
if (code != null) return code.Description;
throw new InvalidOperationException();
}
The right hand side of the evaluation is being evaluated and then implicitly converted to a string.
But my question is why is the right hand side being evaluated at all when clearly only the left hand side should be evaluated?
(The documentation states that "Only one of the two expressions is evaluated.")
The solution incidentally is to cast the null to string - that works but Resharper tells me that the cast is redundant (and I agree)
Edit: This is different to the "Why do I need to add a cast before it will compile" type ternary operator question. The point here is that no cast is required to make it compile - only to make it work correctly.
You're forgetting that implicit operators are determined at compile-time. That means that the null you have is actually of type LookupCode<T> (due to the way type inference works in a ternary operator), and needs to be cast using the implicit operator to a string; that's what gives you your exception.
void Main()
{
byte? reviewMonth = null;
string result = reviewMonth == null
? null // Exception here, though it's not easy to tell
: new LookupCode<object> { Description = "Hi!" };
result.Dump();
}
class LookupCode<T>
{
public string Description { get; set; }
public static implicit operator string(LookupCode<T> code)
{
if (code != null) return code.Description;
throw new InvalidOperationException();
}
}
The invalid operation doesn't happen on the third operand, it happens on the second - the null (actually a default(LookupCode<object>)) isn't of type string, so the implicit operator is invoked. The implicit operator throws an invalid operation exception.
You can easily see this is true if you use a slightly modified piece of code:
string result = reviewMonth == null
? default(LookupCode<object>)
: "Does this get evaluated?".Dump();
You still get an invalid operation exception, and the third operand isn't evaluated. This is of course painfully obvious in the generated IL: the two operands are two separate branches; there's no way for them both to be executed. And the first branch has another painfully obvious thing:
ldnull
call LookupCode`1.op_Implicit
It's not even hidden anywhere :)
The solution is simple: use an explicitly typed null, default(string). R# is simply wrong - (string)null isn't the same as null in this case, and R# has wrong type inference in this scenario.
Of course, this is all described in the C# specification (14.13 - Conditional operator):
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 (§13.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 (§13.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 case, an implicit conversion exists from LookupCode<T> to string, but not vice versa, so the type LookupCode<T> is preferred to string. The interesting bit is that since this is all done at compile-time, the LHS of the assignment actually makes a difference:
string result = ... // Fails
var result = ... // Works fine, var is of type LookupCode<object>
The question is not about the right argument of the ternary evaluating, it clearnly isn't (Try it, throw a different exception in the implicit operator, the code will still throw an InvalidOperationException because of ((Nullable<byte>)(null)).Value)
So the question is rather about when does the implicit cast happen. It seems that:
clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value]
is equvivalent to
(string)(clientFile.ReviewMonth == null ? (Nullable<LookupCode<byte>>)null : (Nullable<LookupCode<byte>>)MonthNames.AllValues[clientFile.ReviewMonth.Value]);
rather than
clientFile.ReviewMonth == null ? (string)null : (string)MonthNames.AllValues[clientFile.ReviewMonth.Value]);
So resharper is simply wrong here.
So I came across some very baffling behavior of the native ternary operator in C# today. The ternary operator is sending the wrong data type to the method I'm calling. The basic premise is I want to convert the decimal value to an int if decimalEntry == false so it will get stored in a database as an int Here is the code:
decimal? _repGroupResult = 85.00
int? intValue = null;
bool decimalEntry = false;
if (decimalEntry == false)
{
intValue = (int?) _repGroupResult;
}
Console.WriteLine("Sending to ResultAdd via ternary operator");
RepGBParent.ResultAdd(this.RepInfo.ResultID, decimalEntry ? _repGroupResult : intValue);
Console.WriteLine("Sending to ResultAdd via if statement");
// All other tests - just add the rep group result
if (decimalEntry)
{
RepGBParent.ResultAdd(this.RepInfo.ResultID, _repGroupResult);
}
else
{
RepGBParent.ResultAdd(this.RepInfo.ResultID, intValue);
}
The method I'm calling ResultAdd is here:
public void ResultAdd(int pResultID, object pResultValue)
{
if (pResultValue == null) { return; }
Console.WriteLine(this.TestInfo.TestNum + ": " + pResultValue.GetType());
....
}
The ternary operator receives a decimal while the if statement sends an int. As shown by the output code below:
I consider myself a programmer of reasonable talent and this really threw me back today. I played around with it for 2-3 hours and figured out the best way to post it here so I'm clear of the issue I'm having.
Please avoid "why are you even doing it that way" type responses. I simply want to know why there is a difference here between the ternary operator and the if statement.
The only other post that I found that was closely related was this one but it didn't quite match up:
Bizarre ternary operator behavior in debugger on x64 platform
The ternary operator is - well - an operator which is only a special kind of method. And as any other method, it can only have one single return type.
What you try to do is to use the operator to return a decimal? or an int? depending on a condition. That is not possible.
What happens is that the compiler knows that there is an implicit conversion from int? to decimal?, but not the other way round. So it infers the return type of the operator as decimal? and implicitly converts your intValue into a decimal?.
The ternary expression returns a single type, not a type conditional on the result of the evaluation.
Your int is promoted to decimal in order to fullful that requirement.
If the conversion could not be applied, you would get a complier error.
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.
https://msdn.microsoft.com/en-us/library/ty67wk28.aspx
Whats wrong with this
ViewBag.description.Length > 160 ?
ViewBag.description.Substring(0, 160) :
ViewBag.description;
gets this error
An exception of type
'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in
System.Core.dll but was not handled in user code
Additional information: Operator '>' cannot be applied to operands of
type 'string' and 'int'
but its not a string, because im doing the check on length?
The type of exception you are seeing is described on MSDN as:
Represents an error that occurs when a dynamic bind in the C# runtime
binder is processed.
Which indicates that one of your fields is dynamic. The exception details specify the Length > 160 part of your code is causing trouble, and that one of the operands is a string while the other is an int.
Now here's the fun part about dynamic typing; consider I have the following code:
dynamic d = 1;
int e = 1;
var length1 = d.Length;
var length2 = e.Length;
Where both e and d hold the same value, but they aren't of the same type. length1 will compile just fine, and I won't ever figure out that d has no Length member until runtime, whereas length2 won't compile at all because int has no member named Length.
The solution would be to explicitly cast your left and right operands before performing the < operation, in this case, casting ViewBag.description to string, and calling .Length on the result.
Try using explicit casts. In case the description is dynamic (as it seems from your exception), you may need to cast it. If the description field is of type string, you can do this:
( ((string) ViewBag.description).Length > 160)
? ViewBag.description.Substring(0, 160)
: (string) ViewBag.description;
I'd do this to make it more readable:
var description = (string) ViewBag.description;
ViewBag.Meta = "Text " + (description.Length > 160
? description.Substring(0, 160)
: description);
Update.
As per #Dale Fraser's comment I added the full assignment according to his code.
I suppose the compiler gets confused when the chunk "Text " + description.Length is encountered as it is unable to determine the latter is part of a ternary expression - perhaps it is confused by the dynamic. This could explain the compiler error you get.
The round brackets should fix this, as they will enforce higher priority of evaluation on the ternary operator. The plus operator then will know it is performing string concatenation, as the right operand will be the evaluated ternary operator (which should be a string).
Can you try this :
ViewBag.Meta = Convert.ToInt32(ViewBag.description.Length) > 160 ? ViewBag.description.Substring(0, 160) : ViewBag.description;
I've noticed that some .NET structs can be compared to null.
For example:
TimeSpan y = new TimeSpan();
if (y == null)
return;
will compile just fine (the same with the Guid struct).
Now I know that stucts are value type and that the code above should not compile, unless there's an overload of operator == which takes an object. But, as far as I could tell there isn't.
I've looked at the class with Reflector, and also at the docs on MSDN.
The two of them do implement the following interfaces:
IComparable, IComparable<T>, IEquatable<T>
but, trying to implment the same Interfaces did not seem to help:
struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
public int CompareTo(Object obj) {
return 0;
}
public int CompareTo (XX other){
return 0;
}
public bool Equals (XX other){
return false;
}
public override bool Equals(object value){
return false;
}
public static int Compare(XX t1, XX t2){
return 0;
}
}
I'm using: .NET 2.0 Visual Studio 2005.
Does anyone has any idea what's the reason for this ?
I am just trying to get a better understanding. This isn't an issue as I know I shouldn't compare structs to null anyway.
It's the == operator.
The TimeSpan class has an overload of the equality operator:
public static bool operator ==(DateTime d1, DateTime d2)
{
return (t1._ticks == t2._ticks);
}
This in itself doesn't make it possible to compare with null, but...
With the arrival of nullable types, each struct is implicitly convertible to its nullable type, so when you see something like
TimeSpan y = new TimeSpan();
if (y == null)
return;
You don't see that this is happening:
TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
return;
Null gets the implicit conversion (implicit assignment?), but not all System.Object objects do:
TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
return;
Okay, but the equality operator doesn't take nullable arguments, does it?
Well, msdn is of help here, stating:
The predefined unary and binary
operators and any user-defined
operators that exist for value types
may also be used by nullable types.
These operators produce a null value
if [any of] the operands are null; otherwise,
the operator uses the contained value
to calculate the result.
So you effectively get a nullable implementation for each operator for free, with a fixed defined behaviour. The "contained value" mentioned above is the actual value the non-nullable operator would return.
This problem was effectively introduced when nullable types were included. There's an implicit conversion from TimeSpan to TimeSpan?, and there's a comparison between TimeSpan? and the null value of that type.
The compiler issues a warning for some types which makes it clearer what it's trying to do:
int x = 10;
if (x == null)
{
Console.WriteLine();
}
Gives this warning:
Test.cs(9,13): warning CS0472: The result of the expression is always 'false'
since a value of type 'int' is never equal to 'null' of type 'int?'
I believe Marc Gravell and I worked out the circumstances under which the warning is given once... it's a shame it's not consistent.
This case is covered for generics in section 7.9.6 of the C# language specification.
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.
I dug through the spec for a bit and couldn't find a more general rule. Jon's answer indicates it's a nullable promotion issue.
This rule (or a similar variation) does seem to be being applied here. If you look at the reflected output closely you'll notice the comparison isn't there. The C# compiler is apparently optimizing this comparison away and replacing it with false.
For instance, if you type the following
var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);
Then decompile it you'll see the following
var x = new TimeSpan();
var y = false;
Console.WriteLine(x);
See also: C# 3 (.NET 3.5) version of csc fails to report CS0162 for unrechable code (struct/null)
Starting with the C# 3 compiler that means it sometimes doesn't even warn you about this ;-p
Because Guid / TimeSpan etc provide ==, they fall into this trap where it doesn't warn you.
I FOUND IT :)
The following gives a warning:
int i = 0;
if (i == null)
// ^^ Warning: The result of the expression is always 'false' since a value of
// type 'int' is never equal to 'null' of type 'int?'
The compiler is just failing to emit the correct warning that the null you typed was converted to type TimeSpan? for the comparison.
Edit: The related section in the spec is §13.7.1 stating that null can be implicitly converted to any nullable type, and (the very difficult to read) section §13.7.2 stating a value type T can be implicitly converted to T?.
What I originally wrote:
Whatever's happening is something in the C# spec because like JaredPar says it compiles to simply false.
Note that this doesn't compile:
TimeSpan ts = new TimeSpan();
object o = null;
if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object'
...
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.