我准备了一个简单的C# Fiddle,它调用an OSRM map matching service URL并解析JSON响应.
我的问题是,有一条生产线生产warning CS8602: Dereference of a possibly null reference
:
if (osrmResponse?.code == "Ok" && osrmResponse.matchings != null)
{
return osrmResponse.matchings
.Where(matching => matching != null)
.SelectMany(matching => matching.legs ?? Enumerable.Empty<Leg>())
.Where(leg => leg != null && leg.annotation != null && leg.annotation.nodes != null)
// How to fix dereference of a possibly null value in the next line?
.SelectMany(leg => leg.annotation.nodes ?? Enumerable.Empty<long>())
// eliminate duplicate node ids by converting to an ISet
.ToHashSet()
.ToList();
}
我不明白,如果我在有问题的行之前判断.Where
中的调用,为什么编译器认为leg
或leg.annotaion
为空?
原因可能是.Where()
呼叫的签名,然后如何解决呢?
完整的测试用例复制如下:
namespace OsrmMapMatch
{
public class OsrmResponse
{
public string? code { get; set; }
public Matching[]? matchings { get; set; }
}
public class Matching
{
public Leg[]? legs { get; set; }
}
public class Leg
{
public Annotation? annotation { get; set; }
}
public class Annotation
{
public long[]? nodes { get; set; }
}
internal class Program
{
const string OsrmUri = "?overview=simplified&generate_hints=false&skip_waypoints=true&gaps=ignore&annotations=nodes&geometries=geojson&radiuses=";
readonly static (double lng, double lat)[] Locations =
{
(10.757938, 52.437444),
(10.764379, 52.437314),
(10.770562, 52.439067),
(10.773268, 52.436633),
};
static async Task Main(string[] args)
{
const string HttpClientMapMatch = "HttpClientMapMatch";
ServiceProvider serviceProvider = new ServiceCollection()
.AddHttpClient(HttpClientMapMatch, httpClient =>
{
httpClient.BaseAddress = new Uri("https://router.project-osrm.org/match/v1/driving/");
}).Services.BuildServiceProvider();
IHttpClientFactory? httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
HttpClient? httpClient = httpClientFactory?.CreateClient(HttpClientMapMatch);
if (httpClient == null)
{
Console.WriteLine("Error httpClient is null");
return;
}
IEnumerable<long> nodes = await GetOsmNodesAsync(httpClient, Locations);
Console.WriteLine($"Map matched OSM node ids: {JsonSerializer.Serialize(nodes.OrderBy(node => node))}\n");
}
private static async Task<IEnumerable<long>> GetOsmNodesAsync(HttpClient httpClient, IEnumerable<(double lng, double lat)> locations)
{
IEnumerable<string> lngLats = locations
.Select(location => $"{location.lng:F6},{location.lat:F6}")
.ToList();
IEnumerable<int> radiuses = locations
.Select(location => 50)
.ToList();
string requestUri = string.Join(";", lngLats) + OsrmUri + string.Join(";", radiuses);
OsrmResponse? osrmResponse = await httpClient.GetFromJsonAsync<OsrmResponse>(requestUri);
if (osrmResponse?.code == "Ok" && osrmResponse.matchings != null)
{
return osrmResponse.matchings
.Where(matching => matching != null)
.SelectMany(matching => matching.legs ?? Enumerable.Empty<Leg>())
.Where(leg => leg != null && leg.annotation != null && leg.annotation.nodes != null)
// How to fix dereference of a possibly null value in the next line?
.SelectMany(leg => leg.annotation.nodes ?? Enumerable.Empty<long>())
// eliminate duplicate node ids by converting to an ISet
.ToHashSet()
.ToList();
}
return Enumerable.Empty<long>();
}
}
}