Null-Forgiving operator still showing warning - c#

I have the following method to override ToString()
public override string ToString()
{
if (HasNoValue)
return "No value";
return Value!.ToString();
}
If HasNoValue returns true, the value property contains null so "No Value" is returned.
The HasNoValue property (and HasValue) looks like this:
public bool HasValue => value != null;
public bool HasNoValue => !HasValue;
So if NoHasValue is false I know value is not Null. As you can see I've added the null-forgiving operator on the value property before the call to ToString()
I have Nullable enable within my project <Nullable>enable</Nullable>
However, I still receive the following warning:
Warning CS8603 Possible null reference return.
Why am I receiving this warning if I've added the null-forgiving operator?

The direct answer to your question is that Object.ToString() returns string?, so you need the null-forgiving operator at the end of the .ToString() call
You can use the MemberNotNullWhen attribute to tell the compiler that the Value property won't be null when the HasNoValue property is false
[MemberNotNullWhen(false, nameof(HasNoValue))]
public object? Value { get; set; }

Related

Implicit operator isn't called for default of struct in C#

I'm implementing a C# variant of Haskell's Maybe and came across a weird issue where null and default has different implication on the value returned from an implicit conversion.
public class TestClass
{
public void Test()
{
Maybe<string> valueDefault = default;
Maybe<string> valueNull = null;
Maybe<string> valueFoobar = "foobar";
Console.WriteLine($"Default: (Some {valueDefault.Some}, None {valueDefault.None}");
Console.WriteLine($"Null: (Some {valueNull.Some}, None {valueNull.None}");
Console.WriteLine($"Foobar: (Some {valueFoobar.Some}, None {valueFoobar.None}");
}
}
public struct Maybe<T>
{
public T Some { get; private set; }
public bool None { get; private set; }
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>() {
Some = value,
None = value == null
};
}
}
The output being:
Default: (Some , None False)
Null: (Some , None True)
Foobar: (Some foobar, None False)
I was expecting both valueDefault and valueNull to be equal. But seems that null is converted while default isn't. I fixed the issue by replacing None with HasSome with a reversed boolean condition, but still the question remains.
Why is null and default treated differently?
Every type has a default value, including Maybe<T>. See this page for a list.
Maybe<string> valueDefault = default; will assign the default value of Maybe<string> to valueDefault. What's the default value of Maybe<string>? According to that page, since Maybe<string> is a struct, its default value is:
The value produced by setting all value-type fields to their default values and all reference-type fields to null.
So it's an instance of Maybe<string> with Some being null and None being false. false is the default value of bool.
The compiler doesn't try to use the default value of string, since that requires a further conversion to Maybe<string>. If it can just use the default value of Maybe<string>, why go the extra trouble, right?
You can force it to though:
Maybe<string> valueDefault = default(string);
null, on the other hand, gets converted to Maybe<string> because null is not a valid value of Maybe<string> (structs can't be null!), so the compiler deduces that you must mean null as string, and does the implicit conversion.
You might know this already, but you seem to be reinventing Nullable<T>
default always fills the memory of the struct with zero bytes. null is not a valid value for a value type, so the compiler discovers the implicit (Maybe<string>)(string)null cast.
Perhaps you could replace with;
public struct Maybe<T>
{
public T Some { get; private set; }
public bool None => Some == null;
...

How is it possible for me to call HasValue on a null object? [duplicate]

This question already has answers here:
Why does Nullable<T> HasValue property not throw NullReferenceException on Nulls?
(5 answers)
Closed 6 years ago.
Consider the following code:
int? myInt = null;
var hasValue = myInt.HasValue;
This is perfectly acceptable, and HasValue will return false. But how have I managed to reference a property on an object that is null? The following wouldn't work:
public class MyClass
{
public bool MyBool { get { return true; } };
}
// Elsewhere in code...
MyClass myObject = null;
myObject.MyBool;
This would raise a null reference exception. The fact I'm comparing the two code snippets above as though they are equal tells me that I'm misunderstanding a structure or some syntax somewhere.
Can somebody please tell me why code snippet 1 does not raise an exception, but why my code snippet 2 would?
In the first case, you have a value of type Nullable<int>, which is "the null value for the type". The Nullable<T> type is a value type with two fields, like this:
public struct Nullable<T> where T : struct
{
private T value;
private bool hasValue;
}
When you use the null literal with a Nullable<T>, you're really just creating a value where the value field has the default value of T, and hasValue is false. Using the HasValue property isn't following a reference - it's just getting a field out of the struct.
In the second case, the value of myObject is a null reference... so if you try to use the MyBool property, that fails because it's trying to find an object when there isn't one.
So the key points are:
Nullable<T> is a struct, not a class
The value of a variable of type Nullable<T> is just a value with two fields, not a reference
The null literal just means "the null value of the relevant type", it does not always mean a null reference.

Null coalescing operator on decimal and decimal

I'm facing the following error while applying a null coalescing operator.
private decimal _currentImpulseId;
// ... later on used in public property getter as follows
public decimal CurrentImpulseId
{
get { return _currentImpulseId ?? 0M; }
set { _currentImpulseId = value; }
}
Following error is returned:
Operator '??' cannot be applied to operands of type 'decimal' and
'decimal'
Why doesn't this work? Does the null coalescing operator not work with decimals or am I missing something here? I know how I can work around this without using the operator, but this somewhat baffled me so I wonder if you know the correct answer on this?
Thanks!
You are getting this error because decimal is a value type, not a nullable value type (e.g. decimal?) or a reference type.
From MSDN:
...and is used to define a default value for nullable value types or reference types
The default value of a variable of type decimal is zero, so I am not quite sure why you would want to use this operator in this scenario.
Also, the backing field variable (_currentImpulseId) is not required as of .NET 3.0, since the introduction of auto-implemented properties means you can just write
public decimal CurrentImpulseId { get; set; }
Because decimal is a value type see HERE, you have to make it nullable.
Try
private decimal? _currentImpulseId;
// ... later on used in public property getter as follows
public decimal? CurrentImpulseId
{
get { return _currentImpulseId ?? 0M; }
set { _currentImpulseId = value; }
}

What are Nullable Types in C#?

int? _fileControlNo = null;
public int? FileControlNo
{
get { return _fileControlNo; }
set { _fileControlNo = value; }
}
I'm getting a syntax error when I assign null values to the above properties.
objDPRUtils.FileControlNo =sArrElements.Value(3)==null ? null : Convert.ToInt32(sArrElements.Value(3));
Please, can anyone explain to me why the error occurs if I'm able to set null value in valuetype object using Nullable Type.
The results of the conditional operators need to be of the same type or types that can be implicitly convertible to each other.
In your case you have a null and an Int32 - these violate that requirement.
If instead of an Int32 you return a nullable Int32, the null can be implicitly converted to this type and your code will work (or alternatively, cast the null to an int?).
Cast your null to int?
objDPRUtils.FileControlNo =sArrElements.Value(3)==null ? (int?) null : Convert.ToInt32(sArrElements.Value(3));
The conditional operator needs to return result of the same type and in your case its not possible for null
this should work
sArrElements.Value(3)==null ? (int?)null : Convert.ToInt32(sArrElements.Value(3));
Nullable types are exactly what they say they are: simple value types that can also store a null value.
I would suggest that you do not need the last line of code at all. you should be able to get away with:
objDPRUtils.FileControlNo =sArrElements.Value(3);
If you really want to assign another value in case of null, use the null coalescing operator ??
objDPRUtils.FileControlNo =sArrElements.Value(3)??0;
which in this case, would assign the value 0 to the FileControlNo in case of the right hand side being null.
Try to assign directly like this :
objDPRUtils.FileControlNo =sArrElements.Value(3);
Your code mixes types null and Int32 cannnot be mixed in this instance like that. They need to be of the same type.
If you use int.TryParse and only attempt to set the value on success you will achieve the same result and can use HasValue on the field to determine if its null or not which is how nullable types are used typically
Silly example
class Program
{
private static int? _fileControlNo;
static void Main(string[] args)
{
string[] sArrElements = new string[] { "1", "2", "3", null };
int result;
if (int.TryParse(sArrElements[3], out result))
{
FileControlNo = result;
}
if (_fileControlNo.HasValue)
{
// do something here
}
}
public static int? FileControlNo
{
get { return _fileControlNo; }
set { _fileControlNo = value; }
}
}
you will note that your code inside the test for HasValue in this case will never execute because _fileControlNo cannot be set because tryParse always fails (change the indexer and that will change).

Why does nullable KeyValuePair<,> have no key property?

I have the following:
KeyValuePair<string, string>? myKVP;
// code that may conditionally do something with it
string keyString = myKVP.Key;
// throws 'System.Nullable<System.Collections.Generic.KeyValuePair<string,string>>'
// does not contain a definition for 'Key'
I'm sure there is some reason for this as I can see that the type is nullable. Is it because I am trying to access the key when null could cause bad things to happen?
Try this instead:
myKVP.Value.Key;
Here is a stripped down version of System.Nullable<T>:
public struct Nullable<T> where T: struct
{
public T Value { get; }
}
Since the Value property is of type T you must use the Value property to get at the wrapped type instance that you are working with.
Edit: I would suggest that you check the HasValue property of your nullable type prior to using the Value.
if (myKVP.HasValue)
{
// use myKVP.Value in here safely
}
This is because nullable types can be assigned null value or the actual value, hence you have to call ".value" on all nullable types. ".value" will return the underlying value or throw a System::InvalidOperationException.
You can also call ".HasValue" on nullable type to make sure that there is value assigned to the actual type.

Categories

Resources