Skip to content

Commit 0bffe8e

Browse files
authored
Merge pull request #1305 from microsoft/dev/andarno/asMethodImprovements
Allow `null` argument to `As<T>` and `Is(Type)` helper methods
2 parents 3873c15 + 1e27f4f commit 0bffe8e

File tree

7 files changed

+27
-20
lines changed

7 files changed

+27
-20
lines changed

src/StreamJsonRpc.Analyzers/GeneratorModels/FullModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ void WriteGroup(SourceWriter writer)
115115
string visibility = iface.IsPublic ? "public" : "internal";
116116
writer.WriteLine($$"""
117117
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
118-
{{visibility}} static bool Is(this {{iface.FullName}} self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
118+
{{visibility}} static bool Is(this {{iface.FullName}}? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
119119
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
120-
{{visibility}} static T? As<T>(this {{iface.FullName}} self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
120+
{{visibility}} static T? As<T>(this {{iface.FullName}}? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
121121
""");
122122
if (first)
123123
{

src/StreamJsonRpc/JsonRpcExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ private interface IRpcEnumerable
3535
/// In such cases, this method can be used to determine whether the proxy was intentionally created to implement the interface
3636
/// or not, allowing feature testing to still happen since conditional casting might lead to false positives.
3737
/// </remarks>
38-
public static T? As<T>(this IClientProxy proxy)
39-
where T : class => Requires.NotNull(proxy).Is(typeof(T)) ? (T)(object)proxy : null;
38+
public static T? As<T>(this IClientProxy? proxy)
39+
where T : class => proxy?.Is(typeof(T)) is true ? (T)(object)proxy : null;
4040

4141
#pragma warning disable VSTHRD200 // Use "Async" suffix in names of methods that return an awaitable type.
4242
/// <summary>

test/StreamJsonRpc.Analyzer.Tests/Resources/ExperimentalApis/OptionalInterfaceExtensions.g.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
public static partial class StreamJsonRpcOptionalInterfaceAccessors
1313
{
1414
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
15-
public static bool Is(this SomeExperimentalInterface self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
15+
public static bool Is(this SomeExperimentalInterface? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
1616
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
17-
public static T? As<T>(this SomeExperimentalInterface self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
17+
public static T? As<T>(this SomeExperimentalInterface? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
1818

1919
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
20-
public static bool Is(this SomeExperimentalInterface2 self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
20+
public static bool Is(this SomeExperimentalInterface2? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
2121
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
22-
public static T? As<T>(this SomeExperimentalInterface2 self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
22+
public static T? As<T>(this SomeExperimentalInterface2? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
2323
}

test/StreamJsonRpc.Analyzer.Tests/Resources/RpcMarshalable_OptionalInterfaces_WithExtensionMethods/OptionalInterfaceExtensions.g.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
public static partial class StreamJsonRpcOptionalInterfaceAccessors
1313
{
1414
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
15-
public static bool Is(this IMarshalable self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
15+
public static bool Is(this IMarshalable? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
1616
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
17-
public static T? As<T>(this IMarshalable self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
17+
public static T? As<T>(this IMarshalable? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
1818

1919
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
20-
internal static bool Is(this IOptional self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
20+
internal static bool Is(this IOptional? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
2121
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
22-
internal static T? As<T>(this IOptional self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
22+
internal static T? As<T>(this IOptional? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
2323
}

test/StreamJsonRpc.Analyzer.Tests/Resources/RpcMarshalable_OptionalInterfaces_WithExtensionMethods_NestedInClass/OptionalInterfaceExtensions.g.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ namespace NS
1313
internal static partial class StreamJsonRpcOptionalInterfaceAccessors
1414
{
1515
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
16-
internal static bool Is(this NS.Wrapper.IMarshalable self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
16+
internal static bool Is(this NS.Wrapper.IMarshalable? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
1717
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
18-
internal static T? As<T>(this NS.Wrapper.IMarshalable self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
18+
internal static T? As<T>(this NS.Wrapper.IMarshalable? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
1919

2020
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
21-
internal static bool Is(this NS.Wrapper.IOptional self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
21+
internal static bool Is(this NS.Wrapper.IOptional? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
2222
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
23-
internal static T? As<T>(this NS.Wrapper.IOptional self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
23+
internal static T? As<T>(this NS.Wrapper.IOptional? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
2424
}
2525
}

test/StreamJsonRpc.Analyzer.Tests/Resources/RpcMarshalable_OptionalInterfaces_WithExtensionMethods_NotPublic/OptionalInterfaceExtensions.g.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
internal static partial class StreamJsonRpcOptionalInterfaceAccessors
1212
{
1313
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
14-
public static bool Is(this IMarshalable self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
14+
public static bool Is(this IMarshalable? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
1515
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
16-
public static T? As<T>(this IMarshalable self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
16+
public static T? As<T>(this IMarshalable? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
1717

1818
/// <inheritdoc cref="global::StreamJsonRpc.IClientProxy.Is(global::System.Type)"/>
19-
internal static bool Is(this IOptional self, global::System.Type type) => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.Is(type) : type.IsAssignableFrom(self.GetType());
19+
internal static bool Is(this IOptional? self, global::System.Type type) => self switch { null => false, global::StreamJsonRpc.IClientProxy proxy => proxy.Is(type), _ => type.IsAssignableFrom(self.GetType()) };
2020
/// <inheritdoc cref="global::StreamJsonRpc.JsonRpcExtensions.As{T}(global::StreamJsonRpc.IClientProxy)"/>
21-
internal static T? As<T>(this IOptional self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
21+
internal static T? As<T>(this IOptional? self) where T : class => self is global::StreamJsonRpc.IClientProxy proxy ? proxy.As<T>() : self as T;
2222
}

test/StreamJsonRpc.Tests/JsonRpcExtensionsTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ public JsonRpcExtensionsTests(ITestOutputHelper logger)
1010
{
1111
}
1212

13+
[Fact]
14+
public void As_NullArg()
15+
{
16+
// Non-null tests are exercised elsewhere.
17+
Assert.Null(JsonRpcExtensions.As<IDisposable>(null));
18+
}
19+
1320
[Fact]
1421
public async Task AsAsyncEnumerable()
1522
{

0 commit comments

Comments
 (0)