C# Null safety

T.Zacks 3,986 Reputation points
2022-05-15T07:25:27.827+00:00

when i code in c# with upper version i got warning like difference of a possible null reference i was reading a write up from this url https://learn.microsoft.com/en-us/learn/modules/csharp-null-safety/3-express-intent

i have some questions

1) see this code and tell me difference  
a) FooBar? fooBar = null;  

// Same as FooBar fooBar = default!;  
b) FooBar fooBar = null!;  

they said - This example adds the null-forgiving (!) operator to null, which instructs the compiler that you're explicitly initializing this variable as null. The compiler will not issue warnings about this reference being null.

if it means FooBar fooBar = null!; i am explicitly assign null to fooBar then i can assign something like this FooBar fooBar = null; then what would be difference?

2) can i instantiate like this way ?

FooBar fooBar = new(Id: 1, Name: "Foo");  

suppose FooBar is class which has 3 properties and can instantiate FooBar like this way ?

3) what is meaning of this FooBar fooBar = fooList.Find(f => f.Name == "Bar")!; ?

Thanks

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,307 questions
0 comments No comments
{count} votes

Accepted answer
  1. Karen Payne MVP 35,196 Reputation points
    2022-05-15T08:28:26.13+00:00

    I tend to steer away from the null-forgiving operator to appease the compiler, instead perform necessary asserts. No matter, the best way to learn is to first read the docs rather than jumping into code.

    Here is something I slapped together without the null-forgiving operator

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    public class Data
    {
        public static List<Person> Persons => new()
        {
            new() {Id = 1, Name = "Anne" },
            new() {Id = 2, Name = null }, // not needed
            new() {Id = 3, Name = "Mike" },
        };
    }
    

    Then

    List<Person> people = Data.Persons;
    
    people.ForEach(person => Console.WriteLine(person.Name ?? "null"));
    
    var findName = "Mike";
    Person? mike = people.Find(person => person.Name == findName);
    if (mike is null)
    {
        Console.WriteLine($"Failed to find {findName}");
    }
    else
    {
        Console.WriteLine(mike.Id);
    }
    

    Example with null forgiving, I know the list is good.

    public static List<Country> Countries()
    {
        var list = JsonSerializer.Deserialize<List<Country>>(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Countries.json")));
    
        list!.Insert(0, new Country() { CountryIdentifier = -1, CountryName = "Select" });
    
        return list;
    }
    

1 additional answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 56,931 Reputation points
    2022-05-15T16:19:33.123+00:00

    Null safety is a compile time feature. The compiler will try to detect when a null reference is possible. In your sample code

    // a nullable variable is set to null - no warning
    FooBar? fooBar = null;
    
    // a non nullable variable is set to null - will generate warning
    FooBar fooBar = null;
    
    // a non nullable variable is set to null - will not generate warning
    // you do this when you know the fooBar will be set to a valid value
    // before use, but don’t want to initial to a value that will not be used
    FooBar fooBar = null!;
    

    Because null safety is not built into C# like it it is in rust or swift, the compiler can not trust that external code behaves properly.

    If null safety enforced non nullable variables, you would not be able to use any library not built with enforcement.

    Your final example is a good case

    3) what is meaning of this?

    FooBar fooBar = fooList.Find(f => f.Name == "Bar")!;

    The Find() can return a null, so the compiler has to assume fooBar can also be set to null. The ! Is telling the compiler, you know that will not happen, and to assume in following statements fooBar is not null.

    A better choice (if available) would to use First(), that would throw an error if not found. But you may know that even if fooBar is set to null from Find(), your code will not fall into a code path that would reference it.

    2 people found this answer helpful.