I was trying to build a string like the following and I noticed that it gets cut off after using the ?? operator, regardless of whether the previous value is null.
"Some Text" + System.Environment.NewLine +
varOne.ToString() ?? string.Empty + System.Environment.NewLine +
varTwo.ToString()...
All that the string contains (regardless of the values) is up to varOne (Some Text + newline + varOne) unless I remove the ?? operator. After looking around a bit I see that this is not the preferred way of doing this and that I should probably use a stringbuilder instead but I was just curious as to why this happens?
Have a look at ?? Operator (C# Reference)
The ?? operator is called the null-coalescing operator and is used to
define a default value for nullable value types or reference types. It
returns the left-hand operand if the operand is not null; otherwise it
returns the right operand.
This implies, stuff after ?? is only assigned, if stuff before it is null.
So
string sNull = null;
string s = sNull ?? "TADA";
s would be TADA
and
string sNull = null;
string s = sNull ?? "TADA";
string sNormal = s ?? "NOT TADA";
sNormal would also be TADA
Try :
"Some Text" + System.Environment.NewLine +
(varOne == null ? string.Empty : varOne.ToString()) + System.Environment.NewLine +
varTwo.ToString()...
varOne.ToString() is never null. And suppose we have a ?? b + c + d ?? n, if a != null the whole thing eveluates to a and not a + (d ?? n) (because of operators precedence)
The ?? operator means "if the thing before this operator is not null, use it, otherwise use the thing after this operator".
What you might be getting confused by is what constitutes "the thing before this operator". In your case, it's the whole of this:
"Some Text" + System.Environment.NewLine + varOne.ToString()
That's never going to be null. Even if varOne.ToString() returned null (which would be a bug in varOne's class, since ToString() should never return null), the result of concatenating a string with null is the original string. There's absolutely no situation in which the above could result in null.
Now, varOne could be null, and in that case you'd get an exception. It's possible that this is what you think you're guarding against, but it isn't the case. If that's really what you want, you'd need something like this:
"Some Text" + System.Environment.NewLine +
(varOne != null ? varOne.ToString() : string.Empty) + System.Environment.NewLine +
varTwo.ToString()...
This checks that varOne is not null, uses varOne.ToString if it isn't, and falls back to string.Empty if it is.
Note that I've avoided the whole issue of this coding style versus the use of StringBuilder, since that isn't actually part of the question.
Nowadays the answer has changed. You could:
"Some Text" + System.Environment.NewLine +
// no need to coalesce because concatenating nulls are ignored
varOne?.ToString()
System.Environment.NewLine + varTwo.ToString()
or even
$"Some Text{System.Environment.NewLine}{varOne}{System.Environment.NewLine}{varTwo}"
// If you're not writing to a system-dependent file, \r\n will also work fine:
$"Some Text\r\n{varOne}\r\n{varTwo}"
Related
Working with a Legacy Application, ASP.Net, c#.
Trying to append my log4net messages with the SessionWrapper.UserDisplayName
The hiccup is if the sessionwrapper is not defined, i don't want it to bomb, i just want it to treat it as null or empty and I'm trying to avoid writing a multiple lines of code.
Using this as the base for my idea:
banana = null;
string result2 = banana?.prop1 + "something new";
result 2 = something new
Applying that concept to my code:
SessionWrapper?.UserDisplayName + "error message"
I get an error compiling saying:
"SessionWrapper is a type and invalid int the current context"
Any insight is greatly appreciated -
A type is not a value and is therefore never null. If UserDisplayName is a static property of this type, then it might be null; however, it is okay to concatenate null with a string. null will be treated like an empty string.
Simply write
string result = SessionWrapper.UserDisplayName + "error message";
In banana?.prop1 the null-condition-operator is only useful if banana is null. The expression is equivalent to banana == null ? (string)null : banana.prop1.
If you want to treat the case where prop1 could be null, then use the null-coalescing operator ??.
string result2 = (banana.prop1 ?? "<empty>") + "something new";
Of course you can combine the two.
string result2 = (banana?.prop1 ?? "<empty>") + "something new";
Now, both, banana and prop1 can be null. In both cases you will get the result "<empty>something new".
I have this weird error.
On this code, as you can, I have a query on database, return it as DataTable and explicitly declared it null. Now I have a condition to check this if this is null and pass it to a string variable. Everything works fine. I receive an empty string. No exception found.
DataTable dtDateUploaded = _BL.GetRecordByDataTableResults();
dtDateUploaded = null;
string strUploadedDate = dtDateUploaded == null ? string.Empty :
dtDateUploaded.Rows[0].IsNull(0) ? string.Empty :
dtDateUploaded.Rows[0][0].ToString();
But when I used the same condition and pass it directly to a Label control for example (and not use a string variable), I get an object reference error. I wonder why do I get an object reference error if I just used the same code on my string variable above?
LblRecordCount.Text = "Record Uploaded last: " + dtDateUploaded == null ? string.Empty :
dtDateUploaded.Rows[0].IsNull(0) ? string.Empty :
dtDateUploaded.Rows[0][0].ToString();
And the weird part is that this works:
LblRecordCount.Text = "Record Uploaded last: " + strUploadedDate;
You get this error because, in the second example, the + operator has precedence over the == operator so you are trying to concatenate the "Record Uploaded last" string to a null variable.
Instead, in the first example, the = operator has lower precedence than the == operator and no error occurs because the ternary operators are evaluated before the assignment.
You should use brackets to group the ternary operators logic together
LblRecordCount.Text = "Record Uploaded last: " + (dtDateUploaded == null ? string.Empty :
dtDateUploaded.Rows[0].IsNull(0) ? string.Empty :
dtDateUploaded.Rows[0][0].ToString());
See MSDN on C# Operators
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 5 years ago.
Improve this question
I've got code like the following where adding the ?? operator causes last to disappear.
Expected Result: was that if first was null then first would be replaced by "" and that "" would be concatinated with last giving just last as the result.
Actual Result: What happened was the result I got was just first.
var first = "Joe";
var last = "Smith"
var str1 = first + last; // gives "JoeSmith"
var str2 = first ?? "" + last // gives "Joe"
It's a matter of precedence. + binds more tightly than ??, so your code is effectively:
var str2 = first ?? ("" + last)
It sounds like you probably meant:
var str2 = (first ?? "") + last
But there's no point, given that string concatenation with null is equivalent to string concatenation with an empty string - so just use first + last as you already have with str1.
This is the expected behavior of the null coalescing operator ??. It will use the first value, unless it is null in which case it will fall to the second. Consider how this appears with the variables replaced:
var str2 = "Joe" ?? "" + "Smith";
Since "Joe" isn't null, it will set str2 to that and ignore the rest of the expression. If you wanted to only coalesce the first name, then you would need to group it with parentheses to replace any null instances of first with the empty string:
var str2 = (first ?? "") + last;
However, this really doesn't make any difference from your existing code, as your null will simply be an empty string and yield the same thing.
From MSDN:
The ?? operator is called the null-coalescing operator. It returns the left-hand operand if the operand is not null; otherwise it returns the right hand operand.
Since first is not null it returns "Joe"
DateTime? date = null;
string tmp = "a" + "(" + date ?? "blablabla" + ")";
Console.WriteLine(tmp);
This will print something close to: 'a ('.
Is this a bug with null-coalescing operator?
If I put date ?? "blablabla" in parenthesis, it is underlined as error.
Null-coalescing operator ?? has a lower precedence than the + operator, so your code is equal to
string tmp = ("a" + "(" + date) ?? ("blablabla" + ")");
Since everything in the + operation with a string produces a string (by calling .ToString() on all non-string operands), your code will always produce the "a(" string.
First, you should always assume it's your fault, not the compiler's fault; select isn't broken. Do you honestly think the Visual Studio 2010 implementation of the ?? operator hasn't been battle tested? When you encounter something that doesn't match your expectations, check your expectations. Get out the manual, and make sure that you understand exactly what is suppose to happen. In this case, open the language specification.
If you proceed to §1.4 of the specification, you'll see a table that groups operators into precedence groupings; you can also find it online. In particular, the null coalescing operator ?? is near the bottom, above only the lowly conditional ternary operator and assignments and =>. It is below the additive operator. Thus, your statement
string tmp = "a" + "(" + date ?? "blablabla" + ")";
is treated by the compiler as
string tmp = (("a" + "(" + date) ?? ("blablabla" + ")"));
I'm not going to be completely pedantic and also parenthesize the first additive expression1. Since the left-hand-side of the expression in that statement is never null, of course it always assigns "a(" (or "a(" + date.ToString() when date.HasValue is true) to tmp.
The main point is that you had an incorrect expectation as to what should be happening that you should have verified against the manual.
If I put date ?? "blablabla" in parenthesis, it is underlined as error.
Of course it is. Did you even read the error message? It probably tells you that you can't do ?? on a DateTime? and a string because there are no implicit conversions between DateTime? and string in either direction. This, too, is covered in the language specification; see §7.13. You have to read this message and respond to it. To get something semantically equivalent to what you're trying to express, you'll have to resort to the conditional ternary operator:
date.HasValue ? date.ToString() : "blablabla"
and then wrap that whole thing in parentheses because the conditional ternary operator has very low precedence.
Finally, I find a correctly parenthesized version of your code rather ugly, not fun to read, and probably not enjoyable to maintain. Just make it simple, please:
var tmp = String.Format("a({0})",
date.HasValue ? date.ToString() : "blablabla");
Now it is so clear what is going on and what is going to happen. I don't have to think to understand it. Save your thinking for the difficult problems you'll encounter.
1: Be careful. We would need to add in a method call to date.ToString (which has the highest precedence) before attempting to correctly figure out what is evaluated first.
Here just another version:
DateTime? date = null;
string tmp = string.Format("a({0})",
date.HasValue ? date.ToString() : "blablabla");
I really love string.Format instead of concatenation.
"blablabla" is not a date, so you can't use is to set a date?.
string tmp = "a" + "(" + date ?? "blablabla" + ")";
is equivalent to
string tmp = ("a" + "(" + date) ?? ("blablabla" + ")");
which is where your a( is coming from.
However
string tmp = "a" + "(" + (date ?? DateTime.Now) + ")";
or similar should work.
From C# Language Specification §1.4
The following table summarizes C#’s operators, listing the operator
categories in order of precedence from highest to lowest. Operators in
the same category have equal precedence.
It says Additive operators is higher precedence than null coalescing operator.
So, + has higher precedence than ?? and that's why "blablabla" + ")" expression works first.
And since date is null, the statement works like;
string tmp = "a" + "(" + null;
And normally result will be a(
This is most likely because date isn't a string and the ?? operator can't determine a common base type for date and "blablabla". Try (date.ToString() ?? "blablabla"). But this won't satisfy your need either because date.ToString() will never be null.
string tmp = "a" + "(" + (date ?? DateTime.Now) + ")";
should work.
It's not a bug with the null-coalescing operator.
It's operator precedence:
"a" + "(" + date ?? "blablabla" + ")"
is the same as:
("a" + "(" + date.ToString() ) ?? ( "blablabla" + ")" )
and ("a" + "(" + date.ToString() ) is not null.
Trying to figure out how to get the null coalescing operator to work in a foreach loop.
I'm checking to see what a string ends with and based on that, route it to a certain method. Basically what I want to say is....
foreach (String s in strList)
{
if s.EndsWith("d") ?? Method1(s) ?? Method2(s) ?? "Unknown file type";
}
In attempting to do this, of course you get the "Operator ?? cannot be used on type bool and type string." I know there is other ways to do it, just want to see how it can be done with null coalescing.
Have a good weekend.
#Richard Ev: Oh yes of course. Switch, if else, etc. Was just curious how it
could be handled
#Jon Skeet: After reading your comments it hit me, this is just bad! I am
interested in two file extensions basically. If a file ends with "abc" for
instance, send to method 1, if the file ends with "xyz" send to method 2. But
what if a file ends with an extension of "hij"...boom, you're done.
Thanks to Brian and GenericTypeTea as well for the thoughful input
I'm content calling it closed.
It looks like you want to use the normal ternary operator, not null coalescing. Something like:
(s.EndsWith("d") ? Method1(s) : Method2(s)) ?? "Unknown file type";
This is equivalent to:
string result;
if (s.EndsWith("d"))
result = Method1(s);
else
result = Method2(s);
if (result == null)
result = "Unknown file type";
return result;
I think you want a combination of the conditional (ternary) operator and the null coalescing operator:
foreach (String s in strList)
{
string result = (s.EndsWith("d") ? Method1(s) : Method2(s))
?? "Unknown file type";
}
In simple english, this will do the following:
If s ends with d, then it will try Method1.
If s does not end with d then it will try Method2.
Then if the outcome is null, it will use "Unknown file type"
If the outcome is not null, it will use the result of either A or B
I think the compiler gave you the appropriate answer, you can't.
Null coalescing is essentially this if statement:
if(x == null)
DoY();
else
DoZ();
A boolean value cannot be null, so you can't coalesce it like that. I'm not sure what your other methods return, but it seems like you want a simple || operator here.
You should first use the ?? null coalescing operator to guard against a null s reference. Then use the ? ternary operator to choose between Method1 and Method2. Finally use the ?? null coalescing operator again to provide the default value.
foreach (string s in strList)
{
string computed = s;
computed = computed ?? String.Empty;
computed = computed.EndsWith("d") ? Method1(s) : Method2(s);
computed = computed ?? "Unknown file type";
}