Explain behaviour terniary operator and default - c#

Can someone please explain the following behaviour?
I am trying to return null, as int?, if a int.TryParse return false.
From the following options, all return null except the second:
var resultInt1 = int.TryParse(null, out var result) ? result : (int?)default; //null
var resultInt2 = int.TryParse(null, out var result2) ? result2 : default; //0
var resultInt3 = int.TryParse(null, out var result3) ? result3 : (int?)null; //null
var resultInt4 = int.TryParse(null, out var result4) ? result4 : default(int?); //null
isn't default for int? null? Why is it returning 0?

The conditional operator always has to select a best type from the types of the two possible results. In 3 of your samples, you're asking it to select between int and int? and it decides that int? is the best type.
However, when you have just int and default, default doesn't have a type and must be inferred from context. So the only possible type the compiler can choose as the type for the conditional is int. Having made that decision, it now knows that default is the same as default(int) which is 0.

If the string to parse is null, Int32.TryParse will always return false, after setting the default value for the out parameter.
So you get always the false part of your expressions but all the out variables above are set to their default value.
Because Int32.TryParse expects the out variable to be an integer, then, if you look at the value set in the out variables you will see that are all set to 0.
You can see this behavior looking at the referencesource site following the source code of Int32.TryParse

In the second case the type is int, not int?.
int.TryParse works with int not int? so it's the else part that dictates int? in the 1st, 3rd and 4th cases.
It becomes clearer if you 'unwind' the code.
//var resultInt1 = int.TryParse(null, out var result) ? result : (int?)default; //null
int result; // <--- int, not 'int?'
int? resultInt1; // has to be nullable
if (int.TryParse(null, out result))
resultInt1 = result;
else
resultInt1 = (int?)default; //null
// var resultInt2 = int.TryParse(null, out var result2) ? result2 : default; //0
// is equivalent (note 'int', not 'int?'
// int resultInt2 = int.TryParse(null, out var result2) ? result2 : default; //0
// is equivalent
int result2; // <--- int, not 'int?'
int resultInt2; // <------------------- here, it's an int
if (int.TryParse(null, out result2))
resultInt2 = result2;
else
resultInt2 = default; <--‐------ the only case with an 'int', not an 'int?'

Related

Checking if JValue is null

Why this code doesn't run, I want to check if JSON contains integer for key PurchasedValue or not? () :
public PropertyInfo(Newtonsoft.Json.Linq.JToken jToken)
{
this.jToken = jToken;
int PurchasedValue = (int)(jToken["PurchasedValue"].Value ?? 0);
}
the error is :
Error CS0019: Operator `??' cannot be applied to operands of type `method group' and `int' (CS0019)
From my understanding jToken["PurchasedValue"] is a nullable value.
You have to use
int PurchasedValue = (int)(jToken["PurchasedValue"]?? 0);
nullableObj.Value can be only used without error only when there is a value for the nullableObj
Otherwise You can use like
int PurchasedValue = jToken["PurchasedValue"].HasValue?jToken["PurchasedValue"].Value: 0;
This May not even need type casting
You can compare the token type:
var purchasedValueToken = jToken["PurchasedValue"];
int purchasedValue = purchasedValueToken.Type == JTokenType.Null ? 0 : purchasedValueToken.Value<int>();
Well, there are couple of things here :
The jToken["PurchasedValue"] could return anything so a type check would be preferable.
You can change your code as following:
public PropertyInfo(Newtonsoft.Json.Linq.JToken jToken)
{
this.jToken = jToken;
int PurchasedValue = jToken["PurchasedValue"] is int ? jToken["PurchasedValue"] : 0;
}

Static members of nullable types

In c# a value-type cannot have a value of null, however you can enable this by appending a question mark.
e.g.
int intCannotBeNull = 1;
int? intCanBeNull = null;
Additionally, in C# many value types have static members so you can do this for example:
string strValue = "123";
intCannotBeNull = int.Parse(strValue);
However you can't do either of these:
intCanBeNull = int?.Parse(strValue);
intCanBeNull = (int?).Parse(strValue);
C# gets confused. Is there a valid syntax that means strValue could be null or a valid integer value and have the assignment work?
I know there are easy workarounds, for example:
intCanBeNull = (strValue == null) ? null : (int?)int.Parse(strValue);
and other variants of the same thing but that's just messy...
int? is syntactic sugar for Nullable<int>. You are asking about Nullable<int>.Parse. There is no such method. That is where your confusion lies.
Parse will not handle null values, It will throw exception. You have to use either TryParse or Convert class to parse
Convert.ToInt32(int or int?)
This applies for long, float, decimal etc..
int? is in fact Nullable<int>. That Nullable<T> struct doesn't have the methods of the T class. Hence, you have to do it yourself.
In the case of int?, you could try something like this:
string s = "1";
int? result;
if (string.IsNullOrEmpty(s))
{
result = null;
}
else
{
int o; // just a temp variable for the `TryParse` call
if (int.TryParse(s, out o))
{
result = o;
}
else
{
result = null;
}
}
// use result

Built in CastOrDefault?

I get the feeling there is something built in for this. If object is null i'd like the default value (or specifically 0 and I am only using decimal/int). Is there a built in way to write this function?
static int GetDecimalFromObject(object o){
return o==null?0:(decimal)o;
}
Convert.ToDecimal and Convert.ToInt32 will return zero if passed null.
Try to use Convert.ToDecimal()
A decimal number that is equivalent to value, or 0 (zero) if value is
null.
Try like this;
static decimal GetDecimalFromObject(object o)
{
return o == null ? Convert.ToDecimal(0) : Convert.ToDecimal(o);
}
or more efficient;
static decimal GetDecimalFromObject(object o)
{
return Convert.ToDecimal(o);
}
Firstly, the decimal and int data types can't be null, so they would be 0 by default.
If you have a nullable decimal (decimal?) use the HasValue method to check if it is null then assign your default of 0.
The ?? Operator might help, but you have to provide the default value:
// y = x, unless x is null, in which case y = -1.
int y = x ?? -1;
Just to offer a different solution:
static int GetDecimalFromObject(object o)
{
return o as int? ?? 0;
}
Generic version:
static T GetValueFromObject<T>(object o) where T : struct
{
return o as T? ?? default(T);
}
Note that I've used the as operator. If you prefer to throw an exception in case o is not of the right type, use the cast operator.

How can I round a C# double nullable type?

I want to round a double? value so that if its value is 2.3 the result should be 2 but if the input is null then the result should be null.
There is no direct way to round a nullable double. You need to check if the variable has a value: if yes, round it; otherwise, return null.
You need to cast a bit if you do this using the conditional ?: operator:
double? result = myNullableDouble.HasValue
? (double?)Math.Round(myNullableDouble.Value)
: null;
Alternatively:
double? result = null;
if (myNullableDouble.HasValue)
{
result = Math.Round(myNullableDouble.Value);
}
As others have pointed out, it is easy enough to do this as a one-off. To do it in general:
static Func<T?, T?> LiftToNullable<T>(Func<T, T> func) where T : struct
{
return n=> n == null ? (T?) null : (T?) func(n.Value);
}
And now you can say:
var NullableRound = LiftToNullable<double>(Math.Round);
double? d = NullableRound(null);
And hey, now you can do this with any method that takes and returns a value type.
return d.HasValue ? Math.Round(d.Value) : (double?)null;

How does cast in C#/.NET 3.5 work for types with '?' [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Nullable types and the ternary operator. Why won’t this work?
This is my code which works
public decimal? Get()
{
var res = ...
return res.Count() > 0 ? res.First() : (decimal?) null;
}
and this one doesn't work
public decimal? Get()
{
var res = ...
return res.Count() > 0 ? res.First() : null;
}
giving the compiler error:
Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between 'decimal' and '<null>'
I wonder why? any ideas?
This behavior is covered in section 7.13 of the C# language spec.
In short the types of the two expressions in the ternary operator must be * compatible* in order for the compiler to determine the type of the ternary expression. In this case the two types being considered are
decimal
null
The decimal type is a value type and hence null is not convertible to it. The value null has no associated type. This prevents the compiler from determining the type of the expression and leads to a compilation error.
In the first example the second type is decimal?. There is a conversion between decimal and decimal? so the compiler picks decimal? as the type.
The error is pretty clear. Both the "?" en ":" parts of that conditional operator need to have the same type or must at least be implicitly converted to the same Type. And a lone null doesn't have a good Type.
Maybe you could use .FirstOrDefault(). Depending on the type of your res that would give a null or 0m.
Off-topic, but...
Using Count is a bit pointless when all you need to know is whether or not the sequence has any elements. Count will loop through all the elements whereas Any will stop as soon as it hits the first element:
public decimal? Get()
{
var res = ...
return res.Any() ? res.First() : (decimal?)null;
}
Or maybe this:
public decimal? Get()
{
var res = ...
using (var e = res.GetEnumerator())
{
return e.MoveNext() ? e.Current : (decimal?)null;
}
}
I'm guessing res is an array of decimals i.e. decimal[]. This would be because you have declared res like this:
var res = { 1.0, 2.0, 3.0 };
and not like this:
var res = { 1.0, 2.0, null };
so there is no reason for the compiler to think res is an array of nullable decimals.
Now you are using a ternary operator which must always return the same (or an equivalent castable) type from both sides.
But as res.First() is a 'decimal' and your null by default is untyped it just makes your null equivalent to the type of your first argument (res.First() i.e. a decimal). By forcing the null to be typed as a nullable decimal ('decimal?') you are actually forcing the compiler to treat res.First() as a nullable decimal too.
However, a better solution overall for you is this:
public decimal? Get()
{
decimal?[] res = ...
return res.FirstOrDefault();
}
In second case you can use res.FirstOrDefault().
IEnumerable<T>.FirstOrDefault() returns first T in set or default<T> if set is empty: null for classes and some default value for structs, in common case - 0.
public decimal? Get()
{
var res = ...
return res.FirstOrDefault();
}
which is equal to
public decimal? Get()
{
var res = ...
return res.Count() > 0 ? res.First() : default(decimal?);
}

Categories

Resources