C# Safe navigation operator - what is actually going on? - c#

I've been following the safe navigation operator feature added in C#6 with some interest. I've been looking forward to it for a while. But I'm finding some different behavior than I expected. I'm realizing I really don't understand how it actually works.
Given this class
class Foo {
public int? Measure;
}
Here's some code using the new operator.
Foo f = new Foo { Measure = 3 };
Console.WriteLine(f?.Measure); // 3
f = new Foo { Measure = null };
Console.WriteLine(f?.Measure); // null
f = null;
Console.WriteLine(f?.Measure); // null
Up to here, everything's working as expected. ?. is accessing members when the left hand side is not null, otherwise returning null. But here things go in a direction I wasn't expecting.
var i = f?.Measure; // i is Nullable<int>
Console.WriteLine(i.HasValue); // false
Console.WriteLine(f?.Measure.HasValue); // null
What?
Why can I get HasValue from i, but not from the same expression I assigned to i? How can HasValue ever be null?
Edit: My real question is about program behavior, not a compilation error. I removed the extra stuff about compilation, and focused this question more narrowly on why two different results are returned by what seems like the same logic.

Let's walk through this logically.
var f = ???;
var i = f?.Measure;
var t = i.HasValue;
We don't know if f is null or not.
If f is null, then the result (i) is null
If f is not null, then the result (i) is an int
Therefore, i is defined as int?, and t is a bool
Now, let's walk through this:
var f = ???;
var i = f?.Measure.HasValue;
If f is null, then the result (i) is null
If f is not null, then the result (i) is Measure.HasValue, which is a bool.
Therefore, i is a bool?.
If f is null, we short-circuit and return null. If it's not, we return the bool result of .HasValue.
Essentially, when using ?. - the return type must be a reference value, or a Nullable<T>, as the expression can short circuit to return null.

var i = f?.Measure; // i is Nullable<int>
Console.WriteLine(i.HasValue); // false
Console.WriteLine(f?.Measure.HasValue); // null
In this case, f is null.
The reason why i.HasValue returned false is because i is of type Nullable<int>. So even when the value of i is null, like in this case, i.HasValue is still accessible.
However, f?.Measure.HasValue immediately returns null after f? is evaluated. Hence the result you see above.
Just to quote Rob's comment:
The main thing to realise is that you're reading and understanding
this: f?.Measure.HasValue as this: (f?.Measure).HasValue, which it's
not.

Nullable<T> is actually a struct and therefore cannot be null, only its Value can, so HasValue will always be accessible.

I ran into this today.
What does the following C# snippet print?
public class NullTests
{
public static void Main(string[] args)
{
object obj = DoIt();
Console.WriteLine(obj?.ToString().NullToNothing());
}
private static object DoIt() => null;
}
public static class Extensions
{
public static string NullToNothing(this string input) => input ?? "nothing";
}
Answer: null.
What does the following Kotlin snippet print?
fun main() {
val obj = doIt()
println(obj?.toString().NullToNothing())
}
fun doIt() = null
fun String?.NullToNothing() = this ?: "nothing"
Answer: "nothing".
Like you, I was expecting the Kotlin behaviour, and it tripped me up for the better part of the day. :(

Related

Incorrect compiler error usage of uninitialized variable

I just encountered a case where Visual Studio tells me my code might result in accessing an uninitialized variable, but the rules of boolean logic dictate this isn't possible here.
I simplified the code to visualize the problem, in this case appearing for x:
static void Main(string[] args)
{
IDictionary<String, MyType> dictionary = null;
if (new Random().Next() % 2 == 0)
{
dictionary = new Dictionary<string, MyType>();
dictionary.Add("xyz", MyType.Whatever);
dictionary.Add("abc", MyType.DontCare);
}
var myType = dictionary?.TryGetValue("abc", out var x) ?? false ? x : MyType.None;
}
enum MyType
{
None,
Whatever,
DontCare
}
In case dictionary isn't initialized it's null, thus resulting in ?? evaluating the ternary operator to false. This is the only case where x won't be initialized, but still I get the error message for x in the true case of the ternary operator.
Remarks: Setting parenthesis doesn't change anything, and resolving the ternary operator to a full-fledged if doesn't either.
Is this a known behavior or a bug?
EDIT: Just to clarify, I DO know how to fix the problem. I was just slightly surprised to encounter this error, since the described problem, accessing the uninitialized x, will never happen according to boolean logic.
EDIT: Using a full if doesn't change the problem:
static void Main(string[] args)
{
...
MyType myType;
if (dictionary?.TryGetValue("abc", out var x) ?? false)
{
myType = x; // <-- error still occurs here
}
else
{
myType = MyType.None;
}
}
The problem is the x in this line:
var myType = dictionary?.TryGetValue("abc", out var x) ?? false ? x : MyType.None;
Since you have a null-propagation operator (?.), x will never be assigned when dictionary is null. You have to take out x as a separate variable above the var with a default value.
MyType x = MyType.None;
var myType = dictionary?.TryGetValue("abc", out x) ?? false ? x : MyType.None;
The static compiler just isn't smart enough to figure out the null-propagation operator will cause that expression always to return false (and hence x is never used). Not it just treats the statement as if it can.
As you can see on the C# language Github page, there is an active feature request to let the compiler figure out the outcome of the null-propagation operator.

Use "as" operator to test integer type in Page.RouteData.Values

In my code I have registered route in global.asax like the following:
void RegisterRoutes(RouteCollection routes)
{
routes.MapPageRoute(
"product",
"product/{id}",
"~/index.aspx"
);
then in my index.aspx page, I want to test the {id} value whether it is integer:
if (Page.RouteData.Values["id"] as int? != null)
//always evaluate as null even id is an integer in the URL, ex: /product/1
I understand I can use some other methods (such as int.TryParse or this ) to achieve the same goal, but would using a "as" make sense in this case?
The way to go depends on what is really stored in Values["id"]. If it is in fact an int, you can test for int with Values["id"] is int. The Values[] property is typed as object. Therefore it can be null. But this does NOT mean, that the stored int is a Nullable<int>. (see my update below.)
If you assign an int (and all other non-reference types) to an object variable or property, it will be boxed. I.e. it will be packed into an object of the corresponding type. int or System.Int32 exists as value type as well as a boxed and immutable System.Int32 reference type. Because of its immutability, you don't notice that it is a reference type. An int can be boxed with object obj = 5; and unboxed with int i = (int)obj;.
On the other hand, if the id was stored as string then you would have to convert it to a number
int id;
if (Int32.TryParse((string)Page.RouteData.Values["id"], out id)) {
// use id here
}
UPDATE:
There seems to be some confusion with null and Nullable<T>.
int? i; // Same as Nullable<int> or Nullable<System.Int32> or System.Int32?
object obj;
bool test;
i = null;
test = i.HasValue; // ==> false
test = i == null; // ==> true
// Now comes the strange part
obj = 5; // The int is boxed here.
// Debugger shows type of obj as "object {int}"
test = obj is int; // ==> true
test = obj is int?; // ==> true
int x = (int)obj; // ==> 5, the int is unboxed here.
// But
obj = null;
// Debugger shows type of obj as "object"
test = obj is int; // ==> false
test = obj is int?; // ==> false
test = obj == null; // ==> true
// And
i = 5;
obj = i; // i is a Nullable<int>
// Debugger shows type of obj as "object {int}"
test = obj is int; // ==> true
test = obj is int?; // ==> true
test = obj == null; // ==> false
i = null;
obj = i;
// Debugger shows type of obj as "object"
test = obj is int; // ==> false
test = obj is int?; // ==> false
test = obj == null; // ==> true
Both obj is int and obj is int? return true if obj contains an int number but both return false if obj contains null! A null Nullable<int> is converted to (object)null when asssigned to obj and is no more Nullable<int>.
The debugger shows the type of obj as object {int} when there is a number stored, no matter whether an int or an int? was assigned! The type of obj is object when null is stored.
So Chuong Le is in fact right!
I would recommend that you use Int.TryParse, because the value is most likely going to be a string. Don't use the Int.Parse because you will also have to handle the exception and you generally want to avoid coding things that could throw exceptions (even if you're handling them).
Using as is not really a good solution since it forces you to use nullable int; if you take the is route, then it's almost guaranteed not to work when the passed in value is a string type.
Regarding the is debate, I ran this in VS:
string value = "42";
object answerToUniverse = value;
if (answerToUniverse is int)
{
Console.WriteLine("It's an integer!");
}
else
{
Console.WriteLine("It's NOT an integer!");
}
The result was: It's NOT an integer!
Looks like you want to check the value rather than the type meaning you don't really care what the type is.
In such case, this is the best approach I can recommend:
object idValue = Page.RouteData.Values["id"];
int dummy;
if (idValue != null && Int32.TryParse(idValue.ToString(), out dummy))
{
//value is integer!
}
This way it doesn't matter what the type is; if it's integer, double, string or even complex type with proper ToString() override, it will work.
as is a safe cast. It will try and cast the provided value to the desired type, returning null instead of throwing an exception when a standard cast would.
Therefore unless Page.RouteData.Values["id"] was an integer object this would not work and you need to user the alternative methods you suggested.
One was could be to use an extension method
public static class IntExtensions {
public static int? AsInt(this string source) {
int result;
return (int.TryParse(source,out result)) ? (int?)null : result;
}
public static bool IsInt(this string source) {
return source.AsInt.HasValue;
}
}
Allowing you to do
if (Page.RouteData.Values["id"].IsInt())
Perhaps you actually want to do this,
var idString = Page.RouteData.Values["id"] as string;
int id;
if (int.TryParse(idString, out id))
{
// id is now a valid number
}
else
{
// your types are confused
}

C# - checking if a variable is initialized

I want to check if a variable is initialized at run time, programmatically. To make the reasons for this less mysterious, please see the following incomplete code:
string s;
if (someCondition) s = someValue;
if (someOtherCondition) s = someOtherValue;
bool sIsUninitialized = /* assign value correctly */;
if (!sIsUninitialized) Console.WriteLine(s) else throw new Exception("Please initialize s.");
And complete the relevant bit.
One hacky solution is to initialize s with a default value:
string s = "zanzibar";
And then check if it changed:
bool sIsUninitialized = s == "zanzibar";
However, what if someValue or someOtherValue happen to be "zanzibar" as well? Then I have a bug. Any better way?
Code won't even compile if the compiler knows a variable hasn't been initialized.
string s;
if (condition) s = "test";
// compiler error here: use of unassigned local variable 's'
if (s == null) Console.Writeline("uninitialized");
In other cases you could use the default keyword if a variable may not have been initialized. For example, in the following case:
class X
{
private string s;
public void Y()
{
Console.WriteLine(s == default(string)); // this evaluates to true
}
}
The documentation states that default(T) will give null for reference types, and 0 for value types. So as pointed out in the comments, this is really just the same as checking for null.
This all obscures the fact that you should really initialize variables, to null or whatever, when they are first declared.
With C# 2.0, you have the Nullable operator that allows you to set an initial value of null for heretofore value types, allowing for such things as:
int? x = null;
if (x.HasValue)
{
Console.WriteLine("Value for x: " + num.Value);
}
Which yields:
"Value for x: Null".
Just assign it null by default, not a string value
Here's one way:
string s;
if (someCondition) { s = someValue; }
else if (someOtherCondition) { s = someOtherValue; }
else { throw new Exception("Please initialize s."); }
Console.WriteLine(s)
This might be preferable for checking if the string is null, because maybe someValue is a method that can sometimes return null. In other words, maybe null is a legitimate value to initialize the string to.
Personally I like this better than an isInitialized flag. Why introduce an extra flag variable unless you have to? I don't think it is more readable.
You can keep a separate flag that indicates that the string has been initialized:
string s = null;
bool init = false;
if (conditionOne) {
s = someValueOne;
init = true;
}
if (conditionTwo) {
s = someValueTwo;
init = true;
}
if (!init) {
...
}
This will take care of situations when s is assigned, including the cases when it is assigned null, empty string, or "zanzibar".
Another solution is to make a static string to denote "uninitialized" value, and use Object.ReferenceEquals instead of == to check if it has changed. However, the bool variable approach expresses your intent a lot more explicitly.
I would agree with Vytalyi that a default value of null should be used when possible, however, not all types (like int) are nullable. You could allocate the variable as a nullable type as explained by David W, but this could break a lot of code in a large codebase due to having to refine the nullable type to its primitive type before access.
This generic method extension should help for those who deal with large codebases where major design decisions were already made by a predecessor:
public static bool IsDefault<T>(this T value)
=> ((object) value == (object) default(T));
If you are staring from scratch, just take advantage of nullable types and initialize it as null; that C# feature was implemented for a reason.
I pick initialization values that can never be used, typical values include String.Empty, null, -1, and a 256 character random string generator .
In general, assign the default to be null or String.Empty. For situations where you cannot use those "empty" values, define a constant to represent your application-specific uninitialized value:
const string UninitializedString = "zanzibar";
Then reference that value whenever you want to initialize or test for initialization:
string foo = UnininitializedString;
if (foo == UninitiaizedString) {
// Do something
}
Remember that strings are immutable constants in C# so there is really only one instance of UninitializedString (which is why the comparison works).

C# IsNullOrZero

This pattern comes up very frequently in my code:
x= x== 0? 1: x;
//or
x= x==null? 1: x;
However it happens that sometimes x is a long expression and I'd have to use intermediate variables. That's just useless boilerplate code. I can cook up a method and call it instead:
Util.IfNullOrZero(x, 1);
But that's just ugly. What is the best way of expressing the pattern? In ruby there is such syntax for when x is nil which gets rid of redundant x's:
x||= 1
I could extend object in a manner
public static class wtf
{
public static T Default<T>(this object o, T d)
{
return o == null ? d : new object[] { o }.Cast<T>().First();
}
}
And then do
object param= null;
int x= param.Default(1);
But that's a bit expensive.
In short how to best make C# do x||= 1 like in ruby?
Update
This is what I cooked up. I'm currently looking for a faster way of using the Template parameter to convert object to T.
public static class MyExtensions
{
public static T d<T>(this object o, T d)
{
return o == null || o.Equals(default(T)) ? d : new object[] { o }.Cast<T>().First();
}
}
In fact the code does three things at once: Casts to default type, checks for default value and also checks for null.
Update 2
return o == null || o.Equals(default(T)) ? d : (T)o; // much simpler and faster
I still think it is a commonality which needs to be included in core language.
Update 3
This is what I finally wrote, taking into account DataTable DBNull types.
public static T d<T>(this object o, T d)
{
return o == null || (o is System.DBNull) || o.Equals(default(T)) ? d : (T)Convert.ChangeType(o, typeof(T));
}
For handling the "==null" case, the null coalesce operator does the trick.
y = x ?? z;
means
if (x == null)
y = z;
else
y = x;
I'm not aware of something that check for both zero and null, writing a method to perform this task might be the best solution. Here it goes:
public static T IsNullOrZero<T>(this T variable, T defaultValue)
{
// defaultValue can't be null, doesn't make sense
if (defaultValue == null) throw new ArgumentException("default value can't be null", "defaultValue");
if (variable == null || variable.Equals(default(T)))
return defaultValue;
return variable;
}
Usage:
x = x.IsNullOrZero(y);
Note: this in fact works on non-numbers too (name might be misleading if dealing with non-numbers... maybe something along the lines of IsNullOrDefault might be a better name).
You can check like
public static bool IsNullOrValue(this int? value, int valueToCheck)
{
return (value??valueToCheck) == valueToCheck;
}
more on here
For checking for null and providing a default value, you can use the ?? operator:
return x ?? new Foo();
That means, if x is null, return new Foo(), else return x. You can use it for reference types and nullable types. For nun-nullable types like int, you still need to explicitly check for 0.
What you want is the Coalesce operator (??), which does just that - if returns the first operand if it's not null, and the second if it is. This will instantiate a new object if the current one is null:
return myObj ?? new MyObject();
Note that the ?? operator works only for classes and reference types, not for ints and other value types that can't be null. There, you'll have to check manually for default, uninitialized values (0 for ints and shorts and stuff, false for bools, and so forth)

What's the C# equivalent to C++'s dynamic_cast?

This C++ code checks if o is a Node * and if so, calls a method on d.
if (Node * d = dynamic_cast<Node *>(o)) d->do_it();
What's the shortest and/or most efficient way to write the equivalent in C#?
Assuming that Node is a class then do the following
Node d = o as Node;
if (d != null) {
d.do_it();
}
If instead it's a struct then try this
if (o is Node) {
((Node)o).do_it();
}
As of C# 6 (July 2015), assuming Node is a class (or Nullable<T>, string, etc), using your example where you
check if o is a Node (not actually the same as converting o to a Node--see note about casting vs converting below)
if so, call do_it()
immediately discard the cast value
you can use the null-conditional operator:
(o as Node)?.do_it();
This syntax also handles the case where o is, in fact, declared as Node, but happens to be null.
If you want to keep the cast variable, as of C# 7 (March 2017), you can run:
if (o is Node node)
{
node.do_it();
}
The variable node at this point is in the scope outside of the if statement, equivalent to:
Node node = o as Node;
if (node != null)
{
node.do_it();
}
So, if you want to only continue the execution if o is a Node, you can write:
if (!(o is Node node))
{
return; // or continue, etc
}
node.do_it();
// ...
Note: The is keyword will always return false if o is null, even if you directly specify the type and then ask if that variable is that type.
string foo = null;
if (foo is string)
{
// never gets here
Console.WriteLine(foo);
}
Casting vs Converting
The is and as keywords do the same as C++'s dynamic_cast<T>: they will check against the specified type, subtype, or interface, but will not actually change the value in memory. They simply tell the compiler which methods should be available on the variable.
There's a misnomer amongst C# users where we use the words "cast" and "convert" interchangeably. This likely stems from the fact that we often know that a base type variable is always going to be a subtype, and so we use the convert syntax when puritanically we should be using the cast syntax:
void Foo(MyBaseType value)
{
// let's assume `value` will always be a MySubType
MySubType subTypeValue = (MySubType)value;
}
This syntax will throw at runtime if value is not, in fact, MySubType.
Converting differs from casting in that the value in memory may change. Consider int and double.
void Foo()
{
// implicit converting
int x = 1;
double y = x;
// explicit converting
y = 1.5;
x = (int)y;
}
In each of these cases, the literal value stored in memory changes format. ints can always be represented by a double--there will never be a loss in data--and so there is a defined implicit operator that will manipulate the data in memory into the new format. doubles, being floating point values and having a range larger than ints, cannot guarantee no loss in data, so C# requires an explicit conversion (usually termed "explicit cast") via the explicit operator to indicate to the compiler that we're okay with losing data.
With classes, we can define our own implicit and explicit operators which will manipulate the data whatever way we see fit. This is where the misnomer between convert and cast gets messy.
using System;
public class Program
{
public static void Main()
{
Foo foo = new Foo();
Bar bar = (Bar)foo;
// writes "1" (or would, if the program compiled)
Console.WriteLine(bar);
// throws compilation error: "Cannot convert type `Foo' to `Bar' via built-in conversion"
bar = foo as Bar;
// note: the same would happen if `foo` was type int? and `bar` was type `double?`
// even though double? can be converted to int?
}
}
class Foo
{
public readonly int Value = 1;
public static explicit operator Bar(Foo foo)
{
return new Bar(foo.Value.ToString());
}
}
class Bar
{
public readonly string Value;
public Bar(string value)
{
Value = value;
}
}
The as operator returns null if o is not a Node:
Node d = o as Node;
if (d != null)
{
d.do_it();
}
You can use the is keyword in C#.
if (o is Node)
{
}

Categories

Resources