Ternary Operator String Length error - c#

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;

Related

Parsing a string to an int difference

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.

Why default does not work with double parentheses?

Why this compiles:
return default(T);
but this does not:
return default((T));
The full method is
public static T PenultimateOrDefault<T>(this IEnumerable<T> items)
{
if (items.Count() >= 2)
return items.ElementAt(items.Count() - 2);
else
return default(T);
}
The errors for default((T)) are
; expected
Invalid expression term ')'
Type expected
So it looks that parser is confued by double parentheses.
Well, that's just not how the language is specified.
It's important to understand that default is like typeof - they're operators1, not method calls. It's not like the name of the type is an argument - it's an operand, and the operand is expected to just be the type name.
Section 7.6.13 of the C# 5 spec shows the construction of a default value expression:
default-value-expression:
default ( type )
where type is just the name of a type or type parameter. You can no more put parentheses round it here as you could when using a generic argument, or declaring a variable:
(string) x = ""; // Invalid
var x = new List<(string)>(); // Invalid
1 It's never specified as "the default operator" in the spec; always "default value expression" - but it really is an operator in all meaningful senses, and it's listed with the other operators in the precedence table.
This is because (AnyType) (or in your case (T)) is expected to be a cast, so the parser gets confused about this and throws errors.
Why this compiles:
return default(T);
Because this is syntactically correct.
but this does not:
return default((T));
Because this is not syntactically correct.

Ternary Operator Unexpected Result

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());

Getting Error Msg - Cannot implicitly convert type 'string' to 'bool'

I am checking for values in a textbox to trigger a conditional statement, but am getting error messages.
if (txtAge.Text = "49") || (txtAge.Text = "59")
{
txtNote.Text = "A valid picture ID must be submitted";
}
The error message I am getting is Cannot implicitly convert type 'string' to 'bool'
How can I resolve this?
In the if statement replace = by ==.
You're using the assignment operator instead of the equals comparison.
The syntax for the if statement is also not correct. Check if-else (C# Reference).
When you type this:
if (txtAge.Text = "49")
This basically is assigning "49" to txtAge.Text, and then returning that value as a string (equal to "49").
This is the same, essentially, as doing:
string tmp = txtAge.Text = "49";
if (tmp) { //...
However, you cannot do "if (stringExpression)", since an if statement only works on a boolean. You most likely wanted to type:
if (txtAge.Text == "49" || txtAge.Text == "59")
{
you cannot use "=" to compare strings. in this case you could use txtAge.Text.comparedTo("49") == 0 and so on
Need to use == instead of =. Former is used for comparison while the later is for assignment.
A better way is to use Equals method
if (txtAge.Text.Equals("49") || txtAge.Text.Equals("59"))
{
}
You are missing ='s. Also, you may need another set of Brackets around the whole if statement.

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