Содержание
- The C# Nullable Reference Types Preview
- Installation instructions
- Feedback
- Known issues
- is pattern incorrectly affects nullability
- Microsoft.CodeAnalysis.Compilers NuGet package conflict
- IntelliSense display
- Uninitialized fields warning
- Array covariance
- Conversion to interfaces and base classes
- Deconstruction
- Type patterns
- Frequently asked questions
- Changes from 05/14/18 build
- Changes from 03/06/18 build
- Legacy warnings
- Tracking non-nullable variables
- Method type inference
- Array initializers
- Conditional operator
- Changes from 11/15/17 build
The C# Nullable Reference Types Preview
Welcome to the preview implementation of C# Nullable Reference Types!
Nullable reference types are a feature currently planned for C# 8.0. It is introduced in this post on the .NET Blog. It is specified in more details in this feature speclet.
The preview installs on top of Visual Studio 2017 15.5 - 15.8. There is no installer for 15.9, as the first preview of Visual Studio 2019 should be forthcoming.
You can also try the C# Nullable Reference Types preview on SharpLab.
- Installation instructions
- Feedback
- Known issues
- Frequently asked questions
- Changes from previous build
Installation instructions
Please note: The preview is pre-release software. The features and behavior may change without notice. Please use for evaluation purposes only.
Please read all instructions before installing.
Known installation issue
The latest preview which we just shared (mid-September) does not install on 15.8.4 or 15.8.5. This was fixed in 15.8.6.
Installing
To install the preview:
- Download and extract Roslyn_Nullable_References_Preview.zip [latest 09/11/18]
- Close all instances of Visual Studio
- From cmd.exe, run .\install.bat from the zip root
The script will install experimental versions of the Roslyn extensions for Visual Studio, replacing the existing Roslyn extensions. The extensions will be installed to the default hive of Visual Studio.
The extensions are supported on Visual Studio 15.5, 15.6, 15.7, and 15.8 only. Before upgrading Visual Studio to a later version, uninstall the extensions using the uninstall.bat script.
Installing or uninstalling directly from Visual Studio (or double-clicking on the individual .vsix files) is not supported. There are dependencies between the extensions and the scripts are necessary to ensure the extensions are installed and uninstalled in order.
To activate the feature on a project you should add [module: System.Runtime.CompilerServices.NonNullTypes]
in any of the source files.
Individual types can be marked with [NonNullTypes]
to only get non-null reference types and corresponding warnings in those types. You can also use [NonNullTypes(false)]
to turn the feature off in parts of your program.
Uninstalling
To uninstall the preview:
- Close all instances of Visual Studio
- From cmd.exe, run .\uninstall.bat from the zip root
The script will uninstall the experimental versions of the Roslyn extensions, restoring the original Roslyn extensions.
If uninstall fails uninstalling a particular extension, re-run uninstall.exe. In some cases, it may be necessary to run uninstall.exe several times to complete all extensions.
Feedback
Please write up your thoughts and send them to us at this Microsoft email address: nullable@microsoft.com.
We use email so that your feedback is a conversation between you and us at Microsoft. Community discussion is great, but can happen elsewhere. We don't want to distract from understanding what you like and don't like about this feature, and what changes you'd like to see. It is very likely that we will reply with follow-up questions.
Known issues
Some of the most salient issues are described below. See the complete list for more information.
is
pattern incorrectly affects nullability
Debug.Assert(x != null); if (!(x is Type)) { x.ToString(); /* incorrect warning */ }
Microsoft.CodeAnalysis.Compilers NuGet package conflict
Referencing a Microsoft.CodeAnalysis.Compilers NuGet package directly in the project is not supported and may result in build errors reported for the .targets file.
IntelliSense display
Intellisense displays the declared nullability rather than the nullability inferred from flow analysis.
Uninitialized fields warning
Uninitialized fields with non-nullable reference types are only reported for constructors that do not call this()
.
class Person
{
public string FirstName;
public string? MiddleName;
public string LastName;
// warning: FirstName, LastName uninitialized
Person()
{
}
// no warning for FirstName
Person(string lastName) : this()
{
LastName = lastName;
}
}
Array covariance
Nullability mismatches are reported for array conversions that are co-variant with respect to nullability.
string?[] array = new string[] { "Hello", "World!" }; // unnecessary warning
Conversion to interfaces and base classes
Nullability mismatches of type arguments are not reported for conversions to interfaces or base classes in all cases.
IList<string> x = new List<string?>(); // no warning
Deconstruction
Deconstruction drops nullability.
(string?, string) tuple = (null, "");
var (x, y) = tuple; // (string, string)
x.ToString(); // no warning
Type patterns
Flow analysis does not infer nullability of operand in the case patterns of a switch
.
object? obj = F();
switch (obj)
{
case string s:
obj.ToString(); // unnecessary warning: may be null
break;
}
Frequently asked questions
Inferring null state from string.IsNullOrEmpty
Q: Can the compiler infer null state based on the return value of method such as string.IsNullOrEmpty
?
return string.IsNullOrEmpty(name) ?
0 :
name.Length; // warning: may be null
There is a similar issue writing a method such as bool TryGetValue(TKey key, out TValue? value)
.
The caller is forced to check value
before derefencing, even if TryGetValue
returns true
.
A: We are investigating allowing annotations on methods that describe simple relationships between parameters and return value with respect to null
values.
The compiler could use those annotations when analyzing the calling code.
In the short-term, a few common methods such as IsNullOrEmpty
and Debug.Assert
are specially recognized by the compiler to simulate this behavior.
Warnings for initialized fields
Q: Why are warnings reported for fields that are initialized indirectly by the constructor, or outside the constructor?
A: The compiler recognizes fields assigned explicitly in the current constructor only, and warns for other fields declared as non-nullable. That ignores other ways fields may be initialized such as factory methods, helper methods, property setters, and object initializers. We will investigate recognizing common initialization patterns to avoid unnecessary warnings.
Changes from 05/14/18 build
Turning the feature on
The feature is no longer on by default. To better reflect the design of feature as we intend to ship it, it is necessary to add the [System.Runtime.CompilerServices.NonNullTypes]
attribute in the user code to activate the feature and the new warnings. Without the attribute, warnings are produced if you use the ?
annotation on a reference type, or the suppression operator (!
).
The attribute can be added on the module ([module: NonNullTypes]
) as well as on types.
This attribute is synthesized by the compiler, so it doesn't need to be declared or referenced.
Nullability in constraints
In a NonNullTypes(true)
context, the class
constraint now means the type parameter is a non-nullable reference type.
If you want to relax this constraint, you can use the new class?
constraint.
Nullability of constraints is now enforced (although not in declarations yet). So [NonNullTypes] class C<T> where T : class { }
cannot be used in executable code such as C<string?> local = new C<string?>();
.
T?
The ?
annotation can only be used on a type parameter that is known to be a value type (via struct
constraint) or a reference type (via class
constraint or a non-nullable reference type constraint, such as where T : Base
).
Annotation attributes
The following attributes are recognized and affect nullability analysis:
[AssertsTrue]
(illustrated byDebug.Assert
)[AssertsFalse]
(illustrated byAssert.False
in test frameworks)[EnsuresNotNull]
(for a parameter of a method that throws if that parameter is null)[NotNullWhenTrue]
(useful for theout
parameter inTryGetValue
methods)[NotNullWhenFalse]
(illustrated bystring.IsNullOrEmpty
)
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class AssertsTrueAttribute : Attribute
{
public AssertsTrueAttribute () { }
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class AssertsFalseAttribute : Attribute
{
public AssertsFalseAttribute () { }
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class EnsuresNotNullAttribute : Attribute
{
public EnsuresNotNullAttribute() { }
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class NotNullWhenFalseAttribute : Attribute
{
public NotNullWhenFalseAttribute() { }
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class NotNullWhenTrueAttribute : Attribute
{
public NotNullWhenTrueAttribute() { }
}
}
Changes from 03/06/18 build
Legacy warnings
Assignments of nullable values to non-nullable locals and explicit casts of nullable values to non-nullable types are reported with a single specific error CS8600
, to allow disabling warnings for these cases particularly in legacy code. Disabling these warnings does not affect flow analysis.
#pragma warning disable 8600
object o = MaybeNull(); // [CS8600 warning]
string s = (string)o; // [CS8600 warning]
int n = s.Length; // warning: may be null
Tracking non-nullable variables
Nullability of locals, parameters, fields, and properties are tracked even if declared non-nullable.
string s = MaybeNull();
s.ToString(); // warning: may be null
Method type inference
Method type inference uses the nullability inferred from flow analysis.
T F<T>(T t) => t;
string? str = ...;
int n = F(str).Length; // warning: F<string?>() maybe null
if (str == null) return;
n = F(str).Length; // no warning for F<string>()
Array initializers
Array initializers use the nullability inferred from flow analysis.
string? str = "";
var array = new[] { str }; // string[] not string?[]
int n = array[0].Length; // no warning
Conditional operator
Conditional operator considers top-level and nested nullability of operands.
Changes from 11/15/17 build
Warnings from existing assemblies
Reference types in member signatures from unannotated assemblies are treated as neither explicitly nullable nor non-nullable, with no warnings from using such references.
string? name = ...;
if (string.IsNullOrEmpty(name)) // no warning
throw Exception();
string[] args = ...;
string? arg = args.FirstOrDefault();
int n = arg.Length; // no warning
Nullability tracking for fields and properties
Flow analysis tracks the nullability within a method body of fields and properties in addition to locals and parameters.
class Person
{
...
public Address? Address;
bool HasSameAddress(Person other)
{
if (Address == null) return other.Address == null;
if (other.Address == null) return false;
return Address.State == // no warning
other.Address.State && // no warning
...;
}
}