C# 7: Ref Returns, Ref Locals, and how to use them

C# has always supported the ability to pass by reference using the ref keyword on method parameters. C# 7 adds the ability to return by reference and to store references in local variables.

The primary reason for using ref returns and ref locals is performance. If you have big structs, you can now reference these directly in safe code to avoid copying. Before C# 7 you had to work with unsafe code and pointers to pinned memory.

A secondary reason for using ref returns and ref locals is to create helper methods that were not possible before C# 7.

There are some restrictions on the usage of ref returns and ref locals to keep things safe:

  • returned refs must point at mutable (not readonly) object fields, or have been passed in by reference
  • ref locals cannot be mutated to point at a different location

These restrictions ensure that we "never allow an alias to a dead variable", Eric Lippert . Which means that the compiler will make sure that objects returned by reference, will be accessible after the method has returned, and will not be cleaned up by the garbage collector.

Ref Returns

To return by reference, add the keyword ref before the return type on any method signature and after the keyword return in the method body. For example, the Get method in Score returns the private field value by reference. If value were readonly , the compiler would not permit it to be returned by reference.

To store a reference into a local variable, define the local variable as a reference by adding the keyword ref before the variable type and add the keyword ref before the method call. For example, in the following code sample, highscore is a ref local. As shown by anotherScore , you can still get a value (as opposed to a reference) when calling a ref returns method, by omitting the ref keyword when making the call.

From the output, we see that highscore does indeed reference the private variable score.value , as its value has changed too. Whereas anotherScore contains a copy, as changing its value has no effect on score.value . Finally, the call to change shows that when ref locals are accessed without the ref keyword, they behave just like normal locals and are passed by value to other methods.

Referencing Array Elements

It is also possible to return references into arrays. In this sample code, ThirdElement is a method which returns a reference to the third element of an array. As test2 shows, modifying the returned value, modifies the array. Note that now value points to the third position of values , there is no way to change value to point at a different position in the array or at a different variable entirely.

You could use this ability to reference array elements to implement an array search helper, which returns a reference to the matching array element, rather than its index.

Referencing Local Variables

We can also reference other local variables, as shown in test3 . However, these references cannot be returned, because they disappear when the method returns.

Assigning Values to Methods

Finally, with ref returns it is now possible to use a method on the left-hand side of an assignment. In test4 , Max returns a reference to the variable with the maximum value, and therefore, j , is changed to 20.

Ref returns and ref locals are primarily useful for improving performance, but as we've seen with the Max function and the array search helper, they find a role in creating certain helper methods too.

While you won't use ref returns and ref locals in all your code, they're a nice addition to the language for when you do need them.

Twitter

Search code, repositories, users, issues, pull requests...

Provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications

Paul-Sebastian Codes

Demystifying C#'s Parameter Modifiers and Value and Reference Types

Photo by Dylan Ferreira on Unsplash

Demystifying C#'s Parameter Modifiers and Value and Reference Types

A quick and rough simplification.

Paul-Sebastian Manole's photo

13 min read

When learning C# you will find out about the different ways that variables can be passed as arguments to function parameters , such as by value or by (some kind of) reference. These are very confusing things at the beginning for people trying to learn C#. In this post, I will try to simply the concepts and summarize the otherwise very lengthy official documentation.

First of all, you need to always remember this: in C# all arguments are passed by value by default! What that means is that values are copied by default, on both variable assignments and function calls.

So, when you are not using parameter modifiers (like ref ), everything is passed by value! The difference in behavior arises from the kind of object you are passing: a value object or a reference object, and what the variables you pass contain, value objects or reference objects. So it matters that you know what kind of objects you are working with (classes, structs, records, simple types — like int, double, etc.)!

Value objects are accessed directly, sort of like they exist right there in the variable, or on the current stack. Think of them like simple types: passing them by value, which is the default, will always copy the actual data they contain.

Reference objects on the other hand are accessed via a reference, so the variable that is assigned a reference object (or a reference value) contains only a reference to the real data. Accessing that data is done transparently so the programmer doesn't have to use a different syntax for this case.

Beware that different variables can be made to hold the same value by assigning a variable to another variable. For reference types this means two variables referring to the same data:

Now obj2 contains a copy of obj1 , which holds a reference, thus the reference gets copied and the copy gets assigned to obj2 , not the actual data that obj1 points to!

In other words, obj2 and obj1 both contain two distinct references pointing to the same object in memory.

Note that reassigning obj2 does not modify obj1 , just the reference held inside that particular obj2 variable:

obj1 still holds the original MyReferenceType("1") instance after the above operation.

For value types the default behavior of passing by value is different in that the actual data gets copied.

DateTime is a value type object (a struct ) so date2 now contains a copy of the actual date1 object, not a copied reference that refers to the same object, like it happened with reference types above.

Now, if we reassign to date2 , just like before, date1 remains untouched, because we're only altering what the variable holds, nothing else:

But now , say that DateTime , which is a struct , did not have an immutable public interface, and calling AddDays did not return a new DateTime instance with a number of days added, but instead modified the state of the current instance that it's being called on:

Now, date2 would be one day ahead of date1 and date1 would be untouched, because date2 was not assigned a reference to the same data that date1 points to, but an actual copy of the data that date1 contained.

Why is that? Because DateTime is a value type (a struct). If DateTime was a reference type, adding days to date2 would have also added days to date1 because both variables would have pointed to the same data in memory.

These deductions are simple to make, once you understand what types of objects you're working with and what the default behavior of C# of passing by value means.

Parameter modifiers

The official Microsoft docs say that in C# arguments can be passed to parameters either by value or by reference , by value being the default.

Passing by reference enables function members, methods, properties, indexers, operators, and constructors to change the value of the parameters and have that change persist in the calling environment.

Functional programming advocates might not agree with the above statement, because it enables functions to change variables that are defined outside of their scope, which makes code prone to runtime bugs that are hard to diagnose.

The scope of this article is not to advocate for or against the different programming styles, but just to explain these features of the C# language and to explain when or where they might be useful.

Pass by value

The default behavior of the language, when no parameter modifiers are used, is called pass by value , like I have mentioned before.

What this means is that whatever value the variable that is passed-in holds, it will be copied from the passed-in variable to the function argument, effectively creating different instances of the data but with the same exact value (at the start, not necessarily by the end of the function block). These instances are in no other way related to each other. Changing (ie. reassigning) one does not change the other for example. They are independent.

This can sometimes be confusing for new C# programmers, because they usually think that only simple types, like int and double , are passed by value, ie. the types inheriting from ValueType , because they are cheap to copy all the time.

In fact, reference types can also be passed by value . This is what normally happens when you don't use parameter modifiers and pass a reference type to an argument. The difference between value types and reference types, is that with reference types it's the reference that gets copied from one variable to the other, not the object the variable refers to (like it happens with primitive types). This is because there's no telling how large the object graph might be and copying such objects might be very expensive to do (but if you're crazy enough, you can still implement it).

So when the copy goes through, you will have two different variables holding the same reference value, but these references, while being equal at the start, are not the same, because the variables are not the same. The variables are still independent.

Passing by value copies values , but what the value actually is underneath doesn't matter very much. C# tries to abstract away memory pointers, which are more prevalent in programming languages like C and C++. This helps you always think of variables as their values, and not as either values or references (pointers).

C# does this by automatically dereferencing pointers when you use them, without requiring any extra syntax, like you have with C++ (ie. the * prefix, the -> arrow operator, etc.).

More precisely, when a reference type object gets passed-in by value (ie. no parameter modifier is used), the outside variable's reference value gets copied to the function argument. The two variables, the outside variable and the function argument, are two distinct variables they now hold the same value: the same reference, to the same object in memory.

So if you have something like this:

It might seem like calling the function PassByValue(1, myObj1) should clone myObj1 into the function body as the object o , but that's not what happens, because myObj is not a value type, but a reference type, so what actually gets copied is the reference itself, because that is the value that is stored intrinsically in the variable.

So, while the two references ( myObj1 and o ) are two distinct variables that point to the same object in memory, nothing is preventing you from changing the value of either variable so that they no longer point to the same object (or act on the same object), but reassigning something else to the parameter variable inside the function, doesn't also reassign to the variable outside the function's scope.

To summarize, pass by value is the default behavior and it copies what the variable that's passed in holds:

  • If it's a value type object, it clones the value of the argument so that the variable in the caller's scope and the variable in the function's scope point to two different objects (but with the same value, initially).
  • If it's a reference type, it copies the reference only, so that that reference in the caller's scope (the one passed as argument) and the reference in the function's scope both point to the same same object, but while the references held by the two variables are practically one and the same, the two variables are not themselves one and the same, so reassigning one does not also reassign the other.

Pass by reference

Passing a variable by reference is very simply what it just sounds like: passing a variable, to a function, by creating a reference to the variable itself.

As you might have deduced, no matter what semantics the passed in object (via its variable) has by default (value object type or reference type), C# will always take a reference to it and pass it as argument to the function.

To elaborate:

If the variable passed in is a value type , then it will no longer be passed by value and cloned before entering the body of the function, but will actually be passed by reference like any other reference type object. C# will take a reference to the value type object (to its variable) and make the variable inside the function's scope point to the same value in memory as the variable from the calling scope.

So this is almost the same behavior like the one described above for pass by value for reference types, thus anything you do with that reference will affect the outside variable too, but this time including reassignment!

Also, because of the value semantics of value types, = (re)assigns (new) values to whatever a variable points to.

  • If the variable holds a value type, the value type is exchanged.
  • If the variable holds a reference to a reference type, the reference is exchanged.
  • If the variable holds a reference to another variable (due to the ref modified) thanks to C#'s automatic dereferencing, the value of the variable that this variable points to is actually being exchanged (😵‍💫 I know), while this variable continues to hold a reference to the other variable and its new value.

So between two variables that hold references to the same value types, if you reassign one of those variables, C# replaces the value held at the address that that variable points to. In a way, value types are also references, but they just have different semantics (and different treatment at runtime).

If the variable passed in is a reference type , then a reference to that variable will be created (a reference to a reference type), so if you reassign to that reference for example, the outside variable will reflect this change.

Note that the above two cases boil down to the same behavior: a reference to the outside variable is created and we work with that reference in the function's scope. Altering it, alters the outside variable too. Calling methods on it that might modify it, also modifies the object pointed to by the outside variable.

To conclude, value types can almost be confused with the variables that hold them, while reference types sort of sit separately from the variables that hold them and C# automatically dereferences them on access, and when using the ref modifier, new references to the arguments will be created (variables pointing to other variables, sort of like becoming aliases of them).

Here is some test code to support the ideas above:

I almost forgot, but passing by reference requires that variables be initialized when passed in (you cannot pass in null variables).

Passing by input reference

This is a special case of passing by reference where you're not allowed to modify (assign to) the variable inside the function, but you can call methods on it. Essentially the variable is readonly inside the function body (but the object is still modifiable internally via its interface).

This is good for functions that might want to take a reference to an object from an outside scope and call methods on it, but ensure that the external variable still points to the same object and that it was not swapped with another object instance, inside the function. (Might seem like a performance optimization but in most cases it's not.)

You also don't need to use the in keyword when calling the function like you do when using ref . In the calling scope, there's no need to be aware that the variable you're passing as an argument will be passed by reference, because it cannot be modified (reassigned) unexpectedly.

This also requires that the reference be already initialized before calling the function or else you'll get a null reference exception.

Passing by output reference

This is the last special case of passing by reference, which is exactly like ref but you don't need to initialize the variable before calling the function.

You do however need to assign a value to the out parameter before returning from the function.

You also need to use the out keyword when declaring and calling the function (you need to be aware of this in the calling scope because the variable is not readonly inside the function and could be reassigned).

Using out can make code more readable and can consolidate for example, multiple operations on multiple variables inside one function by having those variables be returned back to the calling scope without having to pass them as arguments, and also encapsulate them in the function's return type. This way you could use the function's return type for error reporting for example. Or you could use tuples and exceptions for error reporting.

Closing note

At this point, I mainly write these blog posts to help myself consolidate what I've learned, but I hope it can help others like myself as well. While the quality of my posts now is more of a rough draft, my focus is on learning by taking notes. I do try to keep these notes to some standard of quality though and I will try to increase the quality as time goes by.

And hey, if you have anything constructive to say or anything to ask, feel free to use the comments section below.

  • Blogs by Topic

The .NET Tools Blog

Essential productivity kit for .NET developers

  • Guide Guide

Ref local re-assignment – C# 7.3 in Rider and ReSharper

Maarten Balliauw

The Early Access Preview (EAP) versions of  ReSharper 2018.2  and  Rider 2018.2  come with support for C# 7.3. In this series, we are looking at these  new language features in C# 7.3 . Today, we let’s check out ref local re-assignment .

This post is part of a series:

  • Declaration expressions in initializers and queries
  • Tuple equality
  • Unmanaged, delegate and enum type constraints

Ref local re-assignment

  • Fixed pattern and simplified access to fixed size buffer elements
  • C# updates for stackalloc

In C#, it’s been possible for a while to work with references, using the  ref  keyword. For example, it’s possible to return a reference to a  Person  that is already in memory, rather than return a by-value version that will be copied.

If we’d call our method and assign the variable locally, this would essentially read the value of what was returned, and then assign it to a new variable:

var person = people.GetPerson(123);

Whenever we now make a change to the person, that change is made to the local copy of  person , not to the one that was originally returned by  GetPerson() . Even though that method returned a  Person  by reference.

C# 7.2 and C# 7.3 added a number of additions to working with references and the  ref  keyword. In C# 7.2, the ability to replace a referenced variable was added. Consider the following example:

We have effectively re-assigned  people[0]  in this case, which may (or may not) be intentional. Use with caution!

Perhaps this was unintentional after all, and all we wanted to do was to re-use the  personRef  variable to hold a reference to another object. For those cases, C# 7.3 adds ref local re-assignment ( spec ), to make it possible to re-assign an existing reference:

Do keep in mind that, for example, changing  personRef.Name  will update the original person in our array as well.

Download ReSharper 2018.2 EAP now!  Or give  Rider 2018.2 EAP  a try. We’d love to hear your feedback!

Subscribe to Blog updates

By submitting this form, I agree that JetBrains s.r.o. ("JetBrains") may use my name, email address, and location data to send me newsletters, including commercial communications, and to process my personal data for this purpose. I agree that JetBrains may process said data using third-party services for this purpose in accordance with the JetBrains Privacy Policy . I understand that I can revoke this consent at any time in my profile . In addition, an unsubscribe link is included in each email.

Thanks, we've got you!

Discover more

reference assignment c#

12 Debugging Techniques In JetBrains Rider You Should Know About

Twelve must know debugging features in JetBrains Rider every developer should know.

reference assignment c#

Interceptors – Using C# 12 in Rider and ReSharper

Welcome to our series, where we take a closer look at the C# 12 language features and how ReSharper and Rider make it easy for you to adopt them in your codebase. If you haven’t yet, download the latest .NET 8 SDK and update your project files! In this series, we are looking at: Primary …

Matthias Koch

Use JetBrains AI Assistant To Help You Understand Changes In Your Code

JetBrains AI Assistant used to explain commits, contributors, and changes in your codebase.

reference assignment c#

Another Look into the Future with Rider’s Predictive Debugger

In the 2023.2 release cycle, we’ve introduced the Predictive Debugger in ReSharper, which gives you predictions about code paths and variables beyond the current execution pointer. We’ve written extensively about its advantages compared to alternative debugging strategies like thorough thinking, log…

Value and reference types in C#

Jan 30, 2016 • Carlos Schults • Tags: csharp beginners oop type system

reference assignment c#

This is my first “real” post here on my blog, and I decided to talk about value types and reference types. This is somewhat of a basic subject, in the sense that it is something that you should already know if you write C# code for a living.But at the same time, it can be a little non-intuitive if you’re not an experienced developer. I’ll try to make this as simple and short as possible, so I won’t talk about string immutability, boxing, unboxing, and some other things with fancy names. I’ll write about those in the future, but for today let’s focus on the basics.

So, take a look at the following piece of code.

No surprises here, cause this is the exact behaviour you’d expect. Now, suppose we have a class like this:

Now, you do this:

Now is the time when beginners sometimes get confused. “This doesn’t make sense”, they say. “I’ve changed the value of one of the variables, and the other also changed.”

Well, that is the wrong way to look at it. As it turns out, you didn’t really change the value of the variable , in this case. And that’s the point.

Remember the first example? Let’s take a look at it again, this time step by step:

The figure show a sample of C# soure code, in which a variable is being declared and assigned the value 10

We declare the first variable and assign the value 10 to it. Now, somewhere inside the computer’s memory, there is a “box” labeled “x” which holds the value 10.

The image show a sample of C# code, in which a variable y is being declared, and the value of x is assigned to it.

We declare the second variable and assign the value of the first variable to it. Notice that the value is copied from x to y. Now we have two boxes, one labeled x, the other labeled y, and both of them hold the value 10.

The image show a sample of C# code, in which a variable y gets incremented by 1.

Finally, we increment the value of the variable y by 1. Notice that the value of x remained unchanged. Of course, because they have nothing to do with each other!

On the other hand, something very different happens when you’re dealing with reference types. To understand that, let’s review the second example, step by step:

The image show a sample of C# code, in which a variable is assigned a new instance of the Person class

First, we create a variable called person and assign to it a new instance of the Person class. Now, somewhere inside the computer’s memory, we have a box called “person”. Notice, however, that our variable person stores a reference that points to the instance of Person, instead of storing the instance itself!

The image show a sample of C# code, in which a variable person2 is created and receives the value of the variable person.

In the second step, we create a variable called person2 and assign the value of the variable person to it. As in the previous example, the value from one variable is copied to the other. In this case, the value that is copied is the reference that points to the instance . So now we have two variables whose values are references that point to the same instance of Person.

Now, of course when you change the object’s data (e.g. alter the person’s name and/or profession), it seems that the two variables were altered. In fact, the variables’s values remain exactly the same: what was really changed was the object to which they point to.

So, we could summarize that way: value types store data. When you assign the value of a value type variable to another, what gets copied is the data itself (like an integer, for example). This is similar to when you copy a file from one folder and paste it somewhere else. You made a copy, but from now on, they are independent files and have nothing to do with each other.

Reference types store a reference, that points to the data, which lives somewhere else in the machine’s memory. When you assign the value of a reference type variable to another, what gets copied is the reference.It can be useful to think of them as shortcuts to files. If you have one or more shortcuts that point to a particular file, when you make changes in the original file, those changes will be visible through the shortcuts. Because they’re just that, shortcuts.

Which types are value types?

According to MSDN, the value types are:

  • All numeric data types
  • Boolean, Char, and Date
  • All structures
  • Enumerations

Which types are reference types?

Again, acording to MSDN:

Wait a minute! String is a reference type?!

Maybe you’re surprised to see String listed as a reference type. After all, it seems to behave as a value type. Well, the reason for this is because String is an immutable type . As I said, I won’t talk about this today, but soon we’ll have a post all about System.String.

OK, that’s it for today. I hope you guys enjoyed this post, and I’d love to hear your feedback.Have I expressed myself clearly enough? Have I said something innacurate - or even flat-out wrong? Let me know in the comments, or reach me on twitter .

References:

  • https://jonskeet.uk/csharp/references.html
  • https://msdn.microsoft.com/en-us/library/t63sy5hs.aspx

How to Handle Null References in the Latest Version of C#

C# 12 has just been released, and it continues the long tradition of improvements in the safety of the language's software design and execution.

One of these improvements relates to manipulating null references, a programming concept that many developers don't really love.

Using null references in your code can cause all kinds of issues, like exceptions and a lack of information.

This article will teach you how to cope with null references in the latest version of the C# programming language and .NET. The name of the game: let no null pass unattended.

This demonstration will have several stages, each with its own small demo. If you wish to skip through, please use the table of contents below.

Table of Contents

Prerequisites, how to use nullable reference types, how to use the is null and is not null patterns, how to use type-test-and-set patterns, how to use property patterns, how to use the null propagation and null coalescing operators.

  • How to Work with Optional Objects

Final Notes

There are a few prerequisites you will need to meet before proceeding. I assume that you've written enough C# code to see null references in their natural habitat. And I expect you to understand that they can threaten the code's design and stability.

This article will clarify these concepts and identify the issues and solutions using C# syntax and libraries.

If you are ready, we can get started with nullable reference types. That will allow us to set up the working environment and get up to speed for the more complex demos that will follow.

Nullable reference types were introduced in C# 8 and quickly became a mainstay.

The short story is that you can either declare a reference nullable (for example, string? s ) or non-nullable ( string s ).

Note the plot twist: what used to be just a reference before C# 8 ( string s was an ordinary reference to a nullable string) has now become something more: a reference that should never be set to null.

That was the breaking change, maybe the first in a decade of C# syntax evolution!

The compiler will do its best to check if all the assignments to a non-nullable reference (the one without the question mark) set it to a proper object. If it finds an execution path that might set it to null, the compiler will issue a compile-time warning. This is called "definite assignment analysis," as the compiler tries to prove that each non-nullable reference is definitely assigned to an object.

If you have already grown accustomed to nullable reference types, I have a question: would you consider not using them today? Probably not.

Let's start with some code. Below, you see two records – one deriving from another. Record types came with C# 9. I am using them here only for brevity. Consider these two types as just the base and the derived class.

We can either instantiate a record and assign the instance to a reference, or assign a reference to null.

This is where the definite assignment analysis comes to the table. If there is a sequence of instructions in which the reference ends up being null, we must use the question mark to indicate that the reference can be null.

You can see that the second reference ( bob ) is assigned to a proper object but is still declared nullable. That is perfectly fine in scenarios where an object is coming from the outside, and you might not know whether it will be there or not.

Make sure you don't assign a nullable reference to a non-nullable one, though. That would cause a compile-time warning, which you can raise to the level of compile-time error if you prefer.

It is essential to understand that nullability is not the property of the type but rather a hint given to the compiler. Nullable reference types are only used during the compile-time analysis and are never stored in the compiled type itself.

One consequence is that you cannot declare a nullable type parameter in a generic type. That wouldn't make sense because the compiler has no place to put that information in the compiled type!

But then comes a twist because we are free to indicate any reference of the generic parameter type as nullable, as in the code below. Such a reference is subject to definitive assignment analysis as any other.

We have defined the utility function to showcase all situations incorporating null references in the rest of this article. As I already pointed out, it would be a compile-time error to declare this generic function as Showcase<T?> , while accepting a nullable T? in the argument list would be perfectly valid. Makes your head spin around!

An even greater mystery is to come: why not remove nullable from the argument list? What would that mean?

That would leave it to the caller to determine nullability because – now pay attention! – a concrete generic parameter type can be nullable. It determines the nullability of references, which is a real thing during compilation.

I hope you have started to grasp these concepts more now. Unfortunately, it would take a lot of space to explain this concept in-depth, but I would certainly advise you to learn more about nullability of types. It is now part of C# and is here to stay.

Let me give you a quick demo showcasing the two possible choices:

The first call above allows null references in the arguments, while the second call forces non-nullable references. So the compiler would check the references passed as arguments in that case and raise a warning if any of them is, or could be, null.

That concludes our crash course on nullable reference types in C#. We are ready to proceed with more advanced matters.

Before that, here is the output produced by the code as we have it so far:

Pay attention to the empty line in the output. That is where we have passed null to the Console.WriteLine . The WriteLine method accepts null and treats it the same way it treats an empty string.

Once we get nullability right, we can start doing logic around it. The simplest of all operations is asking if a reference is equal to null.

The is operator is testing an object against a pattern. We'll meet this operator several more times in the upcoming sections.

In this demo, you can see its simplest use: testing against the null pattern. There are two possibilities there, is null and is not null , with the meaning that appears to require no further explanation. Oh, but that would be a big mistake!

A corner case is covered by is null and is not null patterns, which might be the core reason for introducing these patterns in the first place. Both patterns will avoid calling any overload of the == and != operators.

So, in theory, a class could overload the == and != operators and, in doing so, declare that a particular object should be considered equal to a null reference. But the is null pattern will not call the operator overload – thus, it will flatly reject comparing that same non-null object to null.

That is a minor corner case, but it teaches how C# operates under the hood. The bottom line is: you should favor is null over == , and is not null over != when testing for null/non-null.

Here is the printout produced when we run the function above on a few references, one of them being null.

The time has come to raise the bar and use some of the more complex methods of processing nullable references. We will remain with the is operator, but this time, we'll use its more potent form: testing type patterns.

Each reference in C# resolves into an object (or a lack of – a null), and each object we reference possesses the type descriptor. That is at the core of any object-oriented language.

So it's pretty easy for the .NET runtime to check whether a reference is pointing to an object – and, if so, whether that object's runtime type derives from a specific type, directly or indirectly.

That was a mouthful, wasn't it? Let's split that up into bits:

  • To test whether a reference references an actual object, that is the person is not null pattern.
  • To add the test whether that object is assignable to a particular type, we use the type pattern instead: person is Celebrity .
  • Finally, to capture the reference to the desired type and use it in subsequent statements and expressions, we use the full-blown type-test-and-set expression: person is Celebrity celeb .

These are the three stages of extracting information from a reference, each more potent than the other.

Without further ado, here is the method that exercises the most detailed form: testing against null and downcasting, all packed in one condensed expression:

You may have noticed that these expressions are effectively implementing safe downcasting. Downcasting was frowned upon for decades, accused (mostly rightfully) of causing code defects and design flaws.

But times they are a-changin'! Type test and set expressions are coming to software development from functional programming.

This article is not a place to discuss the differences between type testing and downcasting as we knew it in object-oriented languages of the past. I strongly encourage you to learn more about this intriguing topic before judging.

Here, you can see the output produced by the function above. As you can see, each actual type is captured correctly, creating its specific output. And the dreaded null was left out – I have indeed passed a null reference to the function at one instant but hadn't matched any of the patterns, and so was ignored.

This demo would be incomplete without one crucial note. The switch expression (of C# 8) is expecting patterns in order from more specific to more general ones. It would be an error to list a more specific pattern after a more general one. The general pattern would overshadow the subsequent one, never letting its right hand execute. Therefore, the switch expression like the one below causes a compile-time error in C#.

An exciting development follows if you push pattern matching even further. One specific form is the properties pattern – one aimed to match the values and attributes of properties of an object (if the object exists!).

You don't have to specify the type if you are not interested in downcasting. It will be the type of the reference to the left of the is operator.

But using the is operator implies a null test. Any reference passing the is test will be non-null and safe to check its property values on the right-hand side of the expression.

Therefore, we read this if instruction's condition as follows: If person is not null, and its property FirstName has a value Bob, then...

Here is the output produced when we call the function above:

So far, we have been doing things to objects, which is awkward in an object-oriented design. Remember, in object-oriented programming, it is the object that exposes behavior, and, as the object's users, we only make calls to its methods.

The problems still come when the reference we expect to point to an object is nullable. Making an unguarded call on the null reference was the primary source of defects. But now, with nullable references and definite assignment checks done for us, we should be safe from the dreaded NullReferenceExceptions .

Consider having a method exposed by the class. We can use ToString as a simple example.

There is a substantial difference between calling ToString on Person and on Person? types. The latter one is nullable, and therefore an unguarded call might cause dereferencing a null reference, leading to a dreaded NullReferenceException , as you can imagine.

Enter the null-propagation operator ( ?. )! We can safely make an optional call to a method, provided the reference is non-null.

But observe the consequences. If the method returns void , the call will be ignored on a null reference. If the method returns a type, then the result will be the nullable version of that type. You cannot expect a string from ToString on a nullable reference, you see? The compiler can only promise a nullable string instead.

And what if we really wanted a string, a true one? Enter the null-coalescing operator ( ?? )! We can easily convert a nullable reference to a non-nullable one by supplying a default to take when the actual value is null at run time.

In this example, we make an optional call to the ToString method first but then short-circuit the result to an empty string if the reference were null. The result is that any null reference would produce an empty string for printout.

How to Work With Optional Objects

The last method of addressing nulls in this article will actually not use nulls. Another riddle! The idea is to avoid nulls altogether by modeling the objects as possibly missing. Mind the word "possibly" – that will become part of the type declaration the same way nullability was.

If you are new to optional objects, then this short explanation will be anything but sufficient to learn about them. C# has no native support for optional objects. You can choose one of the many implementations available on NuGet, the most popular one being the LanguageExt library.

An optional object of some type is an object that either exists or does not exist. Whichever the case, the optional object itself will always exist. Another riddle for you to solve!

Here is how we would declare a few optional objects:

The two shapes of an optional object are usually referred to as None and Some . The Some variant must contain an actual object. That completes the creation of optional objects and the code that will never have a null reference.

But what is the difference compared to nullable references? Why should we use optional objects at all?

The short story is that optional objects let us apply functions to the optional object's content – if present. The optional object will either invoke the function and pass the content to it or skip calling it altogether if there is no content.

Therefore, an optional type is a single place where that calling protocol is now implemented, the protocol in many ways equivalent to safely dereferencing nullable references.

The Match method covers both possibilities: It either maps the Person object to a string or substitutes an empty string if the person is missing. The Do method will only pass content to the console if content exists.

Here is the printout produced by the Do method:

You will only see the Some variants printed out. The only missing object in the input array has produced no output because that optional instance has ignored the action passed to its Do method.

The most significant benefit of using optional objects over nullable references is their ability to apply other functions. We might already have many different classes and methods implemented in our codebase, all methods working on non-nullable references. Optional objects can bridge the gap between potentially missing objects and the common methods that are only operational when nothing is missing.

In this tutorial, we started by declaring nullable objects and testing their existence using the is operator.

Then, we extended the example by displaying the richness of pattern-matching expressions: type test and set and property pattern expressions.

We then moved the focus from consuming objects to calling their behavior from the null-propagation operator over the null-coalescing operator, landing in the vast field of functional programming and optional objects.

I hope you enjoyed the ride. In place of farewell, I will invite you to learn more about optional objects in C# by watching my recent video How to Avoid Null Reference Exceptions: Optional Objects in C# .[

Zoran Horvat is a software developer since the end of the last century, author at Pluralsight and Udemy, YouTube creator, and a speaker at conferences and user groups.

If you read this far, thank the author to show them you care. Say Thanks

Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started

C# Tutorial

C# examples, c# assignment operators, assignment operators.

Assignment operators are used to assign values to variables.

In the example below, we use the assignment operator ( = ) to assign the value 10 to a variable called x :

Try it Yourself »

The addition assignment operator ( += ) adds a value to a variable:

A list of all assignment operators:

Get Certified

COLOR PICKER

colorpicker

Report Error

If you want to report an error, or if you want to make a suggestion, do not hesitate to send us an e-mail:

[email protected]

Top Tutorials

Top references, top examples, get certified.

  • Trending Categories

Data Structure

  • Selected Reading
  • UPSC IAS Exams Notes
  • Developer's Best Practices
  • Questions and Answers
  • Effective Resume Writing
  • HR Interview Questions
  • Computer Glossary

How to assign a reference to a variable in C#

To assign reference to a variable, use the ref keyword. A reference parameter is a reference to a memory location of a variable. When you pass parameters by reference, unlike value parameters, a new storage location is not created for these parameters. Declare the reference parameters using the ref keyword.

Let us see an example −

Here, we are swapping two values using the ref keyword −

 Live Demo

Ankith Reddy

  • Related Articles
  • Can we assign a reference to a variable in Python?
  • Reference assignment operator in PHP to assign a reference?
  • How to assign a PHP variable to JavaScript?
  • How can we assign a function to a variable in JavaScript?
  • How to assign multiple values to a same variable in Python?
  • Is it possible to assign a reference to "this" in java?
  • What is a reference variable in C++?
  • How to assign multiple values to same variable in C#?\n
  • How do I assign a dictionary value to a variable in Python?
  • Assign other value to a variable from two possible values in C++
  • How to assign the result of a MySQL query into a variable?
  • How to assign int value to char variable in Java
  • What are the differences between a pointer variable and a reference variable in C++?
  • In C++ What are the differences between a pointer variable and a reference variable?
  • How can we assign a bit value as a number to a user variable?

Kickstart Your Career

Get certified by completing the course

blog post image

Andrew Lock | .NET Escapades Andrew Lock

  • Razor Pages

Fighting with nullable reference types in Razor Pages

In this post I discuss C#'s nullable reference types in the context of Razor Pages: why the two don't play nicely together, why they probably never will, and how to make the experience a bit nicer.

Nullable reference types: the highs and lows.

I really like working with C#. Recent improvements to pattern matching, for example, can significantly improve the readability of some code (in my opinion), while other improvements like Span<T> unlock performance that wasn't easily achievable previously. There are valid arguments and concerns about the ever increasing complexity of the language, but putting those concerns aside each new feature feels like a definite improvement. Except one, which I constantly struggle with: nullable reference types.

Nullable reference types were introduced in C# 7.3 as an attempt to work around the "billion dollar problem", null as the default for C# reference types, and the ever-pervasive NullReferenceException . This is a laudable goal, as these bugs are often the result of a programming error, misconceptions, broken (implied) API contracts, or a failure to sanitise inputs. Anything that helps with those issues should be a net win!

The trouble is, it's difficult to retrospectively hack that feature into an existing language. The premise of "just add ? to your type definitions and we'll figure it out" is great when it works, but there are all sorts of edge cases that the compiler just can't figure out, or that require a soup of attributes to teach the compiler what to do.

Despite that, I use nullable reference types wherever I can. Sure, there can be annoying edge cases, but for the most part, they provide an extra level of analysis that it would be silly to ignore.

The big exception to that is libraries that need to multi-target older versions of .NET Core or .NET Framework. The lack of nullability annotations for the BCL there can make enabling nullability painful, to the point where it's just not worthwhile IMO.

Unfortunately, there are some patterns and frameworks which just don't lend themselves well to nullability analysis. In my experience, Razor Pages, my preferred framework for server-rendered page-based apps in ASP.NET Core, is one such framework.

Razor Pages and nullable reference types

The Razor Pages framework was introduced in ASP.NET Core 2.0. It provides a set of conventions and features on top of the existing MVC framework to make building traditional "page-based" websites easier. By page-based I generally mean:

  • You navigate to a page
  • You click an edit button, which takes you to the "edit" version of a page, with the details displayed in form elements
  • You post the form results back to the same URL
  • You redirect to another page

This was a very common pattern for years, until the Single Page App (SPA) came along. I'm not going to go into server-rendering vs client-rendering here; the important thing is that Razor Pages can provide a much better developer experience for apps using this pattern than its precursor, MVC controllers.

Razor Pages embraces the fact that the "display form" and "receive POST of form" conceptually are the same page, as opposed to MVC controllers which treats them as two very separate actions. Razor Pages also explicitly ties the UI for a form to the backend logic, which is what you do in MVC 99% of the time.

Anyway, enough background, let's look at the problem of nullable reference types in Razor Pages, by taking an example

The sample page

We'll start with a simple Razor Page. I created a basic page which contains:

  • A Get handler and a Post handler
  • A binding model defined as a nested type InputModel which binds to the property Input
  • An "output" value, Result , which is populated after a successful form post
  • A form for posting back in the HTML

The code behind, which is what we're mostly focused on here, looks like this:

For completeness, the .cshtml file looks like this:

which, when run, looks like this:

The razor page rendered

Nothing wrong or controversial so far…until you look at the build warnings generated by this simple page, summarised below:

Handling the simple cases.

Some of the complaints here are perfectly valid, Result , for example. Result may or may not be set, depending on which handler runs, and on whether the model is valid, so updating that with a nullable reference ( string? ) makes perfect sense:

One down, three to go. But this is where things get tricky, and somewhat philosophical.

Analysing model bound properties

Let's think about FirstName and LastName in the InputModel to start with. For simplicity, I've repeated the definition below:

Both properties are described as a string without the nullable ? annotation. Logically we know that if the model has been validated correctly, these can't be null, so the string makes sense there, right?

But that's only if model binding succeeded. If it didn't, or indeed, if we manually create an instance, then these values could be null. So actually, marking them as nullable would make sense? Probably?

The trouble with that is the only place we access those properties directly are in the OnPost handler after we've validated the model . Which means a bunch of null-conditional access ? are required in places we know it's not null :

Or we could use ! of course, which is more "correct" in some sense (as we know it's not null ), and may be better in specific cases. But I normally feels like I'm doing something wrong when I use it…

So this has kind of solved the issue, but it's mostly just a bunch of extra noise to keep the compiler happy because it can't understand how the interaction between the BCL, type system and framework here (which is perfectly reasonable!).

It's a bit unsatisfying, but let's leave this one for now. Let's look at the instance of InputModel exposed on the PageModel .

The problematic binding model

The final warning we have is for the InputModel property on the PageModel itself;

So, this is where things get tricky. We don't declare InputModel as nullable, because, similar to its properties, it won't be when you use it properly.

Specifically, in the OnPost handler, Input is always non-null, because the Razor Pages framework creates an instance of it and sets the value on the model. So in theory, it's always non null.

Except, it will be null in the OnGet handler. Razor Pages doesn't bind [BindProperty] models on GET unless you specifically tell it to, so the model is left null . That's completely expected for Razor Pages conventions, but it causes us an issue:

  • Input is always null in OnGet
  • Input is never null in OnPost

Clearly we could mark Input as nullable using InputModel? , but then we're going to fighting even more with extraneous ? and ! for a property we know won't be null. And you have to do this in every single Razor Page. Urgh 😩

Possible solutions

The nullable reference type feature is meant to guide you into catching bugs, by annotating your types so they more accurately represent the possible values they can represent. This is a good thing generally. The problem is, it falls down when you have a framework like Razor Pages which is calling into your methods and instantiating your types independently of the "normal" program flow.

In this situation, marking the Input property as InputModel? doesn't really give me any more information. All my Razor Pages use this same pattern, and they all have a null value in OnGet and a non-nullable value in OnPost . Adding the ? annotations doesn't give me any new information, or help to catch bugs; it's just noise.

Coming to the realization finally made me reach out for some other solution. I've described some of the options below, but honestly, I'm not particularly satisfied with any of them. They all have pros and cons, but mostly they add noise for not much extra value.

1. Suck it up

Ok, option 1 is just suck it up and use ? and ! as appropriate. Mark InputModel as ? , and use ?. or !. when accessing it. Maybe it's fine. Maybe I'm being overly sensitive.

One of my big complaints with this is that it degrades the whole value proposition of nullable reference types. If we say "yeah, it doesn't work in Razor Pages" then trying to espouse it's benefits becomes harder. Yes it's a different situation in Razor Pages than in your library code, but it still muddies the waters for newcomers.

Plus it's ugly, and annoying. Still, it's an option.

2. #nullable disable

The other option is just to accept that nullable reference types don't work in Razor Pages . That's reasonable, Razor Pages was released before nullable reference types, and they're just not compatible. So why not turn it off?

You can easily disable nullable reference types for a single file by adding #nullable disable to the top of the file:

This isn't a bad solution. You can leave nullability enabled for the rest of your project, and just disable it in each of your Razor Pages. This is even what the ASP.NET Core Identity scaffolded pages do: they set #nullable disable at the start of every page.

I have 2 concerns with this approach:

  • You have to add it to every single page.
  • This may miss nullability bugs when interacting with the rest of your application.

That first point is that this is just annoying cruft again. I tried adding #nullable to _ViewStart.cshml in the vain hope that it would be applied to every file, but no luck.

The second point is a bigger concern. If your Razor Pages are interacting with some application/domain service, the application/domain service will encode the nullability of the response in the type system. But if you've disabled nullable reference types in your Razor Pages, you could introduce issues that would have been caught if you hadn't added #nullable disable .

For example, imagine I have a user service defined like this:

and I use it in a Razor Page like this:

I've just introduced a potential NullReferenceException that would have been a warning previously.

3. It's not null , dammit !

One option I've seen people using in the wild is setting the property to null! , which says "set this property to null and tell the compiler it's definitely not null ". Putting aside the moral question of lying to a computer, this works surprisingly well:

This is a pretty good position. The compiler is satisfied, it's relatively minimal changes required in our Razor Page, and we're not overriding nullability checks for any of the other code in our app. This was my go-to approach. But with C# 11 coming soon, there's another possibility…

4. C# 11's solution: required

C# 11 is introducing a new modifier for fields and properties, required . As per the docs :

The required modifier indicates that the field or property it's applied to must be initialized by all constructors or by using an object initializer.

While not tied to init , required is the missing step that actually makes it useful (in my opinion). You can use init to make a property read-only except during object construction. For example:

As you can see in the above example, you can set FirstName only in the object initializer (or in a constructor), after that it's readonly. The trouble is, prior to C# 11, there was no way to force you to set it. That's where required comes in. required makes it a compiler error to not set the property in the constructor or in an object initializer. So in the above example, this is a compiler error:

So how does this help us?

If you mark a property as required , then the compiler knows that the creator of the instance has to set the property, so it doesn't have to worry about the fact we haven't set it.

Long story short, we can mark the Input property as required , and all the problems go away:

The Razor Pages framework sets the property via reflection, so it doesn't interact with it at all (reflection ignores init / required ), but we're still getting kind of the best behaviour.

Remember that Input is null inside the OnGet handler, so there's still a "correctness" agument to be made here, but that's essentially unavoidable.

We could revisit the InputModel example, and make them required instead of string? too:

I'm unsure which is the best approach on this one, as these could be null if you're using the values in an "error" path, so I think I prefer to mark those as nullable.

Wrapping up

In summary, the final Razor Page after adding all the extra annotations looks something like this:

I think this gives a reasonable balance of correctness and terseness, but I'm curious which approaches others using Razor Pages have settled on?

Nullable reference types provide a safety net in C#, to avoid the infamous NullreferenceException . The problem is that they don't play well with Razor Pages, as the framework instantiates properties using reflection. This causes a lot of compiler warnings in a typical Razor Pages app. To avoid these warnings, you have to decorate your pages with ? and ! , even if it's impossible for properties to be null thanks to the patterns used by Razor Pages.

In this post I discussed some of the options for working around these issues. I have tried to find the most practical approaches, which minimize typing, while also ensuring correctness wherever possible. My end solution of using required and ? is about as good as it's going to get I think.

Popular Tags

reference assignment c#

Stay up to the date with the latest posts!

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Resolve nullable warnings

  • 5 contributors

This article covers the following compiler warnings:

  • CS8597 - Thrown value may be null.
  • CS8600 - Converting null literal or possible null value to non-nullable type.
  • CS8601 - Possible null reference assignment.
  • CS8602 - Dereference of a possibly null reference.
  • CS8603 - Possible null reference return.
  • CS8604 - Possible null reference argument for parameter.
  • CS8605 - Unboxing a possibly null value.
  • CS8607 - A possible null value may not be used for a type marked with [NotNull] or [DisallowNull]
  • CS8608 - Nullability of reference types in type doesn't match overridden member.
  • CS8609 - Nullability of reference types in return type doesn't match overridden member.
  • CS8610 - Nullability of reference types in type parameter doesn't match overridden member.
  • CS8611 - Nullability of reference types in type parameter doesn't match partial method declaration.
  • CS8612 - Nullability of reference types in type doesn't match implicitly implemented member.
  • CS8613 - Nullability of reference types in return type doesn't match implicitly implemented member.
  • CS8614 - Nullability of reference types in type of parameter doesn't match implicitly implemented member.
  • CS8615 - Nullability of reference types in type doesn't match implemented member.
  • CS8616 - Nullability of reference types in return type doesn't match implemented member.
  • CS8617 - Nullability of reference types in type of parameter doesn't match implemented member.
  • CS8618 - Non-nullable variable must contain a non-null value when exiting constructor. Consider declaring it as nullable.
  • CS8619 - Nullability of reference types in value doesn't match target type.
  • CS8620 - Argument cannot be used for parameter due to differences in the nullability of reference types.
  • CS8621 - Nullability of reference types in return type doesn't match the target delegate (possibly because of nullability attributes).
  • CS8622 - Nullability of reference types in type of parameter doesn't match the target delegate (possibly because of nullability attributes).
  • CS8624 - Argument cannot be used as an output due to differences in the nullability of reference types.
  • CS8625 - Cannot convert null literal to non-nullable reference type.
  • CS8629 - Nullable value type may be null.
  • CS8631 - The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type.
  • CS8633 - Nullability in constraints for type parameter of method doesn't match the constraints for type parameter of interface method. Consider using an explicit interface implementation instead.
  • CS8634 - The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'class' constraint.
  • CS8643 - Nullability of reference types in explicit interface specifier doesn't match interface implemented by the type.
  • CS8644 - Type does not implement interface member. Nullability of reference types in interface implemented by the base type doesn't match.
  • CS8645 - Member is already listed in the interface list on type with different nullability of reference types.
  • CS8655 - The switch expression does not handle some null inputs (it is not exhaustive).
  • CS8667 - Partial method declarations have inconsistent nullability in constraints for type parameter.
  • CS8670 - Object or collection initializer implicitly dereferences possibly null member.
  • CS8714 - The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
  • CS8762 - Parameter must have a non-null value when exiting.
  • CS8763 - A method marked [DoesNotReturn] should not return.
  • CS8764 - Nullability of return type doesn't match overridden member (possibly because of nullability attributes).
  • CS8765 - Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
  • CS8766 - Nullability of reference types in return type of doesn't match implicitly implemented member (possibly because of nullability attributes).
  • CS8767 - Nullability of reference types in type of parameter of doesn't match implicitly implemented member (possibly because of nullability attributes).
  • CS8768 - Nullability of reference types in return type doesn't match implemented member (possibly because of nullability attributes).
  • CS8769 - Nullability of reference types in type of parameter doesn't match implemented member (possibly because of nullability attributes).
  • CS8770 - Method lacks [DoesNotReturn] annotation to match implemented or overridden member.
  • CS8774 - Member must have a non-null value when exiting.
  • CS8776 - Member cannot be used in this attribute.
  • CS8775 - Member must have a non-null value when exiting.
  • CS8777 - Parameter must have a non-null value when exiting.
  • CS8819 - Nullability of reference types in return type doesn't match partial method declaration.
  • CS8824 - Parameter must have a non-null value when exiting because parameter is non-null.
  • CS8825 - Return value must be non-null because parameter is non-null.
  • CS8847 - The switch expression does not handle some null inputs (it is not exhaustive). However, a pattern with a 'when' clause might successfully match this value.

The purpose of nullable warnings is to minimize the chance that your application throws a System.NullReferenceException when run. To achieve this goal, the compiler uses static analysis and issues warnings when your code has constructs that may lead to null reference exceptions. You provide the compiler with information for its static analysis by applying type annotations and attributes. These annotations and attributes describe the nullability of arguments, parameters, and members of your types. In this article, you'll learn different techniques to address the nullable warnings the compiler generates from its static analysis. The techniques described here are for general C# code. Learn to work with nullable reference types and Entity Framework core in Working with nullable reference types .

You'll address almost all warnings using one of four techniques:

  • Adding necessary null checks.
  • Adding ? or ! nullable annotations.
  • Adding attributes that describe null semantics.
  • Initializing variables correctly.

Possible dereference of null

This set of warnings alerts you that you're dereferencing a variable whose null-state is maybe-null . These warnings are:

The following code demonstrates one example of each of the preceding warnings:

In the example above, the warning is because the Container , c , may have a null value for the States property. Assigning new states to a collection that might be null causes the warning.

To remove these warnings, you need to add code to change that variable's null-state to not-null before dereferencing it. The collection initializer warning may be harder to spot. The compiler detects that the collection maybe-null when the initializer adds elements to it.

In many instances, you can fix these warnings by checking that a variable isn't null before dereferencing it. Consider the following that adds a null check before dereferencing the message parameter:

The following example initializes the backing storage for the States and removes the set accessor. Consumers of the class can modify the contents of the collection, and the storage for the collection is never null :

Other instances when you get these warnings may be false positive. You may have a private utility method that tests for null. The compiler doesn't know that the method provides a null check. Consider the following example that uses a private utility method, IsNotNull :

The compiler warns that you may be dereferencing null when you write the property message.Length because its static analysis determines that message may be null . You may know that IsNotNull provides a null check, and when it returns true , the null-state of message should be not-null . You must tell the compiler those facts. One way is to use the null forgiving operator, ! . You can change the WriteLine statement to match the following code:

The null forgiving operator makes the expression not-null even if it was maybe-null without the ! applied. In this example, a better solution is to add an attribute to the signature of IsNotNull :

The System.Diagnostics.CodeAnalysis.NotNullWhenAttribute informs the compiler that the argument used for the obj parameter is not-null when the method returns true . When the method returns false , the argument has the same null-state it had before the method was called.

There's a rich set of attributes you can use to describe how your methods and properties affect null-state . You can learn about them in the language reference article on Nullable static analysis attributes .

Fixing a warning for dereferencing a maybe-null variable involves one of three techniques:

  • Add a missing null check.
  • Add null analysis attributes on APIs to affect the compiler's null-state static analysis. These attributes inform the compiler when a return value or argument should be maybe-null or not-null after calling the method.
  • Apply the null forgiving operator ! to the expression to force the state to not-null .

Possible null assigned to a nonnullable reference

This set of warnings alerts you that you're assigning a variable whose type is nonnullable to an expression whose null-state is maybe-null . These warnings are:

The compiler emits these warnings when you attempt to assign an expression that is maybe-null to a variable that is nonnullable. For example:

The different warnings indicate provide details about the code, such as assignment, unboxing assignment, return statements, arguments to methods, and throw expressions.

You can take one of three actions to address these warnings. One is to add the ? annotation to make the variable a nullable reference type. That change may cause other warnings. Changing a variable from a non-nullable reference to a nullable reference changes its default null-state from not-null to maybe-null . The compiler's static analysis may find instances where you dereference a variable that is maybe-null .

The other actions instruct the compiler that the right-hand-side of the assignment is not-null . The expression on the right-hand-side could be null-checked before assignment, as shown in the following example:

The previous examples demonstrate assignment of the return value of a method. You may annotate the method (or property) to indicate when a method returns a not-null value. The System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute often specifies that a return value is not-null when an input argument is not-null . Another alternative is to add the null forgiving operator, ! to the right-hand side:

Fixing a warning for assigning a maybe-null expression to a not-null variable involves one of four techniques:

  • Change the left side of the assignment to a nullable type. This action may introduce new warnings when you dereference that variable.
  • Provide a null-check before the assignment.
  • Annotate the API that produces the right-hand side of the assignment.
  • Add the null forgiving operator to the right-hand side of the assignment.

Nonnullable reference not initialized

This set of warnings alerts you that you're assigning a variable whose type is non-nullable to an expression whose null-state is maybe-null . These warnings are:

Consider the following class as an example:

Neither FirstName nor LastName are guaranteed initialized. If this code is new, consider changing the public interface. The above example could be updated as follows:

If you require creating a Person object before setting the name, you can initialize the properties using a default non-null value:

Another alternative may be to change those members to nullable reference types. The Person class could be defined as follows if null should be allowed for the name:

Existing code may require other changes to inform the compiler about the null semantics for those members. You may have created multiple constructors, and your class may have a private helper method that initializes one or more members. You can move the initialization code into a single constructor and ensure all constructors call the one with the common initialization code. Or, you can use the System.Diagnostics.CodeAnalysis.MemberNotNullAttribute and System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute attributes. These attributes inform the compiler that a member is not-null after the method has been called. The following code shows an example of each. The Person class uses a common constructor called by all other constructors. The Student class has a helper method annotated with the System.Diagnostics.CodeAnalysis.MemberNotNullAttribute attribute:

Finally, you can use the null forgiving operator to indicate that a member is initialized in other code. For another example, consider the following classes representing an Entity Framework Core model:

The DbSet property is initialized to null! . That tells the compiler that the property is set to a not-null value. In fact, the base DbContext performs the initialization of the set. The compiler's static analysis doesn't pick that up. For more information on working with nullable reference types and Entity Framework Core, see the article on Working with Nullable Reference Types in EF Core .

Fixing a warning for not initializing a nonnullable member involves one of four techniques:

  • Change the constructors or field initializers to ensure all nonnullable members are initialized.
  • Change one or more members to be nullable types.
  • Annotate any helper methods to indicate which members are assigned.
  • Add an initializer to null! to indicate that the member is initialized in other code.

Mismatch in nullability declaration

Many warnings indicate nullability mismatches between signatures for methods, delegates, or type parameters.

The following code demonstrates CS8764 :

The preceding example shows a virtual method in a base class and an override with different nullability. The base class returns a non-nullable string, but the derived class returns a nullable string. If the string and string? are reversed, it would be allowed because the derived class is more restrictive. Similarly, parameter declarations should match. Parameters in the override method can allow null even when the base class doesn't.

Other situations can generate these warnings. You may have a mismatch in an interface method declaration and the implementation of that method. Or a delegate type and the expression for that delegate may differ. A type parameter and the type argument may differ in nullability.

To fix these warnings, update the appropriate declaration.

Code doesn't match attribute declaration

The preceding sections have discussed how you can use Attributes for nullable static analysis to inform the compiler about the null semantics of your code. The compiler warns you if the code doesn't adhere to the promises of that attribute:

Consider the following method:

The compiler produces a warning because the message parameter is assigned null and the method returns true . The NotNullWhen attribute indicates that shouldn't happen.

To address these warnings, update your code so it matches the expectations of the attributes you've applied. You may change the attributes, or the algorithm.

Exhaustive switch expression

Switch expressions must be exhaustive , meaning that all input values must be handled. Even for non-nullable reference types, the null value must be accounted for. The compiler issues warnings when the null value isn't handled:

The following example code demonstrates this condition:

The input expression is a string , not a string? . The compiler still generates this warning. The { } pattern handles all non-null values, but doesn't match null . To address these errors, you can either add an explicit null case, or replace the { } with the _ (discard) pattern. The discard pattern matches null as well as any other value.

Submit and view feedback for

Additional resources

COMMENTS

  1. C# assign by reference

    C# assign by reference Ask Question Asked 11 years, 10 months ago Modified 3 years ago Viewed 36k times 25 Is it possible to assign by reference? I know that ref has to be used in methods. string A = "abc"; string B = A; B = "abcd"; Console.WriteLine (A); // abc Console.WriteLine (B); // abcd Can I have some sort of

  2. ref keyword

    You use the ref keyword in the following contexts: In a method signature and in a method call, to pass an argument to a method by reference. In a method signature, to return a value to the caller by reference. For more information, see ref return.

  3. Assignment operators

    ref assignment Compound assignment Null-coalescing assignment Operator overloadability Show 2 more The assignment operator = assigns the value of its right-hand operand to a variable, a property, or an indexer element given by its left-hand operand. The result of an assignment expression is the value assigned to the left-hand operand.

  4. C# 7: Ref Returns, Ref Locals, and how to use them

    C# has always supported the ability to pass by reference using the ref keyword on method parameters. C# 7 adds the ability to return by reference and to store references in local variables. The primary reason for using ref returns and ref locals is performance.

  5. Assignment operators (C# reference)

    When the operands are of <a href=\"/dotnet/docs/blob/main/docs/csharp/language-reference/builtin-types/reference-types.md\">reference types</a>, assignment copies the reference to the object.</p>\n<p dir=\"auto\">This is called <em>value assignment</em>: the value is assigned.</p>\n<h2 tabindex=\"-1\" dir=\"auto\"><a id=\"user-content-ref-assign...

  6. ?? and ??= operators

    The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, see our contributor guide.

  7. c#

    212 There are numerous questions here. Considering them one at a time: reference assignment is atomic so why is Interlocked.Exchange (ref Object, Object) needed? Reference assignment is atomic. Interlocked.Exchange does not do only reference assignment.

  8. Demystifying C#'s Parameter Modifiers and Value and Reference Types

    The official Microsoft docs say that in C# arguments can be passed to parameters either by value or by reference, by value being the default. Passing by reference enables function members, methods, properties, indexers, operators, and constructors to change the value of the parameters and have that change persist in the calling environment.

  9. Ref local re-assignment

    C# updates for stackalloc; Ref local re-assignment. In C#, it's been possible for a while to work with references, using the ref keyword. For example, it's possible to return a reference to a Person that is already in memory, rather than return a by-value version that will be copied. public ref Person GetPerson(int id) { // ...

  10. Built-in reference types

    C# has many built-in reference types. They have keywords or operators that are synonyms for a type in the .NET library. The object type The object type is an alias for System.Object in .NET. In the unified type system of C#, all types, predefined and user-defined, reference types and value types, inherit directly or indirectly from System.Object.

  11. Value and reference types in C#

    We declare the first variable and assign the value 10 to it. Now, somewhere inside the computer's memory, there is a "box" labeled "x" which holds the value 10. We declare the second variable and assign the value of the first variable to it. Notice that the value is copied from x to y.

  12. C# Reference type assignment VS value type assignment

    using System; class Employee { private string m_name; public string Name { get { return m_name; } set { m_name = value; } } } class Program { static void Main () { Employee joe = new Employee (); joe.Name = "Joe"; Employee bob = new Employee (); bob.Name = "Bob"; Console.WriteLine ("Original Employee Values:"); Console.Write...

  13. How to Handle Null References in the Latest Version of C#

    Nullable reference types were introduced in C# 8 and quickly became a mainstay. The short story is that you can either declare a reference nullable (for example, string? s) or non-nullable ( string s ).

  14. C# Assignment Operators

    Assignment operators are used to assign values to variables. In the example below, we use the assignment operator ( =) to assign the value 10 to a variable called x: Example int x = 10; Try it Yourself » The addition assignment operator ( +=) adds a value to a variable: Example int x = 10; x += 5; Try it Yourself »

  15. Method parameters are passed by value. Modifiers enable pass-by

    In this article. By default, arguments in C# are passed to functions by value.That means a copy of the variable is passed to the method. For value (struct) types, a copy of the value is passed to the method.For reference (class) types, a copy of the reference is passed to the method.Parameter modifiers enable you to pass arguments by reference.The following concepts help you understand these ...

  16. How to assign a reference to a variable in C#

    To assign reference to a variable, use the ref keyword. A reference parameter is a reference to a memory location of a variable. When you pass parameters by reference, unlike value parameters, a new storage location is not created for these parameters. Declare the reference parameters using the ref keyword. Let us see an example −

  17. c#

    Is a reference assignment threadsafe? Ask Question Asked 12 years, 10 months ago Modified 8 years, 9 months ago Viewed 15k times 47 I'm building a multi threaded cache in C#, which will hold a list of Car objects: public static IList<Car> Cars {get; private set;} I'm wondering if it's safe to change the reference in a thread without locking ? e.g.

  18. Using Properties

    C# public class Date { private int _month = 7; // Backing store public int Month { get => _month; set { if ( (value > 0) && (value < 13)) { _month = value; } } } } In this example, Month is declared as a property so that the set accessor can make sure that the Month value is set between 1 and 12.

  19. Fighting with nullable reference types in Razor Pages

    Except one, which I constantly struggle with: nullable reference types. Nullable reference types were introduced in C# 7.3 as an attempt to work around the "billion dollar problem", null as the default for C# reference types, and the ever-pervasive NullReferenceException. This is a laudable goal, as these bugs are often the result of a ...

  20. c#

    2 Answers Sorted by: 2 SelectedObject is an object reference. It is identical with the original object in the list - SelectedObject and the "original" object are the same object. If you are seeing behavior that indicates otherwise, please update your question. Share Follow answered Nov 20, 2012 at 22:54

  21. Resolve nullable warnings

    CS8601 - Possible null reference assignment. CS8602 - Dereference of a possibly null reference. CS8603 - Possible null reference return. CS8604 - Possible null reference argument for parameter. CS8605 - Unboxing a possibly null value. CS8607 - A possible null value may not be used for a type marked with [NotNull] or [DisallowNull]