In this contrived C# 8 example:
#nullable enable
class Fred<T>
{
T Value; // If T is a nullable type, Value can be null.
public Fred() { }
public void SetValue(T value) { Value = value; }
public T GetValue() { return Value; }
public string Describe() { return Value.ToString() ?? "oops"; }
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
George g = fredGeorge.GetValue();
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.GetValue();
}
}
I have three design goals:
The compiler should warn me if I write any methods for Fred that blindly assume that 'Value' will never be null
The compiler should warn me if I write any methods outside of Fred (say, in George) that blindly assume that 'GetValue' will never return null.
No compiler warnings (if I have written code that doesn't blindly assume values won't be null)
So this first version isn't bad, I get a warning in Fred that Describe() might be dereferencing a null reference (satisfies goal #1) but I also get a warning that Value is uninitialized in Fred's constructor (violates goal #3) and George compiles without any warnings (violates goal #2). If I make this change:
public Fred() { Value = default; }
George still compiles without warnings (violates goal #2) and I get a different warning in Fred's constructor about a "Possible null reference assignment" (violates goal #3).
I can get rid of the possible null reference assignment by using the null-forgiving operator:
public Fred() { Value = default!; }
And now Fred only has the correct warning (possible dereference in Describe()), but George also compiles without warning (violates goal #2).
If I try to indicate that 'Value' can be null:
T? Value;
I get a compiler error that "A nullable type parameter must be known to be a value type or non-nullable reference type" so that's no good.
If I go back to
T Value;
and add the "MaybeNull" attribute:
[return: MaybeNull]
public T GetValue() { return Value; }
I get two warnings - one in Fred.Describe() warning of a possible null dereference (correct) and one in George warning that fredGeorge.GetValue() might be null (correct). There is no warning about fredFloat.GetValue() being null (correct).
So after adding code to expect null references, what I end up with is this:
class Fred<T>
{
T Value;
public Fred()
{
Value = default!;
}
public void SetValue(T value)
{
Value = value;
}
[return: MaybeNull]
public T GetValue()
{
return Value;
}
public string Describe()
{
return (Value == null) ? "null" : (Value.ToString() ?? "ToString is null");
}
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
George? g = fredGeorge.GetValue();
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.GetValue();
}
}
Is this the correct pattern for this functionality?
In System.Diagnostics.CodeAnalysis there is an attribute AllowNullAttribute that specifies that null is allowed as an input even if the corresponding type disallows it. I would use this attribute in your final sample to:
Decorate field Value. This will allow us to remove null-forgiving operator in the assignment Value = default. Compiler will not warn us about Possible null reference assignment, because now it knows that null value can be assigned to the property Value.
Decorate argument T value of the method SetValue. It will allow to pass null value to the method SetValue without getting compiler warning Cannot convert null literal to non-nullable reference type. (Currently if we pass null value to the method SetValue we will get this warning)
Here is final sample with suggested changes:
class Fred<T>
{
// AllowNull attribute says that a null value
// can be assigned to the field Value.
[AllowNull]
private T Value;
public Fred()
{
// Now we can delete null-forgiving operator, because compiler knows
// that null value can be assigned to the field Value.
Value = default;
}
// AllowNull attribute says that a null value
// can be passed to the method SetValue.
public void SetValue([AllowNull] T value)
{
Value = value;
}
[return: MaybeNull]
public T GetValue()
{
return Value;
}
public string Describe()
{
return (Value == null) ? "null" : (Value.ToString() ?? "ToString is null");
}
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
George? g = fredGeorge.GetValue();
// Compiler does not warn us "Cannot convert null literal to
// non-nullable reference type" because it knows that a null
// value can be passed to the method SetValue.
fredGeorge.SetValue(null);
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.GetValue();
}
}
If we use a regular property instead of the field Value with a pair of methods GetValue and SetValue then we can rewrite final sample in a clearer way:
class Fred<T>
{
// Here we tell that:
// 1) a null value can be assigned;
// 2) a null value can be returned.
[AllowNull, MaybeNull]
public T Value { get; set; }
public Fred()
{
// Compiler does not warn us "Possible null reference assignment".
// It knows that a null value can be assigned. It is correct.
// We can delete null-forgiving operator.
Value = default;
}
public string Describe()
{
// If we delete null checking, then we get a warning "Dereference of
// a possibly null reference". It is correct. Compiler helps us to avoid
// NullReferenceException.
return (Value == null) ? "null" : (Value.ToString() ?? "ToString is null");
}
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
// Compiler warns us "Converting null literal or possible null
// value to non-nullable type". It is correct.
// We should use nullable reference type George?.
George g = fredGeorge.Value;
// Compiler does not warn us "Cannot convert null literal to
// non-nullable reference type". It knows that a null value
// can be passed to the method SetValue. It is correct.
fredGeorge.Value = null;
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.Value;
}
}
Related
In C#10 I was trying to create a Result type that would have an error or a value (without going for monadic stuff).
When trying to use it I was expecting a compiler warning/error but I got nothing.
Why?
My .csproj has:
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
The code:
public record Error();
public record Result<T> where T : notnull
{
private Result()
{
}
public Error? Error { get; private init; }
public T? Value { get; private init; }
[MemberNotNull(nameof(Error))]
public static implicit operator Result<T>(Error error)
{
return new Result<T> {Error = error};
}
[MemberNotNull(nameof(Value))]
public static implicit operator Result<T>(T result)
{
return new Result<T> {Value = result};
}
[MemberNotNullWhen(false, nameof(Value))]
[MemberNotNullWhen(true, nameof(Error))]
public bool IsError()
{
return Error is not null;
}
}
public record TryResult
{
public void ValueTypes()
{
Result<Guid> result = new Error();
Guid value = result.Value; // no error, why?
if (!result.IsError())
value = result.Value;
}
public void RefTypes()
{
Result<string> result = new Error();
string value = result.Value; // error
if (!result.IsError())
value = result.Value; // OK
}
}
Result<Guid> result = new Error();
var value = result.Value; // no error, why?
result is not null: it's created on the line above, through an implicit conversion from Error, and the implicit conversion returns a non-null Result<T>. So accessing result.Value can't fail.
Your Value property has type T? (ignoring the MemberNotNull stuff -- we'll come to that in the next section). Since T is unconstrained, the T? means "if T is a reference type, this can be null; if T is a value type, the ? is ignored". Since T is Guid, this means that the property Value just has type Guid. So writing var value = result.Value is the same as writing Guid value = result.Value, which is fine.
If T were a reference type (such as string), then the Value property would have type string?, i.e. a nullable string. Even so, the compiler would infer that the var represents a string?, so that assignment would be the same as writing string? value = result.Value, which wouldn't cause any nullability errors.
Result<string> result = new Error();
string value = result.Value; // error
This time, T is a string, so the Value property has type string?. You're trying to assign this to a variable of type string, hence the error.
It seems that the compiler isn't able to do flow analysis from MemberNotNull through an implicit conversion, which I suppose makes sense. The property name you pass applies to the property on the current instance, but implicit conversions are static methods, and so there is no current instance for it to apply to.
if (!result.IsError())
value = result.Value; // OK
Here, MemberNotNUll is performing as expected.
int x might not have an assignment. How do I detect that? What value does int hold when it is unassigned?
This gives me a warning in Visual Studio:
public SomeMethod(int x) {
if (x == null) {
/* VS throws a warning:
'The result of the expression is always 'false' since a value of type 'int'
is never equal to 'null' of type 'int' */
};
}
This also gives me a warning in Visual Studio:
public SomeMethod(int? x) {
// declaring int 'Nullable' does not fix the warning.
if (x == null) {
// Same warning.
};
}
Likewise:
public SomeMethod(int? x) {
if (!x.HasValue) {
// This does not work either.
};
}
int x might not have an assignment.
That's simply not true. In your case, x is a parameter - it's "definitely assigned" from the start of the method, in specification terminology. When someone calls the method, they have to provide a value. There's no concept of the value not being assigned.
If you want to represent a parameter which may not have a meaningful int value, you should use Nullable<int>, aka int?:
public void DoSomething(int? x)
{
if (x == null)
{
...
}
else
{
// Or whatever
Console.WriteLine(x.Value);
}
}
As of C# 4, you could then give the parameter a default value of null if you want to be able to call it as just foo.DoSomething() without providing a value. You couldn't distinguish between that and foo.DoSomething(null) though.
In your case the best solution probably would be to use a default value for the parameter like this, so that when the parameter is null, the code does not throw an error and also the compiler will not show any warning -
public void test(int? x){
if (x == null)
{
Console.Write("test");
}
}
public void testA(int? x = null)
{
if (x == null)
{
Console.Write("test");
}
}
static void Main(string[] args)
{
var p = new Program();
p.test(null);
p.testA();
}
But the code -
public SomeMethod(int x) {
if (x == null) {
/* Never happens because 'The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int' */
};
}
will always show warning because the default value for int is 0, not null as mentioned here -
C# Primitive Types
And thus the the value for x == null is always false.
How do I return null value from a generic method?
protected T ValueOrDefault<T>(IDataReader reader, int ordinalId)
{
Type t = typeof(reader.GetValue(ordinalId));
if (t.IsValueType){
//Struct. How do I return null?
} else {
//Class
//just return null
return default(T);
}
}
default(T) works in both cases.
default(T) does function in both cases, but its meaning is slightly different for value types. It literally returns the default value of the type. In the case of Method<int>, it will return 0, not null.
UPDATE: Given your method signature:
protected T ValueOrDefault<T>(IDataReader reader, int ordinalId)
You can't return Nullable<T> in the case of a value type and type T in the case of a reference type. That would have to be two different methods.
Obviously you can only return null if the return type is either Nullable<T> or a reference type. Normal value-types have no null.
For reference types default(T) is null, and for Nullable<T> it's null too. So you can use default(T) in both cases where null exists.
If the type is another value-type default(T) will not be null, but since there is no null that wouldn't make sense anyways.
It is not possible to simply have a method that has return type T if T is a reference-type/Nullable<T> and T? for normal value types.
One could try to define something like this, but it won't compile because the compiler doesn't understand that the generic constraints are mutually exclusive. It just doesn't consider generic constraints for that.
T? a<T>()
where T:struct
{
}
T a<T>()
where T:class
{
}
You need to make these methods different in some other way. Either by using different names or different parameters.
If you want to return T? if it's a value type, you have to use two separate methods. However, there is a complexity, in that methods can't differ only by their return type (there's also issues around the generic args not being part of a method signature). So you have to provide a 'stub' method parameter which the compiler uses to resolve which method to call:
public T MyMethod<T>(T stub) where T : class {
// ...
return null;
}
public T? MyMethod<T>(T? stub) where T : struct {
// ...
return null;
}
// this will then compile...
string s = MyMethod<string>(null);
int? i = MyMethod<int>(null);
It's not legal to return null for an unconstrained T value. Consider for instance if T is instantiated as a value type. In that case null would not be a legal value and hence the code is illegal.
What you're looking for is default(T). This will work for both value and reference types. For reference types it will produce null and for value types it will produce the zero initialized value.
In C# 9, it is possible to have a single method return null for reference and for nullable value types. Note that string and string? are exactly the same type but int and int? are different (Int32 and Nullable).
[TestMethod]
public void TestGenericNull()
{
static T? GetDefault<T>(bool wantNullable) => wantNullable ? default(T?) : default(T);
string? s1 = GetDefault<string>(false);
Assert.IsNull(s1);
string? s2 = GetDefault<string>(true);
Assert.IsNull(s2);
int? d1 = default(int);
Assert.AreEqual(d1, 0);
int? d2 = default(int?);
Assert.IsNull(d2);
int? n1 = GetDefault<int>(false);
Assert.AreEqual(n1, 0);
int? n2 = GetDefault<int>(true);
Assert.AreEqual(n2, 0); // Expected default(int?), i.e. Nullable<int> with no value.
int? n4 = GetDefault<int?>(false);
Assert.IsNull(n4);
int? n5 = GetDefault<int?>(true);
Assert.IsNull(n5);
}
To see why this isn't possible, try replacing T with a value type:
protected int ValueOrDefault<int>(IDataReader reader, int ordinalId)
{
Type t = typeof(reader.GetValue(ordinalId));
if (t.IsValueType){
//Struct. How do I return null?
} else {
//Class
//just return null
return default(int);
}
}
If the return type is int, you can't return null because it's not a valid value. The type of T would have to be nullable in the first place, in which case default(T) works.
You can use just return default without (T) at the end. C# 7.1 or above.
Googling is only coming up with the keyword, but I stumbled across some code that says
MyVariable = default(MyObject);
and I am wondering what it means.
For a reference-type, it returns null
For a value-type other than Nullable<T> it returns a zero-initialized value
For Nullable<T> it returns the empty (pseudo-null) value (actually, this is a re-statement of the second bullet, but it is worth making it explicit)
The biggest use of default(T) is in generics, and things like the Try... pattern:
bool TryGetValue(out T value) {
if(NoDataIsAvailable) {
value = default(T); // because I have to set it to *something*
return false;
}
value = GetData();
return true;
}
As it happens, I also use it in some code-generation, where it is a pain to initialize fields / variables - but if you know the type:
bool someField = default(bool);
int someOtherField = default(int);
global::My.Namespace.SomeType another = default(global::My.Namespace.SomeType);
default keyword will return null for reference types and zero for numeric value types.
For structs, it will return each member of the struct initialized to zero or null depending on whether they are value or reference types.
from MSDN
Simple Sample code :<br>
class Foo
{
public string Bar { get; set; }
}
struct Bar
{
public int FooBar { get; set; }
public Foo BarFoo { get; set; }
}
public class AddPrinterConnection
{
public static void Main()
{
int n = default(int);
Foo f = default(Foo);
Bar b = default(Bar);
Console.WriteLine(n);
if (f == null) Console.WriteLine("f is null");
Console.WriteLine("b.FooBar = {0}",b.FooBar);
if (b.BarFoo == null) Console.WriteLine("b.BarFoo is null");
}
}
OUTPUT:
0
f is null
b.FooBar = 0
b.BarFoo is null
Specifies the default value of the
type parameter.This will be null for
reference types and zero for value
types.
See default
Default value of MyObject. See default Keyword in Generic Code (C# Programming Guide) (MSDN):
In generic classes and methods, one issue that arises is how to assign
a default value to a parameterized type T when you do not know the
following in advance:
Whether T will be a reference type or a value type.
If T is a value type, whether it will be a numeric value or a struct.
Given a variable t of a parameterized type T, the statement t = null
is only valid if T is a reference type and t = 0 will only work for
numeric value types but not for structs. The solution is to use the
default keyword, which will return null for reference types and zero
for numeric value types. For structs, it will return each member of
the struct initialized to zero or null depending on whether they are
value or reference types. The following example from the
GenericList class shows how to use the default keyword. For more
information, see Generics Overview.
public class GenericList<T>
{
private class Node
{
//...
public Node Next;
public T Data;
}
private Node head;
//...
public T GetNext()
{
T temp = default(T);
Node current = head;
if (current != null)
{
temp = current.Data;
current = current.Next;
}
return temp;
}
}
The default keyword returns the "default" or "empty" value for a variable of the requested type.
For all reference types (defined with class, delegate, etc), this is null. For value types (defined with struct, enum, etc) it's an all-zeroes value (for example, int 0, DateTime 0001-01-01 00:00:00, etc).
It's mostly used with generic code that can be applied to both reference and value types, because you can't assign null to a value type variable.
It will set the default value of an object to a variable:
null for reference types and 0 for value types.
Perhaps this may help you:
using System;
using System.Collections.Generic;
namespace Wrox.ProCSharp.Generics
{
public class DocumentManager < T >
{
private readonly Queue < T > documentQueue = new Queue < T > ();
public void AddDocument(T doc)
{
lock (this)
{
documentQueue.Enqueue(doc);
}
}
public bool IsDocumentAvailable
{
get { return documentQueue.Count > 0; }
}
}
}
It is not possible to assign null to generic types. The reason is that a generic type can also be instantiated as a value type, and null is allowed only with reference types. To circumvent this problem, you can use the default keyword. With the default keyword, null is assigned to reference types and 0 is assigned to value types.
public T GetDocument()
{
T doc = default(T);
lock (this)
{
doc = documentQueue.Dequeue();
}
return doc;
}
The default keyword has multiple meanings depending on the context where it is used. The switch
statement uses a default for defining the default case, and with generics the default is used to initialize generic types either to null or 0 depending on if it is a reference or value type.
When constraints have not been applied to restrict a generic type parameter to be a reference type, then a value type, such as a struct, could also be passed. In such cases, comparing the type parameter to null would always be false, because a struct can be empty, but never null
wrong code
public void TestChanges<T>(T inputValue)
try
{
if (inputValue==null)
return;
//operation on inputValue
}
catch
{
// ignore this.
}
}
corrected
public void TestChanges<T>(T inputValue)
try
{
if (object.Equals(inputValue, default(T)) )
return;
//operation on inputValue
}
catch
{
// ignore this.
}
}
Another good use of default(T) is when compiler can't determine returning type, like in here
class X
{
public int? P {get; set;}
}
// assigning in code
var x = new X();
// consider coll["key"] returns object boxed value
// data readers is one such case
x.P = myReader["someColumn"] == DbNull.Value ? default(int?) : (int)myReader["someColumn"];
Background
Let's say I have the following class:
public class MyValue<T>
{
public T Value { get; set; }
public static bool operator ==(MyValue<T> first, MyValue<T> second)
{
// if first and second are the same instance, they are equal
if (object.Equals(first, second))
{
return true;
}
// for each of the objects, get a value indicating whether either
// the object or its Value property is null
bool firstIsNull = object.Equals(first, null) ? true : first.Value == null;
bool secondIsNull = object.Equals(second, null) ? true : second.Value == null;
// if both are null, they are equal
if (firstIsNull && secondIsNull)
{
return true;
}
// Not both are null. If one is, they are not equal
if (firstIsNull || secondIsNull)
{
return false;
}
// Neither first nor second is null; compare their values
return first.Value.Equals(second.Value);
}
// more code implementing !=, Equals, GetHashCode, implicit conversions and so on
}
This class will be used as the type on properties in an object model. An object could look like so:
public class SomeClass
{
public SomeClass()
{
SomeValue = new MyValue<string>();
}
public MyValue<string> SomeValue { get; set; }
}
The idea with this is that the class simply represents the value of its Value property. It has some more functionality that is stripped away since it's not relevant for the question (some validation and stuff). Since the class has no value in itself, we want it to be as little intrusive as possible when used. Implicit conversions allows for code like this:
SomeClass instance = new SomeClass();
instance.SomeValue = "my string";
...and the question:
The idea with overloading the == (and !=) operator is to have an instance of the object where Value is null to compare equal to null (partly because we feel it makes sense, partly because of backwards compatibility issues in the object model):
MyValue<string> a = new MyValue<string>();
MyValue<string> b = null;
bool areTheyEqual = (a == b); // this will be true
This may of course seem a bit confusing, but given that MyClass is simply a wrapper, that in most of the code will be rather invisible, does it make sense to you too, or is it something really bad that we will regret for reasons that we overlooked?
In my opinion, == operator in C# is already confusing enough. If you overload it like that, you'll be increasing the possibility of confusion.
Class c = somethingEqualsToNullButNotNull;
if (c == null) { // true
}
object co = c;
if (co == null) { // false. WTF!?
}
You say you've already overridden Equals to provide the same behaviour. That means you've already broken the contract for Equals, which explicitly states:
- x.Equals(a null reference (Nothing in Visual Basic)) returns false.
Basically: no, don't do this. It will be confusing to users of the class.
Now Nullable<T> appears to break this rule:
int? x = new int?(); // Or int? x = null;
object o = null;
Console.WriteLine(x.Equals(o)); // True
... but in this case x isn't a reference, which is part of the contract description. The only way of making x a reference is to box the value, at which point x is null anyway. In your case, MyValue is a class so you can have a non-null reference which violates the contract.
If you override ==, then normally it is best to override .Equals to use the same logic.