Here is what I would like to implement:
public static void foo()
{
// Local function
int? parseAppliedAmountTernary( decimal? d ) {
d.HasValue ? return Convert.ToInt32( Math.Round(d.Value, 0) ) : return null;
}
// The rest of foo...
}
However, I get a compilation error. Is this syntax even possible? I am using Visual Studio 2019, .NET Framework 4, which (currently) equates to C# 7.3.
Note - I am just trying to figure out the syntax... any philosophical discussion regarding the "readability" of the code or other aesthetics, while interesting, are beside the point. :)
Code sample (uses Roslyn 4.0)
https://dotnetfiddle.net/TlDY9c
The ternary operator is not a condition but an expression which evaluates to a single value. It is this value that you have to return:
return d.HasValue ? (int?)Convert.ToInt32(Math.Round(d.Value, 0)) : null;
Note, that before C# 9.0 both the value on the left and the the value on the right of the colon have to be of the same type; therefore, you need to cast the non null value here. Starting with C# 9.0 the type can be infered from the target type.
You should be able to write it like this:
int? parseAppliedAmount(decimal? d) => d.HasValue ? Convert.ToInt32(Math.Round(d.Value, 0)) : null;
Related
I wonder why the following code works in C# 6.0:
(In this example data is a random class containing val as a public string)
if ("x".Equals(data.val?.ToLower()) { }
But the following line isn't:
if (data.val?.ToLower().Equals("x")) { }
Visual Studio shows me the following error:
Cannot implicitly convert type 'bool?' to 'bool'. An explicit conversion exists (are you missing a cast?)
if ("x".Equals(data.val?.ToLower()) { }
Will eventually return a boolean because the of Equals call but this:
if (data.val?.ToLower().Equals("x")) { }
when the expression is evaluated it will return a System.Nullable<bool> which is different than a bool (former is a struct that can be assigned the value null while the latter can only be true or false) the the if expects. Also, in C# a null value doesn't evaluate to false (according to the C# specification).
I don't have c# 6.0 to test but this should work
if (data.val?.ToLower().Equals("x") ?? false) { }
The first statement is evaluating the return value of Equals whereas the second statement evaluates to bool? which could be null.
Further to the other answers I think it's useful if you write the equivalent code out long hand so you can see what's going on.
The first line is equivalent to:
string temp = data.val != null ? data.val.ToLower() : null;
if ("x".Equals(temp)) { }
which is perfectly valid and obvious.
The second statement is equivalent to:
bool? temp = data.val != null ? data.val.ToLower().Equals("x") : null;
if (temp) { } //error
This is perhaps less obvious but the statement data.val?.ToLower().Equals("x") will return null if data.val is null otherwise it will return the result of data.val.ToLower().Equals("x"). Because the statement can return null or bool the type of the entire statement is bool? but you can't use a bool? in an if directly.
As others have pointed out, converting the bool? to a bool will fix your issue. One way of doing that is to use GetValueOrDefault():
if (data.val?.ToLower().Equals("x").GetValueOrDefault()) { }
I have to modify a Visual Studio 2010 C# project that works with Oracle, looking at some code I've found a function that returns a List<> with values loaded from a database stored procedure (cursor)
while (myIDataReader.Read())
{
// OK
myTable.myStringField = myIDataReader["TableStringField"].ToString();
// ¿?
myTable.myIntField =
Convert.ToInt32((myIDataReader["TableIntField"] == DBNull.Value) ? null : myIDataReader["myTableField"]);
}
Everything's ok, just want to know, what conditions are given when assign myTable.myIntField, I know this column is nullable, but I don't get the syntax, if someone can explain I'll be thankful
You are talking about this line?
myTable.myIntField =
Convert.ToInt32((myIDataReader["TableIntField"] == DBNull.Value) ? null : myIDataReader["myTableField"]);
If you break that statement apart, you have a type of conditional, often called the Ternary operator, explained here.
conditional ? if_true_do_this : if_false_do_this
It is really just a different syntax for:
if(conditional) {
if_true_do_this;
} else {
if_false_do_this
}
After that, it simply passes the result to Convert.ToInt32() to turn it from a string into an int.
So that line of code is just a more concise version of this:
if(myIDataReader["TableIntField"] == DBNull.Value)
{
myTable.myIntField = Convert.ToInt32(null);
}
else
{
myTable.myIntField = Convert.ToInt32(myIDataReader["myTableField"]);
}
The syntax above works correctly and it means:
TableIntField could contains a database null.
This is expressed in NET via the DBNull object and its Value field.
The code checks if the TableIntField contains the DBNull.Value via C# ternary operator.
If the checks result in a true condition, a null is used as argument for Convert.ToInt32 and this, according to MSDN documentation, result in a zero value assigned to myTable.myIntField.
It seems the compiler is not going let this syntax fly.
void main()
{
foo(false?0:"");
}
void foo(int i) {return;}
void foo(string s) {return;}
The only other way I can see of fixing this is something as follows:
void bar(object o)
{
if (o is string){//do this}
else{//im an int, do this}
}
Anyone have any better ideas?
You cannot use a method with a void return type in a ternary expression in this way. End of story.
To understand why this is, remember what the ternary operator actually does -- it evaluates to the following:
(condition ? [true value] : [false value])
What this implies is that the following code:
int x = a ? b : c;
Must be rewritable to:
int x;
if (a)
{
x = b;
}
else
{
x = c;
}
The two above are logically identical.
So how would this work with a method with void as its return type?
// Does this make sense?
int x = condition ? foo(s) : foo(i);
// Or this?
if (condition)
{
x = foo(s);
}
else
{
x = foo(i);
}
Clearly, the above is not legal.
That said, others' suggestions would otherwise be valid if only your foo overloads returned a value.
In other words, if your signatures looked like this:
object foo(string s);
object foo(int i);
Then you could do this (you're throwing away the return value, but at least it'll compile):
object o = condition ? foo(0) : foo("");
Anyway, the ol' if/else is your best bet, in this case.
The method call of foo is determined at compile time, so it cannot call a different method (or overload) based on the result of evaluating the condition. Instead, try something like this:
condition ? foo(0) : foo("")
This way, the compiler will succeed in performing overload resolution and will resolve the first call to foo(int) and the second call to foo(string).
EDIT: As noted by other, you cannot use the ?: operator as a statement, nor can you use methods which return void in it. If your actual methods return compatible types, you could do something like:
int result = condition ? foo(0) : foo("");
If not, you must use an if:
if (condition)
foo(0);
else
foo("");
You're example doesn't make a whole lot of sense (the second example doesn't relate to the first).
I think the first example would be fine as:
void main()
{
foo("");
}
Since 0 will never be passed anyway (false is always false) and you can't use the inline conditional operator without an assignment somewhere (which your example is lacking).
As for the second way, that is probably how I would prefer to see it:
void bar(object o)
{
if(o is string) foo(o as string);
else foo((int)o);
}
I wouldn't pass in an object as a parameter. The int will be boxed, which is a little less efficient. Let the compiler figure out which method to call.
If you wrote:
foo(0);
foo("");
The appropriate method would be called. You could also write:
if (condition) {
foo(0);
} else {
foo("");
}
Depending on what you're trying to do (your example is lacking in a little detail).
If you use Inline if expressions in C#, both parts before and after the ":" have to be of the same type. What you are intending would never work.
Even if you like to do something like this:
DateTime? theTime = true ? DateTime.Now : null;
The compiler is not satisfied. In this case you will have to use:
DateTime? theTime = true ? DateTime.Now : default(DateTime?);
The conditional operator needs the true and false part to be of the same type. Which is why it's not compiling.
var x = condition ? 0 : "";
What type should the compiler choose for x? If you really want it to choose object make a cast or you could force it to choose dynamic in which case method overload would still work but you loose type safety. Both are however strong smells.
Having to test the runtime type is usually a design error but with the limited code (that will always have the same result) it's hard to help with a different approach that would require testing on runtime types
This:
foo(false?0:"")
Could be this:
false ? foo(0) : foo("")
Both results of the conditional operator must of the same type (or be implicitly convertible). So foo(false ? 0 : "") won't work because it is trying to return an Int32 and a String. Here's more information on the conditional operator.
The fix I would do is change that line to false ? foo(0) : foo("").
EDIT: Derp, can't use a conditional operator just in the open like that. They can only be used for assignments. You'll have to use a if/else block. Not in one line, but it'll do in a pinch.
I have a bunch of strings I need to use .Trim() on, but they can be null. It would be much more concise if I could do something like:
string endString = startString !?? startString.Trim();
Basically return the part on the right if the part on the left is NOT null, otherwise just return the null value. I just ended up using the ternary operator, but is there anyway to use the null-coalescing operator for this purpose?
You could create an extension method which returns null when it tries to trim the value.
public String TrimIfNotNull(this string item)
{
if(String.IsNullOrEmpty(item))
return item;
else
return item.Trim();
}
Note you can't name it Trim because extension methods can't override instance methods.
Not to spec: Not that I like it, but you could use:
string endString = (startString ?? String.Empty).Trim();
To spec, better as an Extension method like #Kevin's:
string endString = (startString == null ? null : startString.Trim());
string endString = string.IsNullOrEmpty(startString) ? startString : startString.Trim();
Though I've also gone the route of writing a string extension method called "safeTrim" which does what you're describing in one method instead of having to use this recipe every time. Check out Kevin's respone for the code.
EDIT: wow I had it all kinds of backwards, wrongly named variables and reversed ternary operators, all the more reason to write one extension method and code check it better than I did!
Starting with C# 6.0 (.NET Framework 4.6 / Visual Studio 2015) you can use null-conditional member access:
string? endString = startString?.Trim();
Sorry for the necromancy, but I was having this same problem and I solved this using a lambda operation. It isn't the prettiest, but it keeps my code succinct.
It's a shame C# doesn't support static imports or individual function imports, but anyway:
Define this function somewhere:
private static TResult N<TParent,TResult>(TParent parent, Func<TParent,TResult> operation) {
if( parent == null ) return default(TResult);
return operation( parent );
}
Then to use it in your example:
String endString = N(startString, s => s.Trim());
The N function returns null if the first argument is null, otherwise it will evaluate the specified lambda function with the value as the argument.
You can nest it, of course, like so. For example, to safely dereference a long chain, e.g.
String someValue = someObject.SomeProperty.SomeOtherProperty.SomeMethod().SomeFinalProperty;
if any of those properties or methods returns null then you have to insert null checks everywhere, or you could do this:
String someValue = N(N(N(N(someObject, o => o.SomeProperty), o => o.SomeOtherProperty), o => o.SomeMethod()), o => o.SomeFinalProperty);
As I said, it isn't the prettiest :)
You could simplify this by making N an extension method of System.Object, like so:
String someValue = someObject.N( o => o.SomeProperty ).N( o => o.SomeOtherProperty ).N( o => o.SomeMethod() ).N( o => o.SomeFinalProperty );
...which I think is a lot tidier.
Use
string endString = (startString ?? "").Trim();
This uses an empy string if startString is null. This, however, does not return null when endString is null.
Fast-forward to 2021:
10 years later startString?.Trim() is definitely the better option. And this does return null.
The following doesn't propagate null but it accepts null as a parameter and returns an empty string in that case.
using Microsoft.VisualBasic; // you need to add a reference to Microsoft.VisualBasic.dll
...
string endString = Strings.Trim(startString);
...
duck&run...
As as side note, if you're using .NET 4, there's a new convenient method String.IsNullOrWhiteSpace which you can use.
I tried to use the following example codes by using a ? b : c expression:
DateTime? GetValue(string input)
{
DateTime? val = string.IsNullOrEmpty(input) ? null : DateTime.Parse(input);
return val;
}
I got compiling error since in the a ? b : c expression because b and c are different data types; Not sure if I can use (DateTime?) case to c part?
DateTime? val = string.IsNullOrEmpty(input) ? null : (DateTime?) DateTime.Parse(input);
I would rather not use if to split this one into two or three statement.
return string.IsNullOrEmpty(input) ? (DateTime?)null : DateTime.Parse(input);
//or
return string.IsNullOrEmpty(input) ? null : (DateTime?)DateTime.Parse(input);
Either works, you have to provide some means of compatability between the two types, since DateTime cannot be null, you need to explicitly with one that you're trying to go to DateTime?, then the compiler can implicit cast the other.
The compiler is ensuring that b and c in your a ? b: c are of the same type. In your original example c is a DateTime (since DateTime.Parse returns a DateTime), and b can not be a DateTime cause its null, so the compiler says:
Type of conditional expression cannot
be determined because there is no
implicit conversion between ''
and 'System.DateTime'
You can get it to work (Because there is an implicit convertion from DateTime? to DateTime)
DateTime? val = string.IsNullOrEmpty(input) ? (DateTime?)null : DateTime.Parse(input);
But ... I think this is one of those cases where the following is much easier to follow.
DateTime? val = null;
if (!string.IsNullOrEmpty(input)) {
val = DateTime.Parse(input);
}
With the caveat that the whole premise of the function is pretty risky, you are only failing early sometimes.
The method has very odd semantics! It will fail with an exception if an invalid date format is passed in unless it is null or an empty string. This violates the fail early principle.
Did you actually try it? Yes it works. Go grab LINQPad to try little things like this.
LINQPad is more than just a LINQ tool: it's a highly ergonomic code snippet IDE that instantly executes any C#/VB expression, statement block or program – the ultimate in dynamic development. Put an end to those hundreds of Visual Studio Console projects cluttering your source folder!
I just tried
public static DateTime? GetValue(string input)
{
DateTime? val = string.IsNullOrEmpty(input) ? null : (DateTime?)DateTime.Parse(input);
return val;
}
and it worked fine.
Use this:
DateTime? val = string.IsNullOrEmpty(input) ? null : new DateTime?(DateTime.Parse(input));
Edit:
The other answers will work too, but with this syntax you won't even need casting.