Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using C# ternary with String.Equals

Tags:

c#

.net

This works:

short value;
value = 10 > 4 ? 5 : 10;

This works:

short value;
value = "test" == "test" ? 5 : 10;

This doesn't work:

short value;
string str = "test";
value = "test" == str ? 5 : 10;

Neither does this:

short value;
string str = "test";
value = "test".Equals(str) ? 5 : 10;

The last two cases I am getting the following error:

Cannot implicitly convert type 'int' to 'short'.
An explicit conversion exists (are you missing a cast?)

Why do I have to cast on the last two cases and not on the two first cases?

like image 950
Vitor Freitas Avatar asked Aug 01 '14 14:08

Vitor Freitas


People also ask

What is using () in C#?

The using statement causes the object itself to go out of scope as soon as Dispose is called. Within the using block, the object is read-only and can't be modified or reassigned. A variable declared with a using declaration is read-only.

Why do we need using in C#?

CSharp Online Training The using statement is used to set one or more than one resource. These resources are executed and the resource is released. The statement is also used with database operations. The main goal is to manage resources and release all the resources automatically.

How do I start learning C?

Get started with C. Official C documentation - Might be hard to follow and understand for beginners. Visit official C Programming documentation. Write a lot of C programming code - The only way you can learn programming is by writing a lot of code.


3 Answers

short value;
value = 10 > 4 ? 5 : 10;             //1
value = "test" == "test" ? 5 : 10;   //2
string str = "test";
value = "test" == str ? 5 : 10;      //3
value = "test".Equals(str) ? 5 : 10; //4

The last two ternary expressions (3,4) cannot be resolved to a constant at compile time. Thus the compiler treats the 5 and 10 as int literals, and the type of the entire ternary expression is int. To convert from an int to a short requires an explicit cast.

The first two ternary expressions (1,2) can be resolved to a constant at compile time. The constant value is an int, but the compiler knows it fits in a short, and thus does not require any casting.

For fun, try this:

value = "test" == "test" ? 5 : (int)short.MaxValue + 1;
like image 157
Timothy Shields Avatar answered Oct 17 '22 21:10

Timothy Shields


You need a cast to make the two last examples work

value = (short)("test" == str ? 5 : 10);

Why dont't you need it in the first two?

Because the first two are compile-time constants. The compiler is able to translate 10 > 4 ? 5 : 10 to true ? 5 : 10, then to just 5

So when you write

value = 10 > 4 ? 5 : 10;

It's effectively the same as

value = 5;

which compiles because the compiler is allowed to implicitly cast constants if they are in the allowed range.

Conversely, "test" == str ? 5 : 10; is not a compile time constant, so the compile is not allowed to implcitly cast it. You need to make an explicit cast yoursef.

like image 4
Falanwe Avatar answered Oct 17 '22 21:10

Falanwe


This is defined by the C# Language Specification, of course.

The key thing to be aware of is that there are two kinds of conversions from int to short. One is an explicit conversion which always applies but which requires you to write (short) explicitly before the int expression. The other one is an implicit constant expression conversion which only applies when (a) the int expression is a compile-time constant and (b) the value of this compile-time expression is within the range of a short, that is -32768 through 32767.

A literal like 5, 10 or 4 has type int in C# (that goes for any integer literal which is between -2147483648 and 2147483647 and not followed by a symbol L, U or similar). So if we look at the right-hand sides of all your assignments, they are clearly int expressions, not short.

In the case 10 > 4 ? 5 : 10, since 10 and 4 are compile-time constants, this is the same as true ? 5 : 10 because the > operator between ints is built-in and will result in a constant when the operands are constants. And in the same way true ? 5 : 10 gives 5 because all three operands are constants, and ?: is classified a constant itself in that case. So it really says:

short value = 5;

where the "5" is a compile-time constant. Hence it is checked at compile-time if the int 5 is within the range (it does not matter with the 10, it could be 999999), and since that is the case, the implicit constant expression conversion applies, and it is legal.

Note that you can do the same with:

const int huge = 10;
const int tiny = 4;
const int significant = 5;
const int unimporatnt = 10;
short value;
value = huge > tiny ? significant : unimportant;

as long as all the operands are const variables (pun?).

Now, if I managed to make the explanation clear, you will also know by now that the obstacle preventing value = "test" == str ? 5 : 10; from working is that you did not mark the str local as const. Do that, and it will be allowed.

With the Equals call, things are a bit worse. The result of a call to Equals is never considered a compile-time constant (and I don't think it is "optimized" away, for example "same".Equals("same") will actually call the method at run-time). Same thing would happen with (10).Equals(4) or (10).CompareTo(4) > 0 and so on, so strings are not special in this respect.


Most likely you know already that when

short value = cond ? 5 : 10;

is not allowed because cond is not a compile-time constant, you just use the explicit conversion instead, so write:

short value = cond ? (short)5 : (short)10;

or:

short value = (short)(cond ? 5 : 10);

Technically, they are not identical, since the first one has no narrowing conversion at run-time (the expressions (short)5 and (short)10 are literals of type short), while the last one has to convert an int to short at run-time (which is of course cheaper than incredibly cheap).


The other (non-deleted!) answers are correct, this is just bonus information.

like image 3
Jeppe Stig Nielsen Avatar answered Oct 17 '22 20:10

Jeppe Stig Nielsen