It seems that there's some type confusion in the ternary operator. I know that this has been addressed in other SO threads, but it's always been with nullables. Also, for my case I'm really just looking for a better way.
I'd like to be able to use
proc.Parameters[PARAM_ID].Value =
string.IsNullOrEmpty(dest.Id) ? DBNull.Value : dest.Id;
but instead I'm stuck with this:
if (string.IsNullOrEmpty(dest.Id))
{
proc.Parameters[PARAM_ID].Value = DBNull.Value;
}
else
{
proc.Parameters[PARAM_ID].Value = dest.Id;
}
The ternary operator fails because there's no conversion possible between DBNull and string, and as silly as that seems considering Value is object, the compiler kicks it back to me and I'm forced to care. The answer to the nullable version of this question is to just cast null to string and be done with it; DBNull can't be cast to string, though, so no luck there.
Is there a more concise way to do this (without using nullables, by the way?)
Thanks!
You could change your first statement to:
proc.Parameters[PARAM_ID].Value =
string.IsNullOrEmpty(dest.Id) ? (object)DBNull.Value : dest.Id;
The Value property is of type object, so you should cast to object, not string:
proc.Parameters[PARAM_ID].Value = string.IsNullOrEmpty(dest.Id)
? (object)DBNull.Value
: (object)dest.Id;
Or you could add an extension method such as:
public static class DBNullExtensions
{
public static object AsDBNullIfEmpty(this string value)
{
if (String.IsNullOrEmpty(value))
{
return DBNull.Value;
}
return value;
}
}
And then you could just say
proc.Parameters[PARAM_ID].Value = dest.Id.AsDBNullIfEmpty();
(Adapted from Phil Haack)
Readable and concise, no?
What about using the ?? null-coalescing operator More details about ?? operator
proc.Parameters[PARAM_ID].Value = dest.Id ?? (object)DBNull.Value;
Related
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.
How come I get an invalid cast exception when trying to set a NULL value returned from the database inside Comments which is of type Int32.
I am trying to replace this:
try
{
objStreamItem.Comments = (Int32)sqlReader["Comments"];
if (objStreamItem.Comments > 0) {
listComments = Comment.GetAll(objStreamItem.Id);
}
}
catch (InvalidCastException)
{
// Execute if "Comments" returns NULL
listComments = null;
objStreamItem.Comments = 0;
}
With this:
Comments = ((Int32?)sqlReader["Comments"]) ?? 0
The two are in different contexts, but you should get the idea. Instead of using a try catch block I am trying to solve this in a more elegant way.
Thanks.
UPDATE
It is stored as a nullable integer in the database.
public int? Comments
{
get;
set;
}
I just want to thank everyone who answered or posted on this question, everything was very informative. thanks!
Your SQL reader is returning DBNull when the value is null; There's no conversion from DBNull to int?, and the null-coalescing operator doesn't recognize DBNull.Value as something that needs to be coalesced.
EDIT 3
(Another problem with your original code: It will assume that "Comments" returned null if "Comments" is non-null but GetAll() throws an InvalidCastException.)
As hvd points out, you can use the as operator with nullable types:
objStreamItem.Comments = sqlReader["Comments"] as int?;
listComments = (objStreamItem.Comments ?? 0) > 0 ? Comment.GetAll(objStreamItem.ID) : null;
You could save yourself from all of this, however, if you simply defined Comment.GetAll() to return an empty list or a null reference when the ID you pass to it has no comments.
I suppose a ternary expression is the way to go here
objStreamItem.Comments = sqlReader["Comments"] is DBNull ? 0 : (Int32)sqlReader["Comments"] ;
You could also store the return value of sqlReader["Comments"] in a variable first to shorten the expression
var comments = sqlReader["Comments"];
objStreamItem.Comments = comments is DBNull ? 0 : (Int32)comments;
The ?? operator checks for null, but sqlReader["Comments"] won't ever be null. It will either be an Int32, or a DBNull. You can cast null to Int32?, but you cannot do so with DBNull.Value. You can use sqlReader["Comments"] as Int32? instead, which checks if the result can be converted to Int32, and if not, assigns null.
That statement tries to perform the cast before coalescing the values. You need to add parenthesis. Judging by your example above, it also looks like the field is an int rather than an int?:
Comments = (Int32)(sqlReader["Comments"] ?? 0);
In my program, I'm looping through a datatable to get data from each field. One line of my code looks like this:
int LYSMKWh = Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"]);
Basically, I'm just taking the value that's in the cell and converting it to an int32. I run into a problem, though, when the data in the field is a null. I get an Invalid Cast error message about not being able to convert a "DBNull" to another type.
So, I know that I can just check the value of the field before I try to convert it by doing something like this:
if (resultsDT.Rows[currentRow]["LYSMKWh"] == null)
{
resultsDT.Rows[currentRow]["LYSMKWh"] = 0;
}
But, is there a better way to do this? Is there something I can do "inline" while I'm trying to convert the value without having to resort to using the if ?
EDIT: I did try using this suggested method:
int LYSMKWh = Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"] ?? "0");
Unfortunately, I still received the Invalid Cast error message with regards to the DBNull type. It was suggested that I could have problems using the ?? operator if the types on either side were different.
Also, since I have complete control over the data that I'm building my datatable with, I changed it so that no null values would be written to any field. So, that pretty much negates the need to convert a null to an int32, in this case. Thanks for all the advice, everyone!
You could do this:
var LYSMKWh =
resultsDT.Rows[currentRow]["LYSMKWh"].Equals(DBNull.Value)
? 0
: Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"]);
use the ?? operator:
resultsDT.Rows[currentRow][...] ?? "0"
(expecting the field to be a string - if not change the "0")
You may consider using C#'s ?? operator, which checks a value for null and if it's null, assigns a default value to it.
You can use the ?? operator:
object x = null;
int i = (int)(x ?? 0)
You can use a custom convert method:
public static int ConvertToInt32(object value, int defaultValue) {
if (value == null)
return defaultValue;
return Convert.ToInt32(value);
}
You may need overloads that take other types, like string. You may have problems with the ?? operator if the types on either side are different.
You can replace it with a ternary operator:
var rowData = resultsDT.Rows[currentRow]["LYSMKWh"];
int LYSMKWh = rowData != null ? Convert.ToInt32(rowData) : 0;
Alternatively, the ?? operator could work:
int LYSMKWh = Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"] ?? 0);
But I think that's less readable.
You can use extension Method.
int LYSMKWh = resultsDT.Rows[currentRow]["LYSMKWh"].IfNullThenZero();
Create the below class
public static class Converter
{
public static Int32 IfNullThenZero(this object value)
{
if (value == DBNull.Value)
{
return 0;
}
else
{
return Convert.ToInt32(value);
}
}
}
Check if it's DBNull.Value instead of null:
if (resultsDT.Rows[currentRow]["LYSMKWh"] == DBNull.Value)
{
resultsDT.Rows[currentRow]["LYSMKWh"] = 0;
}
The ternary expression would work like this:
var LYSMKwhField = resultsDT.Rows[currentRow]["LYSMKWh"];
int LYSMKWh = LYSMKwhField != DBNull.Value ? Convert.ToInt32(rowData) : 0;
Using dotnet 2.0. Can the following code be improved in style ?
private object GetObj_Version1(int? num)
{
return num ?? (object)DBNull.Value;
}
The cast looks a bit messy to me.
Version2 below avoids the cast, but its long winded :
private object GetObj_Version2(int? num)
{
object numObj;
if (num.HasValue)
numObj = num.Value;
else
numObj = DBNull.Value;
return numObj;
}
Can you think of an alternative which is both short and avoids the cast ? TIA.
The cast, in this case, does nothing at runtime - it is there purely for the compiler. If you really hate it, perhaps:
static readonly object NullObject = DBNull.Value;
private object GetObj_Version1(int? num)
{
return num ?? NullObject;
}
But I'd leave it myself. As an aside - since you are going to box it anyway, you could dispense with the overload, and just work with object - then you don't even need the static field:
private object GetObj_Version1(object value)
{
return value ?? DBNull.Value;
}
If you want to insert values to the database, then you can directly pass the nullable type as a procedure's parameter.
param.Add("#Param1", nullableParam);
I am pulling varchar values out of a DB and want to set the string I am assigning them to as "" if they are null. I'm currently doing it like this:
if (string.IsNullOrEmpty(planRec.approved_by) == true)
this.approved_by = "";
else
this.approved_by = planRec.approved_by.toString();
There seems like there should be a way to do this in a single line something like:
this.approved_by = "" || planRec.approved_by.toString();
However I can't find an optimal way to do this. Is there a better way or is what I have the best way to do it?
Try this:
this.approved_by = IsNullOrEmpty(planRec.approved_by) ? "" : planRec.approved_by.toString();
You can also use the null-coalescing operator as other have said - since no one has given an example that works with your code here is one:
this.approved_by = planRec.approved_by ?? planRec.approved_by.toString();
But this example only works since a possible value for this.approved_by is the same as one of the potential values that you wish to set it to. For all other cases you will need to use the conditional operator as I showed in my first example.
Starting with C# 8.0, you can use the ??= operator to replace the code of the form
if (variable is null)
{
variable = expression;
}
with the following code:
variable ??= expression;
More information is here
You are looking for the C# coalesce operator: ??. This operator takes a left and right argument. If the left hand side of the operator is null or a nullable with no value it will return the right argument. Otherwise it will return the left.
var x = somePossiblyNullValue ?? valueIfNull;
The coalesce operator (??) is what you want, I believe.
My guess is the best you can come up with is
this.approved_by = IsNullOrEmpty(planRec.approved_by) ? string.Empty
: planRec.approved_by.ToString();
Of course since you're hinting at the fact that approved_by is an object (which cannot equal ""), this would be rewritten as
this.approved_by = (planRec.approved_by ?? string.Empty).ToString();
With C#6 there is a slightly shorter way for the case where planRec.approved_by is not a string:
this.approved_by = planRec.approved_by?.ToString() ?? "";
Use the C# coalesce operator: ??
// if Value is not null, newValue = Value else if Value is null newValue is YournullValue
var newValue = Value ?? YourNullReplacement;
To extend #Dave's answer...if planRec.approved_by is already a string
this.approved_by = planRec.approved_by ?? "";
The accepted answer was correct in time, when it was given.
For people still finding this question:
Today you can use the ??= Operator.
e.g:
private string _test = null;
private void InitIfNull(){
_test ??= "Init";
}
To assign a non-empty variable without repeating the actual variable name (and without assigning anything if variable is null!), you can use a little helper method with a Action parameter:
public static void CallIfNonEmpty(string value, Action<string> action)
{
if (!string.IsNullOrEmpty(value))
action(value);
}
And then just use it:
CallIfNonEmpty(this.approved_by, (s) => planRec.approved_by = s);
You can also do it in your query, for instance in sql server, google ISNULL and CASE built-in functions.
I use extention method SelfChk
static class MyExt {
//Self Check
public static void SC(this string you,ref string me)
{
me = me ?? you;
}
}
Then use like
string a = null;
"A".SC(ref a);