Почему этот код без предупреждения возвращает исключение с нулевой ссылкой?C#

Место общения программистов C#
Ответить
Anonymous
 Почему этот код без предупреждения возвращает исключение с нулевой ссылкой?

Сообщение Anonymous »

Насколько я понимаю, функции безопасности C# (начиная с C# 8.0) заключаются в том, что (возможно) переменные со значением NULL должны быть типизированы как допускающие значение NULL, и что компилятор статически обнаруживает любые части кода, которые могут включать разыменование нулевой ссылки, и выдает предупреждение. В сочетании с Nullable я ожидаю, что это сделает невозможным возникновение исключений нулевых ссылок.
Однако следующий код компилируется, но когда запрос Get отправляется в /endpoint, он возвращает исключение нулевой ссылки:

Код: Выделить всё

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

var connString = "User ID=postgres;Password=password;Host=localhost;Port=5432;Pooling=true;Connection Lifetime=0;Database=MinExample";
builder.Services.AddDbContext(options => options.UseNpgsql(connString));
var app = builder.Build();

app.MapGet("/endpoint", long (AppDbContext context) =>
{
var id = new Service(context).GetBId();
return id;
});

app.Run();

public record A
{
public long AId { get; init; }
public required B B { get; set; }
}

public class B
{
public required long BId { get; init; }
}

public class Service
{
private readonly AppDbContext _context;

public Service(AppDbContext cx)
{
_context = cx;
}

public long GetBId()
{
var a = _context.A.First();
return a.B.BId;
}
}

public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions options)
: base(options) { }

public DbSet A { get; set; }
}
Моя база данных имеет одну запись в таблице A и одну связанную запись в таблице B.
На самом деле компилятор настолько уверен, что id не равен нулю, что если я вставлю Console.WriteLine(id is null) непосредственно перед оператором возврата API, компилятор выдаст ошибку.

Невозможно преобразовать значение null в 'long', потому что это тип значения, не допускающий значения NULL.

С другой стороны, Console.WriteLine(id == null) компилируется и работает нормально (и записывает True на консоль), хотя JetBrains предупреждает меня, что

Результат выражения всегда равен «false», поскольку значение типа «long» никогда не равно 'null' типа 'long?'

Почему компилятор не выдает ошибку о том, что возвращаемая переменная равна нулю? Является ли это нарушением безопасности? Что мне не хватает в том, как работает нулевая безопасность в C# 8.0 и более поздних версиях?
(«Решение» этой проблемы — заменить запрос на

Код: Выделить всё

var a = _context.A.Include(a => a.B).First();
но мой вопрос о том, почему компилятор не обнаруживает, что что-то не так, а не о том, как решить проблему самостоятельно).
Все вышеперечисленное было построено с помощью .NET 9.0.112.

Подробнее здесь: https://stackoverflow.com/questions/798 ... ut-warning
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»