From f7dc35fb365407fd7579316b4b220595281e2e5c Mon Sep 17 00:00:00 2001 From: Iliar Turdushev Date: Thu, 26 Sep 2024 13:54:27 +0200 Subject: [PATCH] Update ResilienceProperties to correctly handle null values (#2300) Adds logic handling a case when the value being requested is null. --- src/Polly.Core/ResilienceProperties.cs | 20 +++++++++++++--- .../ResiliencePropertiesTests.cs | 23 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/Polly.Core/ResilienceProperties.cs b/src/Polly.Core/ResilienceProperties.cs index 466901436a8..a5f43d54086 100644 --- a/src/Polly.Core/ResilienceProperties.cs +++ b/src/Polly.Core/ResilienceProperties.cs @@ -20,10 +20,24 @@ public sealed class ResilienceProperties /// True, if a property was retrieved. public bool TryGetValue(ResiliencePropertyKey key, [MaybeNullWhen(false)] out TValue value) { - if (Options.TryGetValue(key.Key, out object? val) && val is TValue typedValue) + if (Options.TryGetValue(key.Key, out object? val)) { - value = typedValue; - return true; + if (val is TValue typedValue) + { + value = typedValue; + return true; + } + else if (val is null) + { + // We have to use null-forgiving operator "!" here to suppress a null-state analysis warning. + // The reason is the following. The output type "TValue" doesn't have any type constraints as + // "notnull", "class" or "struct", therefore the analyzer considers "TValue" as non-nullable + // and warns us that we're assigning "null" to it. But that's not correct, because "TValue" + // could be a nullable type, e.g. "string?", and assigning "null" to it is correct. Therefore + // it is reasonable to use "!" here to suppress the warning. + value = default!; + return true; + } } value = default; diff --git a/test/Polly.Core.Tests/ResiliencePropertiesTests.cs b/test/Polly.Core.Tests/ResiliencePropertiesTests.cs index e4ef2c31ba0..45e97016b7f 100644 --- a/test/Polly.Core.Tests/ResiliencePropertiesTests.cs +++ b/test/Polly.Core.Tests/ResiliencePropertiesTests.cs @@ -14,6 +14,18 @@ public void TryGetValue_Ok() val.Should().Be(12345); } + [Fact] + public void TryGetValue_ValueIsNull_Ok() + { + var key = new ResiliencePropertyKey("dummy"); + var props = new ResilienceProperties(); + + props.Set(key, null); + + props.TryGetValue(key, out var val).Should().Be(true); + val.Should().Be(null); + } + [Fact] public void TryGetValue_NotFound_Ok() { @@ -34,6 +46,17 @@ public void GetValue_Ok() props.GetValue(key, default).Should().Be(12345); } + [Fact] + public void GetValue_ValueIsNull_Ok() + { + var key = new ResiliencePropertyKey("dummy"); + var props = new ResilienceProperties(); + + props.Set(key, null); + + props.GetValue(key, "default").Should().Be(null); + } + [Fact] public void GetValue_NotFound_EnsureDefault() {