If F were "on" a, I could do this...
var obj = a?.F();
If F is not on a, I have to do this...
var obj = a == null ? null : MyFunc.F((A) a);
Or do I? Is there a more succinct way of skipping the method call if a parameter value is null?
The short answer is no, there's no succinct way to do that.
The slightly longer answer is still no, but there's an interesting language design point here. C# was designed by people with extremely good taste, if I say so myself, but it was designed over a very long period of time. Nowhere is this more obvious than with respect to its treatment of nullability.
In C# 1.0 we had a straightforward language in the tradition of C, C++, Java, JavaScript, and so on, where there are references and values, and references can be null. This has benefits; if it did not, Sir Tony would not have invented the null reference in the first place. But it has downsides: we have the possibility of null references, dereferencing null leads to program crashes, and there is an inconsistency between reference and value types: reference types have a natural "no value" value, and value types do not.
In C# 2.0 we added nullable value types, but nullable value types do not behave like nullable reference types. Of course nullable values types are not references, so you cannot "dereference" them, but if we squint a little, the .Value property looks a lot like "dereferencing", and it leads to a similar crash if the value is null. In that sense, they behave the same, but in other senses, they do not. Adding together two nullable integers does not crash if one of them is null; instead, the result is also null.
So at this point we have a contradiction built into the language:
Using a null value of a nullable value type usually automatically propagates the null, but using a null reference can crash.
And of course C# has then gone on to add a variety of features that make null references behave more like null values, like ?. and the related operations. There are also proposals for C# 8 that are very exciting, and will support "non nullable reference type" scenarios.
But the bolded text above is the fundamental problem you've pinpointed: the semantics of operators on nullable reference types are almost always "lift the non-nullable version of the operator to nullable types; if all the operands are non-null then the result is the same as the unlifted version; otherwise, the result is null". However, those semantics are not automatically extended to the . member access operator or the () call operator, regardless of whether the operands are nullable value types or nullable reference types. . can be lifted explicitly by ?. but the () operator does not get lifted to nullable semantics, ever.
Imagine a language like C# 1.0, but with Nullable<T> built in from the start, such that it applied to both reference and value types. In that world, you can see natural ways to implement generalized lifting, where if you have a method
class C { string M(double, int[]) }
and you call it with a Nullable<C> receiver, or Nullable<double> and Nullable<int[]> arguments, you automatically get the code that we build for you for nullable integer arithmetic: check whether the receiver or any arguments are null, and if they are, result in a null Nullable<string>. Otherwise, call the function normally and use the non-nullable result.
The C# compiler already implements these semantics for all user-defined operators declared on struct types; it would not be hardly any difficulty at all to extend those semantics to other kinds of methods. But we can't do it now; there are far too many backwards-compatibility issues to solve.
This design choice would also have the nice property that it would be a correct implementation of the "maybe monad".
But that's not the world we are in, and it's largely because these design considerations evolved over time, rather than being invented all at once. The next time you invent a new language, consider carefully how to represent nullability!
Related
In a C# 8 project with nullable reference types enabled, I have the following code which I think should give me a warning about a possible null dereference, but doesn't:
public class ExampleClassMember
{
public int Value { get; }
}
public struct ExampleStruct
{
public ExampleClassMember Member { get; }
}
public class Program
{
public static void Main(string[] args)
{
var instance = new ExampleStruct();
Console.WriteLine(instance.Member.Value); // expected warning here about possible null dereference
}
}
When instance is initialized with the default constructor, instance.Member is set to the default value of ExampleClassMember, which is null. Thus, instance.Member.Value will throw a NullReferenceException at runtime. As I understand C# 8's nullability detection, I should get a compiler warning about this possibility, but I don't; why is that?
Note that there is no reason for there to be a warning on the call to Console.WriteLine(). The reference type property is not a nullable type, and so there's no need for the compiler to warn that it might be null.
You might argue that the compiler should warn about the reference in the struct itself. That would seem reasonable to me. But, it doesn't. This seems to be a loophole, caused by the default initialization for value types, i.e. there must always be a default (parameterless) constructor, which always just zeroes out all the fields (nulls for reference type fields, zeroes for numeric types, etc.).
I call it a loophole, because in theory non-nullable reference values should in fact always be non-null! Duh. :)
This loophole appears to be addressed in this blog article: Introducing Nullable Reference Types in C#
Avoiding nulls
So far, the warnings were about protecting nulls in nullable references from being dereferenced. The other side of the coin is to avoid having nulls at all in the nonnullable references.
There are a couple of ways null values can come into existence, and most of them are worth warning about, whereas a couple of them would cause another “sea of warnings” that is better to avoid:
…
Using the default constructor of a struct that has a field of nonnullable reference type. This one is sneaky, since the default constructor (which zeroes out the struct) can even be implicitly used in many places. Probably better not to warn [emphasis mine - PD], or else many existing struct types would be rendered useless.
In other words, yes this is a loophole, but no it's not a bug. The language designers are aware of it, but have chosen to leave this scenario out of the warnings, because to do otherwise would be impractical given the way struct initialization works.
Note that this is also in keeping with the broader philosophy behind the feature. From the same article:
So we want it to complain about your existing code. But not obnoxiously. Here’s how we are going to try to strike that balance:
…
There is no guaranteed null safety [emphasis mine - PD], even if you react to and eliminate all the warnings. There are many holes in the analysis by necessity, and also some by choice.
To that last point: Sometimes a warning is the “correct” thing to do, but would fire all the time on existing code, even when it is actually written in a null safe way. In such cases we will err on the side of convenience, not correctness. We cannot be yielding a “sea of warnings” on existing code: too many people would just turn the warnings back off and never benefit from it.
Also note that this same issue exists with arrays of nominally non-nullable reference types (e.g. string[]). When you create the array, all of the reference values are null, and yet this is legal and won't generate any warnings.
So much for explaining why things are the way the are. Then the question becomes, what to do about it? That's a lot more subjective, and I don't think there's a right or wrong answer. That said…
I personally would treat my struct types on a case-by-case basis. For those where the intent is actually a nullable reference type, I would apply the ? annotation. Otherwise, I would not.
Technically, every single reference value in a struct should be "nullable", i.e. include the ? nullable annotation with the type name. But as with many similar features (like async/await in C# or const in C++), this has an "infectious" aspect, in that you'll either need to override that annotation later (with the ! annotation), or include an explicit null check, or only ever assign that value to another nullable reference type variable.
To me, this defeats a lot of the purpose of enabling nullable reference types. Since such members of struct types will require special-case handling at some point anyway, and since the only way to truly safely handle it while still being able to use non-nullable reference types is to put null checks everywhere you use the struct, I feel that it's a reasonable implementation choice to accept that when code initializes the struct, it is that code's responsibility to do so correctly and make sure the non-nullable reference type member is in fact initialized to a non-null value.
This can be aided by providing an "official" means of initialization, such as a non-default constructor (i.e. one with parameters) or factory method. There will still always be the risk of using the default constructor, or no constructor at all (as in array allocations), but by providing a convenient means to initialize the struct correctly, this will encourage code that uses it to avoid null references in non-nullable variables.
That said, if what you want is 100% safety with respect to nullable reference types, then clearly the correct approach for that particular goal is to always annotate every reference type member in a struct with ?. This means every field and every auto-implemented property, along with any method or property getter that directly returns such values or the product of such values. Then the consuming code will need to include null checks or the null-forgiving operator at every point where such values are copied into non-nullable variables.
In the light of the excellent answer by #peter-duniho it seems that as of Oct-2019 it's best to mark all non-value-type members a nullable reference.
#nullable enable
public class C
{
public int P1 { get; }
}
public struct S
{
public C? Member { get; } // Reluctantly mark as nullable reference because
// https://devblogs.microsoft.com/dotnet/nullable-reference-types-in-csharp/
// states:
// "Using the default constructor of a struct that has a
// field of nonnullable reference type. This one is
// sneaky, since the default constructor (which zeroes
// out the struct) can even be implicitly used in many
// places. Probably better not to warn, or else many
// existing struct types would be rendered useless."
}
public class Program
{
public static void Main()
{
var instance = new S();
Console.WriteLine(instance.Member.P1); // Warning
}
}
Does maintenance become a nightmare on code that allows a nullable type of a value type? I realize that int? is the equivalent of Nullable<int>, but my question is more geared towards the usability of it. We see value types and naturally overlook them as not allowing null. But bringing in the Nullable<T> with a shorthand of the question mark, it's obvious what it does but not always noticeable.
Is this one of those features that just because you can do it, doesn't mean you should?
What should be the preference? A default value of a value type (i.e. int SomeConfigOption = -1;) or utilizing Nullable<T> (i.e. int? SomeConfigOption;)?
What should be the preference? A default value of a value type (i.e.
int SomeConfigOption = -1;) or utilizing Nullable (i.e. int?
SomeConfigOption;)?
In this case clearly you want Nullable<T> whenever you have the case that you have to account for the absence of a value. Magic numbers like -1 are a far worse maintenance nightmare.
This is a core feature of the C# language, as with other features it can be abused but it provides clear benefits as well - these benefits far outweigh any problems someone not proficient in the language might have reading the source code - time to get up to speed.
I think Nullable looks nice: code with Nullable types is quite self-documented.
Examples:
int? someConfigOption;
if (someConfigOption.HasValue)
{
// Use someConfigOption.Value property.
}
else
{
// Value is absent.
}
Another handy approach:
int value = someConfigOption.GetValueOrDefault();
Of course, the methods which take Nullable values as their parameters should be well documented.
I much prefer a nullable type to a value type with a default value (which the developer then means null). I have found more issues in code where default values are used to mean nothing.
Nullable types should be used when "null" is a valid value. If null is a valid value, then using nullable types is a good practice. But if nullable values are not valid, then carrying the null value is bad practice.
Suppose a function GetClientID where the function reads from the DB, and returns a ClientID. Let's assume that GetClientID should never return NULL or empty.
While reading the value from the DB, a nullable type is the best practice for handling possible nulls from the DB. GetClientID therefore should use the nullable type to handle correctly the exception situation (failing / logging / etc) and making sure only valid values return.
The worst practice would be to return a nullable type. You can potentially be carrying an already invalid value that would be read somewhere in your code, failing far from where the invalid value was loaded (and becoming a maintenance nightmare).
So ask yourself, if the null value a valid value (valid is no the same as possible) and code accordingly.
I heard that the addition of Nullable<T> type to C# 2.0 need to revise CLR(runtime) a little, is this change necessary? Could it make the same goal if a new Nullable<T> generic class was added only?
Nullable isn't a generic class as you indicate, Nullable<T> is generic (it has a type parameter, T). That's why Nullable<T> only arrived in C# 2.0: it came with the addition of generics to the CLR.
You could do the same thing with a general Nullable, but you couldn't do things like this:
int? myInt = 123;
int result = myInt.Value;
You would instead have to:
int result = (int)myInt.Value;
...and it might not be type-safe, by that I mean what if myInt.Value is a string? The generic version, Nullable<T>, would only allow an int into the Value property.
I don't completely understand what you're asking, though.. "why are generic classes useful"?
If I understand you correctly, you are asking why can't it just be Type introduced in the framework library? but instead the CLR need to be changed?
Well based on my understanding Nullable is a special type, not quite like other container type. First it is actually a value type - defined as struct, not a class. Separately it allows to be assigned a value of null (for value type, that is a special case), plus it support the use of '?' and operator '??' which is all new. The Nullable also become part of the Common type system. So I guess from these perspective, the specification, compiler and CLR will need to be changed.
There are only two reasons I know of for the special handling of Nullable<T>:
To allow a typecast from null Object reference to a Nullable<T> with HasValue = false.
To allow a Nullable<T> where HasValue is false, to compare equal to null.
Frankly, think it would have been better to let Nullable<T> box like any other value type, and define Nullable<T>.Empty as a value which may be compared against (for those cases where one might want to compare against a variable that might be null or might hold a value). To my mind, there's no reason why Object.Equals should report that an "int?" which is equal to null is equal to a "long?" which is also equal to null. The first should be viewed as an empty int-sized box, and the latter as an empty long-sized box.
Why is the following forbidden?
Nullable<Nullable<int>>
whereas
struct MyNullable <T>
{
}
MyNullable<Nullable<int>>
is NOT
This is because the struct constraint actually means 'not nullable' since Nullable, despite being a struct, is nullable (can accept the value null) the Nullable<int> is not a valid type parameter to the outer Nullable.
This is made explicit in the constraints documentation
where T: struct
The type argument must be a value type. Any value type except Nullable can be specified.
See Using Nullable Types (C# Programming Guide) for more information.
If you want the rationale for that you would need the actual language designer's comments on it which I can't find. However I would postulate that:
the compiler and platform changes required to achieve Nullable in it's current form are quite extensive (and were a relatively last minute addition to the 2.0 release).
They have several potentially confusing edge cases.
Allowing the equivalent of int?? would only confuse that since the language provides no way of distinguishing Nullable<Nullable<null>> and Nullable<null> nor any obvious solution to the following.
Nullable<Nullable<int>> x = null;
Nullable<int> y = null;
Console.WriteLine(x == null); // true
Console.WriteLine(y == null); // true
Console.WriteLine(x == y); // false or a compile time error!
Making that return true would be very complex and significant overhead on many operations involving the Nullable type.
Some types in the CLR are 'special', examples are strings and primitives in that the compiler and runtime know a lot about the implementation used by each other. Nullable is special in this way as well. Since it is already special cased in other areas special casing the where T : struct aspect is not such a big deal. The benefit of this is in dealing with structs in generic classes because none of them, apart from Nullable, can be compared against null. This means the jit can safely consider t == null to be false always.
Where languages are designed to allow two very different concepts to interact you tend to get weird, confusing or down right dangerous edge cases. As an example consider Nullable and the equality operators
int? x = null;
int? y = null;
Console.WriteLine(x == y); // true
Console.WriteLine(x >= y); // false!
By preventing Nullables when using struct generic constraint many nasty (and unclear) edge cases can be avoided.
As to the exact part of the specification that mandates this from section 25.7 (emphasis mine):
The value type constraint specifies that a type argument used for the type parameter
must be a value type (§25.7.1). Any non-nullable struct type, enum type, or type
parameter having the value type constraint satisfies this constraint. A type parameter
having the value type constraint shall not also have the constructor-constraint.
The System.Nullable type specifies the non-nullable value type constraint for T.
Thus, recursively constructed types of the forms T?? and Nullable<Nullable<T>> are prohibited.
I believe you can only use non-nullable value types in Nullables. Since Nullable itself is nullable, nesting this way is prohibited.
From http://msdn.microsoft.com/en-us/library/kwxxazwb.aspx
public Nullable(
T value
)
Type: T A value type.
Nullable is special because there's explicit support for boxing and unboxing of Nullable types built into the CLR:
If you use the MSIL box instruction against a Nullable<T>, you will actually get a null as the result. There's no other value type which will produce a null when boxed.
There's similar and symmetrical support for unboxing.
The generic type parameter for Nullable must itself be a non-nullable type (i.e. value type). This is the C# compiler warning I get, and it would seem to make sense. Tell me, why would you want to do such a thing anyway? I personally can see no use, and little meaning to such a declaration even.
Nullable allows you to take a value type and make it like a reference type, in the sense that the value either exists or not (is null). Since a reference type is already nullable it is not allowed.
Quoted from MSDN:
The Nullable(T) structure supports
using only a value type as a nullable
type because reference types are
nullable by design.
I am always confused with the different ways of expressing nulls. There is the null reference type (aka "null"). Then I've seen that throughout my application, developers have used MinValue to represent nulls. Ex: Double.MinValue or DateTime.MinValue except for a String for which they use "null"
Then there is System.DBNull (and System.DBNull.Value - not sure what to use when). To add to the confusion, there are also System.Nullable and System.Nullable<T> namespaces.
Can someone help me clear this null confusion?
Thanks
Sure.
System.DBNull is a class that was (and still is) used by ADO.NET to represent a null value in a database.
null is actually a null reference, and in your application code any reference type should use null as its, well, null value.
The usage of MinValue for various primitive types (which, since they are value types cannot be assigned null) dates back to the dark days before C# 2.0, which introduced generics. Now the preferred method for representing a nullable primitive type is to use the Nullable<T> generic type, usually represented in shorthand with a question mark after the primitive type. For example, I could declare a nullable int variable named foo in C# as:
Nullable<int> foo;
or
int? foo;
Both are identical.
null is only valid for reference types: types that are a class rather than a structure.
.Net also has value types: int, double, DateTime, etc. Value types cannot be null, you so you normally compare those to their default value, which is very often type.MinValue, but might be something (consider Boolean, for example).
Nullable<T> is for when you have a value type that might genuinely be null. The default value (maybe MinValue) is also valid and you need to distinguish it from when the variable has not been assigned yet. In C#, you can use a ? with a value type as a short hand notation (for example: int?), but you're still creating the same Nullable<T>.
DBNull specifically refers to NULL values from a database. It's not the same thing as using null elsewhere in the language: it's only for talking to a database so you can know when a query returned a null value.
When using generics, you'll also see the default(T) construct used, where T is a type parameter. This allows you to set a type's default value without knowing whether that type is a reference type or a value type, much less what a specific value type's default value might be.
A Null value represents any reference type object that has not had its memory allocated yet.
The MinValue does not represent a Null, rather it is a property that is often used to represent the smallest possible constant value that a given value type can have.
The DBNull.Value class is a mapping of the Nulls returned/passed to a database.
The Nullable generic type enables you to assign Null values to a Value-type.
null for reference types in the
actual null value
Nullable, added in .NET 2.0 is a
way of expressing nulls for value
types, which by definition can not be
null.
Same with DateTime.MinValue -
DateTime is a value type, and can not
be null, so you can have a convention
that a well known value, like
DateTime.MinValue is treated as if it
was null. It also has other usages.
"MinValue"s were used with value types before nullable types came around in C# 2.0. So there is a lot of legacy code which uses the old style of knowing when a value type doesn't have a value. Nowadays it's much easier to use a DateTime? date = null than DateTime date = DateTime.MinValue. As far as DBNull goes, that is something that is required as an abstraction layer to databases, but you can avoid having to deal with it yourself by employing an ORM such as NHibernate or some such - you pretty much, from a app. development standpoint will only have to deal with built-in C# types.
MinValue is not null. It's MinValue. It is sometimes used "as null" by people using C# 1.0, which did not have nullable value types. Instead, they should use Nullable, ot DateTime?.
DBNull is a .NET value that is used to communicate with a database ("DB" null). The database concept of NULL means something different from the .NET null reference. In database terms, NULL means "unknown" or "absent".
Reference types (aka objects) can be set to null since reference type variables is just a pointer to the actual instance. Indicating the lack of an instance is easy since you can set the variable to null directly.
For value types this is a bit harder since a value type variable always contains a value.
Therefore, the use of Double.MinValue or DateTime.MinValue was somewhat valid in the pre-Nullable days. Back then there was no easy way of expressing the lack of a value in value types.
Now, with nullable types you can say:
double? d = null;
And thus you can also have value type variables containing null.
System.DBNull is a different story since it is directly tied to expressing the value "NULL" in datasets. This was introduced before nullable types which imo supersedes DBNull.
Most types in .NET are reference types, and null is "not a reference" which can be assigned to a variable or field to indicate there is no object.
All other types in .NET are value types. There is always a value with a value type, so no ability to indicate "no value". Therefore an implementation may define specific special values to indicate this. Often the value type's MinValue (constant) field is used for this. So using DateTime.MinValue for a date of birth can indicate "not known" or "not applicable" (e.g. for a corporate entity).
DbNUll exists to express RDBMS's meaning of NULL which is a little different to .NET's. In most cases this will be translated to null or similar.
Finally, Nullable<T> is a wrapper for value types to more directly express "not known", and is generally a better option than using MinValue, but was added in .NET 2, so older designs may have started using MinValue before Nullable<T> is available.
the best way for tou to understand this different null values is to try them and see what the use of each one but to make things simple here is some guideline
null can be used only on reference types like collections and custom classes (string is special)
DBNull is for null values that came out of the db query.
because we cant assign null to a decimal or double we assign them with the MinValue property (they are value objects)
Nullable is a way for the developer to assign null values to value objects like decimal,
Nullable<int> is same as int?
i hope i helped you understanding the differences.
The important difference here is that between value types and reference types.
Value types represent directly a certain value, whereas reference types point to a memory location that should represent a value. When a a reference type actually does not point to a memory location with valid contents, the reference is Null.
Since value types are direct representations of value, they cannot be null. However, it might be the case that a actual value of a value type is unknown. For this case, .Net provides the Nullable types construct. When such a construct is not available, however, people tend to use special or default values, such as MinValue.
When communicating with databases, a lot of things we expect to be value types can actually be Null, since that it the way the database handles unknown values. This can also be solved by the Nullable types, but these were not always available. That's why DBNull exist, to deal with a possible null in a database.