From a4c79fb69263919bdc74fb2a3132e720c274bca8 Mon Sep 17 00:00:00 2001 From: Mayuki Sawatari Date: Wed, 25 Oct 2023 19:32:00 +0900 Subject: [PATCH 1/3] MagicOnion.Client targets C# 9 and enable nullable annotations --- Directory.Build.props | 6 +- src/Directory.Build.props | 6 ++ .../ClientStreamingResult.cs | 17 +++-- .../MagicOnion.Abstractions.csproj | 5 +- src/MagicOnion.Abstractions/UnaryResult.cs | 66 ++++++++++------- ...cOnion.Client.SourceGenerator.Unity.csproj | 2 - .../MagicOnion.Client.SourceGenerator.csproj | 2 - .../ClientStreamingResult.cs | 17 +++-- .../MagicOnion.Abstractions/UnaryResult.cs | 66 ++++++++++------- .../MagicOnion.Abstractions/csc.rsp | 1 + .../MagicOnion.Abstractions/csc.rsp.meta | 7 ++ .../DynamicClient/DynamicClientBuilder.cs | 26 +++---- .../DynamicStreamingHubClientBuilder.cs | 72 ++++++++++--------- .../DynamicClient/RawMethodInvokerTypes.cs | 10 +-- .../DynamicClient/ServiceClientDefinition.cs | 18 ++--- .../Internal/MagicOnionMethodInvoker.cs | 8 +-- .../MagicOnion.Shared/AsyncLock.cs | 6 +- .../MagicOnion.Shared/BroadcasterHelper.cs | 21 +++--- .../MagicOnion.Shared/GrpcMethodHelper.cs | 16 +++-- .../Internal/MagicOnionClientStreamWriter.cs | 2 +- .../Internal/MagicOnionServerStreamWriter.cs | 2 +- .../MagicOnionMarshallers.cs | 4 +- .../MagicOnion.Shared/MetadataExtensions.cs | 6 +- .../Serialization/MagicOnionSerializer.cs | 9 --- ...MessagePackMagicOnionSerializerProvider.cs | 8 +-- .../UnsafeDirectBlitResolver.cs | 14 ++-- .../Utils/ArrayPoolBufferWriter.cs | 14 ++-- .../Utils/DynamicAssembly.cs | 4 +- .../Utils/ILGeneratorExtensions.cs | 6 +- .../Utils/ReservedWhenAllPromise.cs | 30 ++++---- .../Utils/UniqueHashDictionary.cs | 15 ++-- .../MagicOnion.Client/MagicOnionClientBase.cs | 8 +-- .../MagicOnionClientFactoryProvider.cs | 11 +-- .../MagicOnion.Client/RequestContext.cs | 4 +- .../MagicOnion.Client/ResponseContext.cs | 35 ++++----- .../MagicOnion.Client/StreamingHubClient.cs | 15 ++-- .../StreamingHubClientBase.cs | 44 ++++++------ .../StreamingHubClientFactoryProvider.cs | 13 ++-- .../MagicOnion/MagicOnion.Client/csc.rsp | 1 + .../MagicOnion/MagicOnion.Client/csc.rsp.meta | 7 ++ .../MagicOnion.Unity/GrpcChannelProvider.cs | 8 +-- .../GrpcChannelProviderHost.cs | 8 +-- .../MagicOnion.Unity/GrpcChannelx.cs | 4 +- .../MagicOnion.Unity/IGrpcChannelProvider.cs | 13 ++-- .../MagicOnion.Unity/UnityDebugLogger.cs | 8 +-- .../MagicOnion/MagicOnion.Unity/csc.rsp | 1 + .../MagicOnion/MagicOnion.Unity/csc.rsp.meta | 7 ++ .../DynamicClient/DynamicClientBuilder.cs | 26 +++---- .../DynamicStreamingHubClientBuilder.cs | 72 ++++++++++--------- .../DynamicClient/RawMethodInvokerTypes.cs | 10 +-- .../DynamicClient/ServiceClientDefinition.cs | 18 ++--- .../Internal/MagicOnionMethodInvoker.cs | 8 +-- .../MagicOnion.Client.csproj | 13 +++- src/MagicOnion.Client/MagicOnionClientBase.cs | 8 +-- .../MagicOnionClientFactoryProvider.cs | 11 +-- src/MagicOnion.Client/RequestContext.cs | 4 +- src/MagicOnion.Client/ResponseContext.cs | 35 ++++----- src/MagicOnion.Client/StreamingHubClient.cs | 15 ++-- .../StreamingHubClientBase.cs | 44 ++++++------ .../StreamingHubClientFactoryProvider.cs | 13 ++-- ...MagicOnion.Serialization.MemoryPack.csproj | 4 +- .../MemoryPackMagicOnionSerializer.cs | 6 +- .../MagicOnion.Server.HttpGateway.csproj | 4 +- .../MagicOnion.Server.Redis.csproj | 5 +- .../NativeGuidArrayFormatter.cs | 8 +-- src/MagicOnion.Server.Redis/RedisGroup.cs | 17 ++--- .../RedisGroupOptions.cs | 2 +- src/MagicOnion.Server/Hubs/Group.cs | 2 +- .../MagicOnion.Server.csproj | 4 +- src/MagicOnion.Shared/AsyncLock.cs | 6 +- src/MagicOnion.Shared/BroadcasterHelper.cs | 21 +++--- src/MagicOnion.Shared/GrpcMethodHelper.cs | 16 +++-- .../Internal/MagicOnionClientStreamWriter.cs | 2 +- .../Internal/MagicOnionServerStreamWriter.cs | 2 +- .../MagicOnion.Shared.csproj | 14 +++- .../MagicOnionMarshallers.cs | 4 +- src/MagicOnion.Shared/MetadataExtensions.cs | 6 +- .../Serialization/MagicOnionSerializer.cs | 9 --- ...MessagePackMagicOnionSerializerProvider.cs | 8 +-- .../UnsafeDirectBlitResolver.cs | 14 ++-- .../Utils/ArrayPoolBufferWriter.cs | 14 ++-- .../Utils/DynamicAssembly.cs | 4 +- .../Utils/ILGeneratorExtensions.cs | 6 +- .../Utils/ReservedWhenAllPromise.cs | 30 ++++---- .../Utils/UniqueHashDictionary.cs | 15 ++-- .../ClientFilterTest.cs | 6 +- .../DynamicClient/SameInterfaceNameTest.cs | 2 + .../ServiceClientDefinitionTest.cs | 20 +++--- .../XorMessagePackMagicOnionSerializer.cs | 2 +- tests/MagicOnion.Shared.Tests/BoxTest.cs | 9 ++- .../UnaryResultNonGenericTest.cs | 4 ++ .../UnaryResultTest.cs | 6 +- 92 files changed, 668 insertions(+), 554 deletions(-) create mode 100644 src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp create mode 100644 src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp.meta create mode 100644 src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp create mode 100644 src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp.meta create mode 100644 src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp create mode 100644 src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp.meta diff --git a/Directory.Build.props b/Directory.Build.props index 03e293c72..5651d52fc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,12 @@ + + 5.1.8 + + true $(NoWarn);CS1591 - 5.1.8 + $(WarningsAsErrors);Nullable diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 43583c38d..1c254dc90 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,12 @@ + + + latest + <_LangVersionUnityBaseline>9.0 + + true $(MSBuildProjectDirectory)\..\MagicOnion\opensource.snk diff --git a/src/MagicOnion.Abstractions/ClientStreamingResult.cs b/src/MagicOnion.Abstractions/ClientStreamingResult.cs index 1e04707b0..c46561a72 100644 --- a/src/MagicOnion.Abstractions/ClientStreamingResult.cs +++ b/src/MagicOnion.Abstractions/ClientStreamingResult.cs @@ -15,9 +15,9 @@ namespace MagicOnion /// public struct ClientStreamingResult : IDisposable { - internal readonly TResponse rawValue; + internal readonly TResponse? rawValue; internal readonly bool hasRawValue; - readonly IAsyncClientStreamingCallWrapper inner; + readonly IAsyncClientStreamingCallWrapper? inner; public ClientStreamingResult(TResponse rawValue) { @@ -33,23 +33,26 @@ public ClientStreamingResult(IAsyncClientStreamingCallWrapper GetRequiredInner() + => inner ?? throw new NotSupportedException("ClientStreamingResult has no inner stream."); + /// /// Asynchronous call result. /// public Task ResponseAsync - => hasRawValue ? Task.FromResult(rawValue) : inner.ResponseAsync; + => hasRawValue ? Task.FromResult(rawValue!) : GetRequiredInner().ResponseAsync; /// /// Asynchronous access to response headers. /// public Task ResponseHeadersAsync - => inner.ResponseHeadersAsync; + => GetRequiredInner().ResponseHeadersAsync; /// /// Async stream to send streaming requests. /// public IClientStreamWriter RequestStream - => inner?.RequestStream; + => GetRequiredInner().RequestStream; /// /// Allows awaiting this object directly. @@ -65,14 +68,14 @@ public TaskAwaiter GetAwaiter() /// Throws InvalidOperationException otherwise. /// public Status GetStatus() - => inner.GetStatus(); + => GetRequiredInner().GetStatus(); /// /// Gets the call trailing metadata if the call has already finished. /// Throws InvalidOperationException otherwise. /// public Metadata GetTrailers() - => inner.GetTrailers(); + => GetRequiredInner().GetTrailers(); /// /// Provides means to cleanup after the call. diff --git a/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj b/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj index b3382fd89..b6c78be70 100644 --- a/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj +++ b/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj @@ -2,7 +2,10 @@ netstandard2.0 - 8.0 + + $(_LangVersionUnityBaseline) + enable + $(DefineConstants);NON_UNITY MagicOnion.Abstractions diff --git a/src/MagicOnion.Abstractions/UnaryResult.cs b/src/MagicOnion.Abstractions/UnaryResult.cs index 1d45b5064..b73727cd4 100644 --- a/src/MagicOnion.Abstractions/UnaryResult.cs +++ b/src/MagicOnion.Abstractions/UnaryResult.cs @@ -15,8 +15,8 @@ namespace MagicOnion public readonly struct UnaryResult { internal readonly bool hasRawValue; - internal readonly Task rawTaskValue; - internal readonly Task> response; + internal readonly Task? rawTaskValue; + internal readonly Task>? response; public UnaryResult(Nil nil) { @@ -74,20 +74,24 @@ public Task ResponseAsync /// public Task ResponseHeadersAsync => UnwrapResponseHeaders(); + Task> GetRequiredResponse() + => response ?? throw new InvalidOperationException("UnaryResult has no response."); + async Task UnwrapResponse() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); await ctx.ResponseAsync.ConfigureAwait(false); } async Task UnwrapResponseHeaders() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); return await ctx.ResponseHeadersAsync.ConfigureAwait(false); } IResponseContext TryUnwrap() { + var response = GetRequiredResponse(); if (!response.IsCompleted) { throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this."); @@ -128,13 +132,16 @@ public Metadata GetTrailers() /// public void Dispose() { - if (!response.IsCompleted) + if (response is not null) { - UnwrapDispose(); - } - else - { - response.Result.Dispose(); + if (!response.IsCompleted) + { + UnwrapDispose(); + } + else + { + response.Result.Dispose(); + } } } @@ -142,7 +149,7 @@ async void UnwrapDispose() { try { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); ctx.Dispose(); } catch @@ -182,10 +189,10 @@ public static UnaryResult Nil public readonly struct UnaryResult { internal readonly bool hasRawValue; // internal - internal readonly TResponse rawValue; // internal - internal readonly Task rawTaskValue; // internal + internal readonly TResponse? rawValue; // internal + internal readonly Task? rawTaskValue; // internal - readonly Task> response; + readonly Task>? response; public UnaryResult(TResponse rawValue) { @@ -224,18 +231,18 @@ public Task ResponseAsync // So, we will return the default value of TResponse as Task. if (response is null) { - return Task.FromResult(default(TResponse)); + return Task.FromResult(default(TResponse)!); } return UnwrapResponse(); } - else if (rawTaskValue != null) + else if (rawTaskValue is not null) { return rawTaskValue; } else { - return Task.FromResult(rawValue); + return Task.FromResult(rawValue!); } } } @@ -245,15 +252,18 @@ public Task ResponseAsync /// public Task ResponseHeadersAsync => UnwrapResponseHeaders(); + Task> GetRequiredResponse() + => response ?? throw new InvalidOperationException("UnaryResult has no response."); + async Task UnwrapResponse() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); return await ctx.ResponseAsync.ConfigureAwait(false); } async Task UnwrapResponseHeaders() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); return await ctx.ResponseHeadersAsync.ConfigureAwait(false); } @@ -261,7 +271,7 @@ async void UnwrapDispose() { try { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); ctx.Dispose(); } catch @@ -271,6 +281,7 @@ async void UnwrapDispose() IResponseContext TryUnwrap() { + var response = GetRequiredResponse(); if (!response.IsCompleted) { throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this."); @@ -311,13 +322,16 @@ public Metadata GetTrailers() /// public void Dispose() { - if (!response.IsCompleted) + if (response is not null) { - UnwrapDispose(); - } - else - { - response.Result.Dispose(); + if (!response.IsCompleted) + { + UnwrapDispose(); + } + else + { + response.Result.Dispose(); + } } } } diff --git a/src/MagicOnion.Client.SourceGenerator.Unity/MagicOnion.Client.SourceGenerator.Unity.csproj b/src/MagicOnion.Client.SourceGenerator.Unity/MagicOnion.Client.SourceGenerator.Unity.csproj index 35e364d08..d1c20da87 100644 --- a/src/MagicOnion.Client.SourceGenerator.Unity/MagicOnion.Client.SourceGenerator.Unity.csproj +++ b/src/MagicOnion.Client.SourceGenerator.Unity/MagicOnion.Client.SourceGenerator.Unity.csproj @@ -3,10 +3,8 @@ netstandard2.0 - latest enable enable - Nullable true diff --git a/src/MagicOnion.Client.SourceGenerator/MagicOnion.Client.SourceGenerator.csproj b/src/MagicOnion.Client.SourceGenerator/MagicOnion.Client.SourceGenerator.csproj index 52196c556..eb8e9461b 100644 --- a/src/MagicOnion.Client.SourceGenerator/MagicOnion.Client.SourceGenerator.csproj +++ b/src/MagicOnion.Client.SourceGenerator/MagicOnion.Client.SourceGenerator.csproj @@ -3,10 +3,8 @@ netstandard2.0 - latest enable enable - Nullable true diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/ClientStreamingResult.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/ClientStreamingResult.cs index 1e04707b0..c46561a72 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/ClientStreamingResult.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/ClientStreamingResult.cs @@ -15,9 +15,9 @@ namespace MagicOnion /// public struct ClientStreamingResult : IDisposable { - internal readonly TResponse rawValue; + internal readonly TResponse? rawValue; internal readonly bool hasRawValue; - readonly IAsyncClientStreamingCallWrapper inner; + readonly IAsyncClientStreamingCallWrapper? inner; public ClientStreamingResult(TResponse rawValue) { @@ -33,23 +33,26 @@ public ClientStreamingResult(IAsyncClientStreamingCallWrapper GetRequiredInner() + => inner ?? throw new NotSupportedException("ClientStreamingResult has no inner stream."); + /// /// Asynchronous call result. /// public Task ResponseAsync - => hasRawValue ? Task.FromResult(rawValue) : inner.ResponseAsync; + => hasRawValue ? Task.FromResult(rawValue!) : GetRequiredInner().ResponseAsync; /// /// Asynchronous access to response headers. /// public Task ResponseHeadersAsync - => inner.ResponseHeadersAsync; + => GetRequiredInner().ResponseHeadersAsync; /// /// Async stream to send streaming requests. /// public IClientStreamWriter RequestStream - => inner?.RequestStream; + => GetRequiredInner().RequestStream; /// /// Allows awaiting this object directly. @@ -65,14 +68,14 @@ public TaskAwaiter GetAwaiter() /// Throws InvalidOperationException otherwise. /// public Status GetStatus() - => inner.GetStatus(); + => GetRequiredInner().GetStatus(); /// /// Gets the call trailing metadata if the call has already finished. /// Throws InvalidOperationException otherwise. /// public Metadata GetTrailers() - => inner.GetTrailers(); + => GetRequiredInner().GetTrailers(); /// /// Provides means to cleanup after the call. diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/UnaryResult.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/UnaryResult.cs index 1d45b5064..b73727cd4 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/UnaryResult.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/UnaryResult.cs @@ -15,8 +15,8 @@ namespace MagicOnion public readonly struct UnaryResult { internal readonly bool hasRawValue; - internal readonly Task rawTaskValue; - internal readonly Task> response; + internal readonly Task? rawTaskValue; + internal readonly Task>? response; public UnaryResult(Nil nil) { @@ -74,20 +74,24 @@ public Task ResponseAsync /// public Task ResponseHeadersAsync => UnwrapResponseHeaders(); + Task> GetRequiredResponse() + => response ?? throw new InvalidOperationException("UnaryResult has no response."); + async Task UnwrapResponse() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); await ctx.ResponseAsync.ConfigureAwait(false); } async Task UnwrapResponseHeaders() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); return await ctx.ResponseHeadersAsync.ConfigureAwait(false); } IResponseContext TryUnwrap() { + var response = GetRequiredResponse(); if (!response.IsCompleted) { throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this."); @@ -128,13 +132,16 @@ public Metadata GetTrailers() /// public void Dispose() { - if (!response.IsCompleted) + if (response is not null) { - UnwrapDispose(); - } - else - { - response.Result.Dispose(); + if (!response.IsCompleted) + { + UnwrapDispose(); + } + else + { + response.Result.Dispose(); + } } } @@ -142,7 +149,7 @@ async void UnwrapDispose() { try { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); ctx.Dispose(); } catch @@ -182,10 +189,10 @@ public static UnaryResult Nil public readonly struct UnaryResult { internal readonly bool hasRawValue; // internal - internal readonly TResponse rawValue; // internal - internal readonly Task rawTaskValue; // internal + internal readonly TResponse? rawValue; // internal + internal readonly Task? rawTaskValue; // internal - readonly Task> response; + readonly Task>? response; public UnaryResult(TResponse rawValue) { @@ -224,18 +231,18 @@ public Task ResponseAsync // So, we will return the default value of TResponse as Task. if (response is null) { - return Task.FromResult(default(TResponse)); + return Task.FromResult(default(TResponse)!); } return UnwrapResponse(); } - else if (rawTaskValue != null) + else if (rawTaskValue is not null) { return rawTaskValue; } else { - return Task.FromResult(rawValue); + return Task.FromResult(rawValue!); } } } @@ -245,15 +252,18 @@ public Task ResponseAsync /// public Task ResponseHeadersAsync => UnwrapResponseHeaders(); + Task> GetRequiredResponse() + => response ?? throw new InvalidOperationException("UnaryResult has no response."); + async Task UnwrapResponse() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); return await ctx.ResponseAsync.ConfigureAwait(false); } async Task UnwrapResponseHeaders() { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); return await ctx.ResponseHeadersAsync.ConfigureAwait(false); } @@ -261,7 +271,7 @@ async void UnwrapDispose() { try { - var ctx = await response.ConfigureAwait(false); + var ctx = await GetRequiredResponse().ConfigureAwait(false); ctx.Dispose(); } catch @@ -271,6 +281,7 @@ async void UnwrapDispose() IResponseContext TryUnwrap() { + var response = GetRequiredResponse(); if (!response.IsCompleted) { throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this."); @@ -311,13 +322,16 @@ public Metadata GetTrailers() /// public void Dispose() { - if (!response.IsCompleted) + if (response is not null) { - UnwrapDispose(); - } - else - { - response.Result.Dispose(); + if (!response.IsCompleted) + { + UnwrapDispose(); + } + else + { + response.Result.Dispose(); + } } } } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp new file mode 100644 index 000000000..2dabb0c53 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp @@ -0,0 +1 @@ +-nullable \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp.meta new file mode 100644 index 000000000..d5720fcca --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/csc.rsp.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c0364c108bb40924c82ef313114ac561 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs index b594abead..2130aad07 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs @@ -33,7 +33,7 @@ static Type Build() EmitServiceClientClass(buildContext); - return buildContext.ServiceClientType.CreateTypeInfo(); + return buildContext.ServiceClientType.CreateTypeInfo()!; } class ServiceClientBuildContext @@ -45,13 +45,13 @@ public ServiceClientBuildContext(ServiceClientDefinition definition) public ServiceClientDefinition Definition { get; } - public TypeBuilder ClientCoreType { get; set; } // {ServiceName}Client+ClientCore - public ConstructorBuilder ClientCoreConstructor { get; set; } // {ServiceName}Client+ClientCore..ctor + public TypeBuilder ClientCoreType { get; set; } = default!; // {ServiceName}Client+ClientCore + public ConstructorBuilder ClientCoreConstructor { get; set; } = default!; // {ServiceName}Client+ClientCore..ctor - public TypeBuilder ServiceClientType { get; set; } // {ServiceName}Client - public ConstructorBuilder ServiceClientConstructor { get; set; } // {ServiceName}Client..ctor - public ConstructorBuilder ServiceClientConstructorForClone { get; set; } // {ServiceName}Client..ctor - public FieldBuilder FieldCore { get; set; } + public TypeBuilder ServiceClientType { get; set; } = default!; // {ServiceName}Client + public ConstructorBuilder ServiceClientConstructor { get; set; } = default!; // {ServiceName}Client..ctor + public ConstructorBuilder ServiceClientConstructorForClone { get; set; } = default!; // {ServiceName}Client..ctor + public FieldBuilder FieldCore { get; set; } = default!; public Dictionary FieldAndMethodInvokerTypeByMethod { get; } = new Dictionary(); } @@ -65,7 +65,7 @@ static void EmitServiceClientClass(ServiceClientBuildContext ctx) // ctx.ServiceClientType = DynamicClientAssemblyHolder.Assembly.DefineType($"MagicOnion.DynamicallyGeneratedClient.{ctx.Definition.ServiceInterfaceType.Namespace}.{ctx.Definition.ServiceInterfaceType.Name}Client", TypeAttributes.Public | TypeAttributes.Sealed, constructedBaseClientType, new[] { ctx.Definition.ServiceInterfaceType }); // Set `IgnoreAttribute` to the generated client type. Hides generated-types from building MagicOnion service definitions. - ctx.ServiceClientType.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoreAttribute).GetConstructor(Type.EmptyTypes), Array.Empty())); + ctx.ServiceClientType.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoreAttribute).GetConstructor(Type.EmptyTypes)!, Array.Empty())); { // class ClientCore { ... } EmitClientCore(ctx); @@ -98,13 +98,13 @@ static void EmitClone(ServiceClientBuildContext ctx, Type constructedBaseClientT static void EmitConstructor(ServiceClientBuildContext ctx) { - var baseCtor = ctx.ServiceClientType.BaseType.GetConstructor( + var baseCtor = ctx.ServiceClientType.BaseType!.GetConstructor( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Standard, new[] { typeof(MagicOnionClientOptions) }, Array.Empty() - ); + )!; // public {ServiceName}Client(MagicOnionClientOptions options, IMagicOnionSerializerProvider serializerProvider) { ctx.ServiceClientConstructor = ctx.ServiceClientType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, KnownTypes.ClientConstructorParameters); { @@ -165,7 +165,7 @@ static void EmitServiceMethods(ServiceClientBuildContext ctx) foreach (var method in ctx.Definition.Methods) { var hasNonGenericUnaryResult = method.MethodReturnType == typeof(UnaryResult); - var methodInvokerInvokeMethod = ctx.FieldAndMethodInvokerTypeByMethod[method.MethodName].MethodInvokerType.GetMethod($"Invoke{method.MethodType}{(hasNonGenericUnaryResult ? "NonGeneric" : "")}"); + var methodInvokerInvokeMethod = ctx.FieldAndMethodInvokerTypeByMethod[method.MethodName].MethodInvokerType.GetMethod($"Invoke{method.MethodType}{(hasNonGenericUnaryResult ? "NonGeneric" : "")}")!; var methodBuilder = ctx.ServiceClientType.DefineMethod(method.MethodName, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, methodInvokerInvokeMethod.ReturnType, method.ParameterTypes.ToArray()); var il = methodBuilder.GetILGenerator(); @@ -208,13 +208,13 @@ static void EmitServiceMethods(ServiceClientBuildContext ctx) break; } } - il.Emit(OpCodes.Newobj, method.RequestType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, method.ParameterTypes.ToArray(), Array.Empty())); + il.Emit(OpCodes.Newobj, method.RequestType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, method.ParameterTypes.ToArray(), Array.Empty())!); } } else if (method.ParameterTypes.Count == 0) { // Nil.Default - il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default", BindingFlags.Public | BindingFlags.Static)); + il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default", BindingFlags.Public | BindingFlags.Static)!); } } else diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs index 4d36c23e3..13786bf20 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs @@ -53,9 +53,9 @@ static class DynamicStreamingHubClientBuilder // static readonly Type ClientFireAndForgetType; static readonly Type bytesMethod = typeof(Method<,>).MakeGenericType(new[] { typeof(byte[]), typeof(byte[]) }); - static readonly FieldInfo throughMarshaller = typeof(MagicOnionMarshallers).GetField("ThroughMarshaller", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + static readonly FieldInfo throughMarshaller = typeof(MagicOnionMarshallers).GetField("ThroughMarshaller", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)!; - static readonly ConstructorInfo notSupportedException = typeof(NotSupportedException).GetConstructor(Type.EmptyTypes); + static readonly ConstructorInfo notSupportedException = typeof(NotSupportedException).GetConstructor(Type.EmptyTypes)!; static DynamicStreamingHubClientBuilder() { @@ -85,7 +85,7 @@ static DynamicStreamingHubClientBuilder() DefineMethods(typeBuilder, t, typeof(TReceiver), methodField, clientField, methodDefinitions); } - ClientType = typeBuilder.CreateTypeInfo().AsType(); + ClientType = typeBuilder.CreateTypeInfo()!.AsType(); } static MethodDefinition[] SearchDefinitions(Type interfaceType) @@ -115,11 +115,7 @@ static MethodDefinition[] SearchDefinitions(Type interfaceType) return true; }) .Where(x => !x.IsSpecialName) - .Select(x => new MethodDefinition - { - ServiceType = interfaceType, - MethodInfo = x, - }) + .Select(x => new MethodDefinition(interfaceType, x, default, default)) .ToArray(); } @@ -188,11 +184,11 @@ static FieldInfo DefineConstructor(TypeBuilder typeBuilder, Type interfaceType, il.Emit(OpCodes.Ldarg_3); il.Emit(OpCodes.Ldarg_S, (byte)4); il.Emit(OpCodes.Ldarg_S, (byte)5); - il.Emit(OpCodes.Call, typeBuilder.BaseType + il.Emit(OpCodes.Call, typeBuilder.BaseType! .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).First()); // { this.fireAndForgetClient = new FireAndForgetClient(this); } - var clientField = typeBuilder.DefineField("fireAndForgetClient", fireAndForgetClientCtor.DeclaringType, FieldAttributes.Private); + var clientField = typeBuilder.DefineField("fireAndForgetClient", fireAndForgetClientCtor.DeclaringType!, FieldAttributes.Private); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Newobj, fireAndForgetClientCtor); @@ -229,8 +225,8 @@ static Tuple DefineFireAndForgetConstructor(Ty static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type receiverType, FieldInfo methodField, FieldInfo clientField, MethodDefinition[] definitions) { var baseType = typeof(StreamingHubClientBase<,>).MakeGenericType(interfaceType, receiverType); - var serializerOptionsField = baseType.GetField("serializerOptions", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var receiverField = baseType.GetField("receiver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var serializerOptionsField = baseType.GetField("serializerOptions", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; + var receiverField = baseType.GetField("receiver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; // protected abstract Method DuplexStreamingAsyncMethod { get; } { @@ -251,7 +247,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece var il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, baseType.GetMethod("DisposeAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)); + il.Emit(OpCodes.Call, baseType.GetMethod("DisposeAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!); il.Emit(OpCodes.Ret); } // Task WaitForDisconnect(); @@ -261,7 +257,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece var il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, baseType.GetMethod("WaitForDisconnect", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)); + il.Emit(OpCodes.Call, baseType.GetMethod("WaitForDisconnect", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!); il.Emit(OpCodes.Ret); } // TSelf FireAndForget(); @@ -318,7 +314,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldarg_2); // taskCompletionSource il.Emit(OpCodes.Ldarg_3); // data - il.Emit(OpCodes.Call, baseType.GetMethod("SetResultForResponse", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(responseType)); + il.Emit(OpCodes.Call, baseType.GetMethod("SetResultForResponse", BindingFlags.Instance | BindingFlags.NonPublic)!.MakeGenericMethod(responseType)); il.Emit(OpCodes.Ret); } @@ -352,7 +348,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece // var value = Deserialize>(data); // receiver.OnMessage(value.Item1, value.Item2); - var deserializeMethod = baseType.GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.NonPublic); + var deserializeMethod = baseType.GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.NonPublic)!; var parameters = item.def.MethodInfo.GetParameters(); if (parameters.Length == 0) { @@ -394,7 +390,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece for (int i = 0; i < parameters.Length; i++) { il.Emit(OpCodes.Ldloc, lc); - il.Emit(OpCodes.Ldfld, deserializeType.GetField("Item" + (i + 1))); + il.Emit(OpCodes.Ldfld, deserializeType.GetField("Item" + (i + 1))!); } il.Emit(OpCodes.Callvirt, item.def.MethodInfo); } @@ -430,12 +426,12 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece il.Emit(OpCodes.Ldarg, j + 1); } - Type callType = null; + Type callType; if (parameters.Length == 0) { // use Nil. callType = typeof(Nil); - il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")); + il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")!); } else if (parameters.Length == 1) { @@ -445,30 +441,30 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece else { // call new DynamicArgumentTuple - callType = def.RequestType; + callType = def.RequestType!; il.Emit(OpCodes.Newobj, callType.GetConstructors().First()); } if (def.MethodInfo.ReturnType == typeof(Task) || def.MethodInfo.ReturnType == typeof(ValueTask)) { - var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; il.Emit(OpCodes.Callvirt, mInfo.MakeGenericMethod(callType, typeof(Nil))); } else { - var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; il.Emit(OpCodes.Callvirt, mInfo.MakeGenericMethod(callType, def.MethodInfo.ReturnType.GetGenericArguments()[0])); } // If the return type is `ValueTask`, the task must be wrapped as ValueTask. if (def.MethodInfo.ReturnType == typeof(ValueTask)) { - il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })!); } else if (def.MethodInfo.IsGenericMethod && def.MethodInfo.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) { var returnTypeOfT = def.MethodInfo.ReturnType.GetGenericArguments()[0]; - il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })!); } il.Emit(OpCodes.Ret); @@ -533,12 +529,12 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy il.Emit(OpCodes.Ldarg, j + 1); } - Type requestType = null; + Type requestType; if (parameters.Length == 0) { // use Nil. requestType = typeof(Nil); - il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")); + il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")!); } else if (parameters.Length == 1) { @@ -548,7 +544,7 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy else { // call new DynamicArgumentTuple - requestType = def.RequestType; + requestType = def.RequestType!; il.Emit(OpCodes.Newobj, requestType.GetConstructors().First()); } @@ -561,7 +557,7 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy { responseType = def.MethodInfo.ReturnType.GetGenericArguments()[0]; } - var mInfo = parentNestedType.BaseType + var mInfo = parentNestedType.BaseType! .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Single(x => x.Name == "WriteMessageFireAndForgetAsync"); // WriteMessageAsyncFireAndForget il.Emit(OpCodes.Callvirt, mInfo.MakeGenericMethod(requestType, responseType)); @@ -569,12 +565,12 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy // If the return type is `ValueTask`, the task must be wrapped as ValueTask. if (def.MethodInfo.ReturnType == typeof(ValueTask)) { - il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })!); } else if (def.MethodInfo.IsGenericMethod && def.MethodInfo.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) { var returnTypeOfT = def.MethodInfo.ReturnType.GetGenericArguments()[0]; - il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })!); } il.Emit(OpCodes.Ret); @@ -586,11 +582,19 @@ class MethodDefinition { public string Path => ServiceType.Name + "/" + MethodInfo.Name; - public Type ServiceType; - public MethodInfo MethodInfo; - public int MethodId; + public Type ServiceType { get; set; } + public MethodInfo MethodInfo { get; set; } + public int MethodId { get; set; } + + public Type? RequestType { get; set; } - public Type RequestType; + public MethodDefinition(Type serviceType, MethodInfo methodInfo, int methodId, Type? requestType) + { + ServiceType = serviceType; + MethodInfo = methodInfo; + MethodId = methodId; + RequestType = requestType; + } } } } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs index 8116fbb20..74499020b 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs @@ -7,10 +7,10 @@ namespace MagicOnion.Client.DynamicClient { internal static class RawMethodInvokerTypes { - static readonly MethodInfo create_RefType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_RefType), BindingFlags.Static | BindingFlags.Public); - static readonly MethodInfo create_RefType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_ValueType), BindingFlags.Static | BindingFlags.Public); - static readonly MethodInfo create_ValueType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_RefType), BindingFlags.Static | BindingFlags.Public); - static readonly MethodInfo create_ValueType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_ValueType), BindingFlags.Static | BindingFlags.Public); + static readonly MethodInfo create_RefType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_RefType), BindingFlags.Static | BindingFlags.Public)!; + static readonly MethodInfo create_RefType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_ValueType), BindingFlags.Static | BindingFlags.Public)!; + static readonly MethodInfo create_ValueType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_RefType), BindingFlags.Static | BindingFlags.Public)!; + static readonly MethodInfo create_ValueType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_ValueType), BindingFlags.Static | BindingFlags.Public)!; public static MethodInfo GetMethodRawInvokerCreateMethod(Type requestType, Type responseType) { @@ -30,4 +30,4 @@ public static MethodInfo GetMethodRawInvokerCreateMethod(Type requestType, Type return create_RefType_RefType.MakeGenericMethod(requestType, responseType); } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs index ee925532c..94b8c361d 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs @@ -91,7 +91,7 @@ private void Verify() } } - private static (MethodType MethodType, Type RequestType, Type ResponseType) GetMethodTypeAndResponseTypeFromMethod(MethodInfo methodInfo) + private static (MethodType MethodType, Type? RequestType, Type ResponseType) GetMethodTypeAndResponseTypeFromMethod(MethodInfo methodInfo) { const string UnsupportedReturnTypeErrorMessage = "The method of a service must return 'UnaryResult', 'Task>', 'Task>' or 'DuplexStreamingResult'."; @@ -103,7 +103,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM } if (!returnType.IsGenericType) { - throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } var isTaskOfT = false; @@ -114,7 +114,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM returnType = returnType.GetGenericArguments()[0]; if (!returnType.IsGenericType) { - throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } returnTypeOpen = returnType.GetGenericTypeDefinition(); } @@ -123,7 +123,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (isTaskOfT) { - throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.Unary, null, typeof(Nil)); } @@ -131,7 +131,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (isTaskOfT) { - throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.Unary, null, returnType.GetGenericArguments()[0]); } @@ -139,7 +139,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (!isTaskOfT) { - throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.ClientStreaming, returnType.GetGenericArguments()[0], returnType.GetGenericArguments()[1]); } @@ -147,7 +147,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (!isTaskOfT) { - throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.ServerStreaming, null, returnType.GetGenericArguments()[0]); // Use method parameters as response type } @@ -155,13 +155,13 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (!isTaskOfT) { - throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.DuplexStreaming, returnType.GetGenericArguments()[0], returnType.GetGenericArguments()[1]); } else { - throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } } } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs index b00ac82ed..fafaf1d52 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs @@ -109,7 +109,7 @@ class AsyncServerStreamingCallWrapper : IAsyncServerStreamingCallWrapper inner; readonly Func fromRawResponse; - IAsyncStreamReader responseStream; + IAsyncStreamReader? responseStream; public AsyncServerStreamingCallWrapper(AsyncServerStreamingCall inner, Func fromRawResponse) { @@ -136,7 +136,7 @@ class AsyncClientStreamingCallWrapper : IAsyncClientStreamingCallWrapper inner; readonly Func toRawRequest; readonly Func fromRawResponse; - IClientStreamWriter requestStream; + IClientStreamWriter? requestStream; public AsyncClientStreamingCallWrapper(AsyncClientStreamingCall inner, Func toRawRequest, Func fromRawResponse) { @@ -169,8 +169,8 @@ class AsyncDuplexStreamingCallWrapper : IAsyncDuplexStreamingCallWrapper inner; readonly Func toRawRequest; readonly Func fromRawResponse; - IClientStreamWriter requestStream; - IAsyncStreamReader responseStream; + IClientStreamWriter? requestStream; + IAsyncStreamReader? responseStream; public AsyncDuplexStreamingCallWrapper(AsyncDuplexStreamingCall inner, Func toRawRequest, Func fromRawResponse) { diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/AsyncLock.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/AsyncLock.cs index 67aae46b5..4ea9be5fa 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/AsyncLock.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/AsyncLock.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; @@ -29,9 +29,9 @@ public async Task LockAsync() public struct LockReleaser : IDisposable { - readonly SemaphoreSlim semaphore; + readonly SemaphoreSlim? semaphore; - public LockReleaser(SemaphoreSlim semaphore) + public LockReleaser(SemaphoreSlim? semaphore) { this.semaphore = semaphore; } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/BroadcasterHelper.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/BroadcasterHelper.cs index f82c3839b..4b4813a5c 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/BroadcasterHelper.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/BroadcasterHelper.cs @@ -40,11 +40,7 @@ internal static MethodDefinition[] SearchDefinitions(Type interfaceType) return true; }) .Where(x => !x.IsSpecialName) - .Select(x => new MethodDefinition - { - ReceiverType = interfaceType, - MethodInfo = x, - }) + .Select(x => new MethodDefinition(interfaceType, x, default)) .ToArray(); } @@ -74,9 +70,16 @@ internal class MethodDefinition { public string Path => ReceiverType.Name + "/" + MethodInfo.Name; - public Type ReceiverType; - public MethodInfo MethodInfo; - public int MethodId; + public Type ReceiverType { get; set; } + public MethodInfo MethodInfo { get; set; } + public int MethodId { get; set; } + + public MethodDefinition(Type receiverType, MethodInfo methodInfo, int methodId) + { + ReceiverType = receiverType; + MethodInfo = methodInfo; + MethodId = methodId; + } } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/GrpcMethodHelper.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/GrpcMethodHelper.cs index 7ac93817c..3654c83d1 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/GrpcMethodHelper.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/GrpcMethodHelper.cs @@ -40,11 +40,11 @@ static T FromRaw(TRaw obj) { if (typeof(TRaw) == typeof(Box)) { - return ((Box)(object)obj).Value; + return ((Box)(object)obj!).Value; } else { - return DangerousDummyNull.GetObjectOrDefault(obj); + return DangerousDummyNull.GetObjectOrDefault(obj!); } } } @@ -53,7 +53,7 @@ public static MagicOnionMethod, TRawResponse> CreateMet { return CreateMethod(methodType, serviceName, name, null, messageSerializer); } - public static MagicOnionMethod, TRawResponse> CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo methodInfo, IMagicOnionSerializer messageSerializer) + public static MagicOnionMethod, TRawResponse> CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo? methodInfo, IMagicOnionSerializer messageSerializer) { // WORKAROUND: Prior to MagicOnion 5.0, the request type for the parameter-less method was byte[]. // DynamicClient sends byte[], but GeneratedClient sends Nil, which is incompatible, @@ -76,7 +76,7 @@ public static MagicOnionMethod C { return CreateMethod(methodType, serviceName, name, null, messageSerializer); } - public static MagicOnionMethod CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo methodInfo, IMagicOnionSerializer messageSerializer) + public static MagicOnionMethod CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo? methodInfo, IMagicOnionSerializer messageSerializer) { var isMethodRequestTypeBoxed = typeof(TRequest).IsValueType; var isMethodResponseTypeBoxed = typeof(TResponse).IsValueType; @@ -120,7 +120,9 @@ static Marshaller CreateMarshaller(IMagicOnionSerializer messageSerializer return new Marshaller( serializer: (obj, ctx) => { +#pragma warning disable CS8602 if (obj.GetType() == typeof(RawBytesBox)) +#pragma warning restore CS8602 { var rawBytesBox = (RawBytesBox)(object)obj; var writer = ctx.GetBufferWriter(); @@ -134,7 +136,7 @@ static Marshaller CreateMarshaller(IMagicOnionSerializer messageSerializer } ctx.Complete(); }, - deserializer: (ctx) => DangerousDummyNull.GetObjectOrDummyNull(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence()))); + deserializer: (ctx) => DangerousDummyNull.GetObjectOrDummyNull(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence()))!); } static Marshaller> CreateBoxedMarshaller(IMagicOnionSerializer messageSerializer) @@ -142,7 +144,9 @@ static Marshaller> CreateBoxedMarshaller(IMagicOnionSerializer message return new Marshaller>( serializer: (obj, ctx) => { +#pragma warning disable CS8602 if (obj.GetType() == typeof(RawBytesBox)) +#pragma warning restore CS8602 { var rawBytesBox = (RawBytesBox)(object)obj; var writer = ctx.GetBufferWriter(); @@ -156,7 +160,7 @@ static Marshaller> CreateBoxedMarshaller(IMagicOnionSerializer message } ctx.Complete(); }, - deserializer: (ctx) => Box.Create(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence())) + deserializer: (ctx) => Box.Create(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence())!) ); } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs index 1b8006644..ba6ef5531 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs @@ -18,7 +18,7 @@ public MagicOnionClientStreamWriter(IClientStreamWriter inner, Func inner.WriteAsync(toRawMessage(message)); - public WriteOptions WriteOptions + public WriteOptions? WriteOptions { get => inner.WriteOptions; set => inner.WriteOptions = value; diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs index a47bd9c3b..56b42ccde 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs @@ -18,7 +18,7 @@ public MagicOnionServerStreamWriter(IServerStreamWriter inner, Func inner.WriteAsync(toRawMessage(message)); - public WriteOptions WriteOptions + public WriteOptions? WriteOptions { get => inner.WriteOptions; set => inner.WriteOptions = value; diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MagicOnionMarshallers.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MagicOnionMarshallers.cs index c557fbac1..178445667 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MagicOnionMarshallers.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MagicOnionMarshallers.cs @@ -39,7 +39,7 @@ internal static Type CreateRequestType(ParameterInfo[] parameters) } else if (parameters.Length >= 16) { - throw new InvalidOperationException($"The method '{parameters[0].Member.DeclaringType.FullName}.{parameters[0].Member.Name}' must have less than 16 parameters. (Length: {parameters.Length})"); + throw new InvalidOperationException($"The method '{parameters[0].Member.DeclaringType!.FullName}.{parameters[0].Member.Name}' must have less than 16 parameters. (Length: {parameters.Length})"); } else { @@ -54,7 +54,7 @@ public static object InstantiateDynamicArgumentTuple(Type[] typeParameters, obje { // start from T2 var tupleTypeBase = dynamicArgumentTupleTypes[arguments.Length - 2]; - return Activator.CreateInstance(tupleTypeBase.MakeGenericType(typeParameters), arguments); + return Activator.CreateInstance(tupleTypeBase.MakeGenericType(typeParameters), arguments)!; } } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MetadataExtensions.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MetadataExtensions.cs index 40373dc55..b56870da4 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MetadataExtensions.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/MetadataExtensions.cs @@ -1,4 +1,4 @@ -using Grpc.Core; +using Grpc.Core; using System; namespace MagicOnion @@ -10,7 +10,7 @@ public static class MetadataExtensions /// /// Get metdata entry. If does not exists, return null. /// - public static Metadata.Entry Get(this Metadata metadata, string key, bool ignoreCase = true) + public static Metadata.Entry? GetOrDefault(this Metadata metadata, string key, bool ignoreCase = true) { for (int i = 0; i < metadata.Count; i++) { @@ -36,7 +36,7 @@ public static Metadata.Entry Get(this Metadata metadata, string key, bool ignore /// /// Get metdata string value. If does not exists, return null. /// - public static string GetValue(this Metadata metadata, string key, bool ignoreCase = true) + public static string? GetValueOrDefault(this Metadata metadata, string key, bool ignoreCase = true) { for (int i = 0; i < metadata.Count; i++) { diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs index 53318ea93..50f992aa5 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs @@ -15,11 +15,7 @@ public interface IMagicOnionSerializerProvider /// gRPC method type of the method. /// A method info for an implementation of the service method. It is a hint that handling request parameters on the server, which may be passed null on the client. /// -#if NET5_0_OR_GREATER IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo); -#else - IMagicOnionSerializer Create(MethodType methodType, MethodInfo methodInfo); -#endif } /// @@ -27,13 +23,8 @@ public interface IMagicOnionSerializerProvider /// public interface IMagicOnionSerializer { -#if NET5_0_OR_GREATER - void Serialize(IBufferWriter writer, in T? value); - T? Deserialize(in ReadOnlySequence bytes); -#else void Serialize(IBufferWriter writer, in T value); T Deserialize(in ReadOnlySequence bytes); -#endif } /// diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs index a6633affb..8da58b1c3 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs @@ -42,11 +42,7 @@ public MessagePackMagicOnionSerializerProvider WithEnableFallback(bool enableFal return new MessagePackMagicOnionSerializerProvider(SerializerOptions, enableFallback); } -#if NET5_0_OR_GREATER public IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo) -#else - public IMagicOnionSerializer Create(MethodType methodType, MethodInfo methodInfo) -#endif { var serializerOptions = EnableFallback && methodInfo != null ? WrapFallbackResolverIfNeeded(methodInfo.GetParameters()) : SerializerOptions; return new MessagePackMagicOnionSerializer(serializerOptions); @@ -96,7 +92,7 @@ MessagePackSerializerOptions WrapFallbackResolverIfNeeded(ParameterInfo[] parame } }).ToArray(); - var formatter = Activator.CreateInstance(formatterType, defaultValues); + var formatter = Activator.CreateInstance(formatterType, defaultValues)!; return SerializerOptions.WithResolver(new PriorityResolver(t, formatter, SerializerOptions.Resolver)); } @@ -111,7 +107,7 @@ public MessagePackMagicOnionSerializer(MessagePackSerializerOptions serializerOp public T Deserialize(in ReadOnlySequence bytes) => MessagePackSerializer.Deserialize(bytes, serializerOptions); - public void Serialize(IBufferWriter writer, in T value) + public void Serialize(IBufferWriter writer, in T? value) => MessagePackSerializer.Serialize(writer, value, serializerOptions); } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/UnsafeDirectBlitResolver.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/UnsafeDirectBlitResolver.cs index 64fcf8af1..868fc3d0b 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/UnsafeDirectBlitResolver.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/UnsafeDirectBlitResolver.cs @@ -31,14 +31,14 @@ public void Register() formatters.Add(typeof(T[]), new UnsafeDirectBlitArrayFormatter()); } - public IMessagePackFormatter GetFormatter() + public IMessagePackFormatter? GetFormatter() { return FormatterCache.formatter; } static class FormatterCache { - public static readonly IMessagePackFormatter formatter; + public static readonly IMessagePackFormatter? formatter; static FormatterCache() { @@ -46,7 +46,7 @@ static FormatterCache() var t = typeof(T); - object formatterObject; + object? formatterObject; if (Instance.formatters.TryGetValue(t, out formatterObject)) { formatter = (IMessagePackFormatter)formatterObject; @@ -55,7 +55,7 @@ static FormatterCache() } } - public class UnsafeDirectBlitArrayFormatter : IMessagePackFormatter + public class UnsafeDirectBlitArrayFormatter : IMessagePackFormatter where T : unmanaged { const int TypeCode = 45; @@ -70,7 +70,7 @@ public UnsafeDirectBlitArrayFormatter() this.StructLength = Unsafe.SizeOf(); } - public void Serialize(ref MessagePackWriter writer, T[] value, MessagePackSerializerOptions options) + public void Serialize(ref MessagePackWriter writer, T[]? value, MessagePackSerializerOptions options) { if (value == null) { @@ -101,7 +101,7 @@ public void Serialize(ref MessagePackWriter writer, T[] value, MessagePackSerial } } - public T[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + public T[]? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { if (reader.IsNil) { @@ -115,7 +115,7 @@ public T[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOption var isLittleEndian = extReader.ReadBoolean(); // extReader.read - byte[] rentMemory = default; + byte[]? rentMemory = default; ReadOnlySpan span = default; try { diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs index acf6209c9..0978ea830 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs @@ -1,12 +1,13 @@ -using System; +using System; using System.Buffers; +using System.Diagnostics.CodeAnalysis; namespace MagicOnion.Utils { internal sealed class ArrayPoolBufferWriter : IBufferWriter, IDisposable { [ThreadStatic] - static ArrayPoolBufferWriter staticInstance; + static ArrayPoolBufferWriter? staticInstance; public static ArrayPoolBufferWriter RentThreadStaticWriter() { @@ -20,7 +21,7 @@ public static ArrayPoolBufferWriter RentThreadStaticWriter() const int MinimumBufferSize = 32767; // use 32k buffer. - byte[] buffer; + byte[]? buffer; int index; void Prepare() @@ -37,9 +38,9 @@ void Prepare() public int WrittenCount => index; - public int Capacity => buffer.Length; + public int Capacity => buffer?.Length ?? throw new ObjectDisposedException(nameof(ArrayPoolBufferWriter)); - public int FreeCapacity => buffer.Length - index; + public int FreeCapacity => Capacity - index; public void Advance(int count) { @@ -61,6 +62,7 @@ public Span GetSpan(int sizeHint = 0) void CheckAndResizeBuffer(int sizeHint) { + if (buffer == null) throw new ObjectDisposedException(nameof(ArrayPoolBufferWriter)); if (sizeHint < 0) throw new ArgumentException(nameof(sizeHint)); if (sizeHint == 0) @@ -97,4 +99,4 @@ public void Dispose() buffer = null; } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/DynamicAssembly.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/DynamicAssembly.cs index 8e436b2f8..057a520e8 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/DynamicAssembly.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/DynamicAssembly.cs @@ -38,8 +38,8 @@ public DynamicAssembly(string moduleName) // HACK: Allow access to `internal` classes from dynamically generated assembly. // https://www.strathweb.com/2018/10/no-internalvisibleto-no-problem-bypassing-c-visibility-rules-with-roslyn/ - this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) }), new[] { "MagicOnion.Client" })); - this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) }), new[] { "MagicOnion.Server" })); + this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) })!, new[] { "MagicOnion.Client" })); + this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) })!, new[] { "MagicOnion.Server" })); } // requires lock on mono environment(for example, UnityEditor). see: https://github.com/neuecc/MessagePack-CSharp/issues/161 diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs index fc08c7f87..6430c4195 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs @@ -1,4 +1,4 @@ -#if NON_UNITY || !NET_STANDARD_2_0 +#if NON_UNITY || !NET_STANDARD_2_0 using System; using System.Reflection; @@ -161,10 +161,10 @@ public static void EmitNullReturn(this ILGenerator il) public static void EmitThrowNotimplemented(this ILGenerator il) { - il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetConstructor(Type.EmptyTypes)); + il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetConstructor(Type.EmptyTypes)!); il.Emit(OpCodes.Throw); } } } -#endif \ No newline at end of file +#endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs index af8ecfb11..1e100ebdd 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs @@ -13,8 +13,8 @@ public class ReservedWhenAllPromise : IValueTaskSource { static class ContinuationSentinel { - public static readonly Action AvailableContinuation = _ => { }; - public static readonly Action CompletedContinuation = _ => { }; + public static readonly Action AvailableContinuation = _ => { }; + public static readonly Action CompletedContinuation = _ => { }; } static readonly ContextCallback execContextCallback = ExecutionContextCallback; @@ -23,11 +23,11 @@ static class ContinuationSentinel int completedCount; readonly int resultCount; - ExceptionDispatchInfo exception; - Action continuation = ContinuationSentinel.AvailableContinuation; - object state; - SynchronizationContext syncContext; - ExecutionContext execContext; + ExceptionDispatchInfo? exception; + Action continuation = ContinuationSentinel.AvailableContinuation; + object? state; + SynchronizationContext? syncContext; + ExecutionContext? execContext; public ReservedWhenAllPromise(int reserveCount) { @@ -127,7 +127,7 @@ public ValueTaskSourceStatus GetStatus(short token) : ValueTaskSourceStatus.Pending; } - public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) + public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { if (Interlocked.CompareExchange(ref this.continuation, continuation, ContinuationSentinel.AvailableContinuation) != ContinuationSentinel.AvailableContinuation) { @@ -155,9 +155,11 @@ public void OnCompleted(Action continuation, object state, short token, } } - static void ExecutionContextCallback(object state) + static void ExecutionContextCallback(object? state) { - var t = (Tuple, ReservedWhenAllPromise>)state; + if (state is null) throw new ArgumentNullException(nameof(state)); + + var t = (Tuple, ReservedWhenAllPromise>)state; var self = t.Item2; if (self.syncContext != null) { @@ -171,9 +173,11 @@ static void ExecutionContextCallback(object state) } } - static void SynchronizationContextCallback(object state) + static void SynchronizationContextCallback(object? state) { - var t = (Tuple, ReservedWhenAllPromise>)state; + if (state is null) throw new ArgumentNullException(nameof(state)); + + var t = (Tuple, ReservedWhenAllPromise>)state; var self = t.Item2; var invokeState = self.state; self.state = null; @@ -182,4 +186,4 @@ static void SynchronizationContextCallback(object state) } } -#endif \ No newline at end of file +#endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/UniqueHashDictionary.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/UniqueHashDictionary.cs index 4dcbcac17..16fb9a4dc 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/UniqueHashDictionary.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnion.Shared/Utils/UniqueHashDictionary.cs @@ -1,7 +1,8 @@ -#if NON_UNITY +#if NON_UNITY using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace MagicOnion.Utils @@ -31,7 +32,9 @@ public class UniqueHashDictionary public int Count => count; public int MaxConflict => maxConflict; +#pragma warning disable CS8618 // NOTE: .NET Standard 2.1 doesn't have MemberNotNull. PolySharp is only enabled when targeting .NET Standard 2.0. public UniqueHashDictionary(params (int hashCode, T value)[] values) +#pragma warning restore CS8618 { CreateTable(values); } @@ -78,7 +81,7 @@ void CreateTable((int hashCode, T value)[] values) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetValue(int hashCode, out T value) + public bool TryGetValue(int hashCode, [NotNullWhen(true)] out T? value) { ref var array = ref table[hashCode & indexFor]; if (array == null) @@ -90,7 +93,7 @@ public bool TryGetValue(int hashCode, out T value) ref var v = ref array[0]; if (v.hash == hashCode) { - value = v.value; + value = v.value!; return true; } @@ -98,14 +101,14 @@ public bool TryGetValue(int hashCode, out T value) } [MethodImpl(MethodImplOptions.NoInlining)] - bool TryGetValueSlow(ref (int, T)[] array, int hashCode, out T value) + bool TryGetValueSlow(ref (int, T)[] array, int hashCode, [NotNullWhen(true)] out T? value) { for (int i = 1; i < array.Length; i++) // 0 is already checked. { ref var v = ref array[i]; if (v.Item1 == hashCode) { - value = v.Item2; + value = v.Item2!; return true; } } @@ -155,4 +158,4 @@ public override string ToString() } } -#endif \ No newline at end of file +#endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientBase.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientBase.cs index bb1f7b6aa..9a370287c 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientBase.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientBase.cs @@ -8,12 +8,12 @@ namespace MagicOnion.Client { public readonly struct MagicOnionClientOptions { - public string Host { get; } + public string? Host { get; } public CallInvoker CallInvoker { get; } public IReadOnlyList Filters { get; } public CallOptions CallOptions { get; } - public MagicOnionClientOptions(CallInvoker callInvoker, string host, CallOptions callOptions, IReadOnlyList filters) + public MagicOnionClientOptions(CallInvoker callInvoker, string? host, CallOptions callOptions, IReadOnlyList filters) { Host = host; CallOptions = callOptions; @@ -23,7 +23,7 @@ public MagicOnionClientOptions(CallInvoker callInvoker, string host, CallOptions public MagicOnionClientOptions WithCallOptions(CallOptions callOptions) => new MagicOnionClientOptions(CallInvoker, Host, callOptions, Filters); - public MagicOnionClientOptions WithHost(string host) + public MagicOnionClientOptions WithHost(string? host) => new MagicOnionClientOptions(CallInvoker, host, CallOptions, Filters); public MagicOnionClientOptions WithFilters(IReadOnlyList filters) => new MagicOnionClientOptions(CallInvoker, Host, CallOptions, filters); @@ -70,4 +70,4 @@ public virtual T WithHost(string host) public virtual T WithOptions(CallOptions options) => (T)(object)Clone(Options.WithCallOptions(options)); } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs index 431a9b648..552103dca 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs @@ -1,5 +1,6 @@ using MagicOnion.Serialization; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace MagicOnion.Client @@ -33,7 +34,7 @@ public interface IMagicOnionClientFactoryProvider /// A MagicOnion service interface type. /// A MagicOnionClient factory of specified service type. /// The value indicates whether a factory was found or not. - bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService; + bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService; } public class ImmutableMagicOnionClientFactoryProvider : IMagicOnionClientFactoryProvider @@ -50,7 +51,7 @@ public ImmutableMagicOnionClientFactoryProvider Add(IMagicOnionClientFactoryProv return new ImmutableMagicOnionClientFactoryProvider(providers.Append(provider).ToArray()); } - public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService + public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService { foreach (var provider in providers) { @@ -71,7 +72,7 @@ public class DynamicNotSupportedMagicOnionClientFactoryProvider : IMagicOnionCli DynamicNotSupportedMagicOnionClientFactoryProvider() { } - public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService + public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService { throw new InvalidOperationException($"Unable to find a client factory of type '{typeof(T)}'. If the application is running on IL2CPP or AOT, dynamic code generation is not supported. Please use the code generator (moc)."); } @@ -87,7 +88,7 @@ public class DynamicMagicOnionClientFactoryProvider : IMagicOnionClientFactoryPr DynamicMagicOnionClientFactoryProvider() { } - public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService + public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService { factory = Cache.Factory; return true; @@ -96,7 +97,7 @@ public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) whe static class Cache where T : IService { public static readonly MagicOnionClientFactoryDelegate Factory - = (clientOptions, serializerProvider) => (T)Activator.CreateInstance(DynamicClient.DynamicClientBuilder.ClientType, clientOptions, serializerProvider); + = (clientOptions, serializerProvider) => (T)Activator.CreateInstance(DynamicClient.DynamicClientBuilder.ClientType, clientOptions, serializerProvider)!; } } #endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/RequestContext.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/RequestContext.cs index c051df59f..bd91649cb 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/RequestContext.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/RequestContext.cs @@ -18,7 +18,7 @@ public RequestContext(T request, MagicOnionClientBase client, string methodPath, public abstract class RequestContext { - Dictionary items; + Dictionary? items; public string MethodPath { get; } public CallOptions CallOptions { get; } @@ -43,4 +43,4 @@ internal RequestContext(MagicOnionClientBase client, string methodPath, CallOpti this.RequestMethod = requestMethod; } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/ResponseContext.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/ResponseContext.cs index ba55aee45..6604a50f5 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/ResponseContext.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/ResponseContext.cs @@ -7,26 +7,26 @@ namespace MagicOnion.Client { internal class ResponseContextRaw : ResponseContext { - readonly AsyncUnaryCall inner; + readonly AsyncUnaryCall? inner; readonly bool hasValue; - readonly T value; + readonly T? value; readonly bool hasMetadataAndStatus; readonly Status status; - readonly Metadata responseHeaders; - readonly Metadata trailers; + readonly Metadata? responseHeaders; + readonly Metadata? trailers; readonly Func fromRawResponseToResponse; public ResponseContextRaw(T value, Status status, Metadata responseHeaders, Metadata trailers) - : this(null, hasValue: true, value, hasMetadataAndStatus: true, status, responseHeaders: responseHeaders, trailers: trailers, x => (T)(object)x) + : this(null, hasValue: true, value, hasMetadataAndStatus: true, status, responseHeaders: responseHeaders, trailers: trailers, x => (T)(object)x!) { } public ResponseContextRaw(AsyncUnaryCall inner, Func fromRawResponseToResponse) : this(inner, hasValue: false, default, hasMetadataAndStatus: false, default, default, default, fromRawResponseToResponse) { } - public ResponseContextRaw(AsyncUnaryCall inner, bool hasValue, T value, bool hasMetadataAndStatus, Status status, Metadata responseHeaders, Metadata trailers, Func fromRawResponseToResponse) + public ResponseContextRaw(AsyncUnaryCall? inner, bool hasValue, T? value, bool hasMetadataAndStatus, Status status, Metadata? responseHeaders, Metadata? trailers, Func fromRawResponseToResponse) { if (!hasValue && inner == null) throw new ArgumentNullException(nameof(inner)); if (hasMetadataAndStatus && responseHeaders == null) throw new ArgumentNullException(nameof(responseHeaders)); @@ -45,6 +45,9 @@ public ResponseContextRaw(AsyncUnaryCall inner, bool hasValue, T value, bo this.fromRawResponseToResponse = fromRawResponseToResponse; } + AsyncUnaryCall GetRequiredInner() + => inner ?? throw new InvalidOperationException("ResponseContextRaw has no inner AsyncUnaryCall."); + public override Type ResponseType => typeof(T); public override async Task WaitResponseAsync() @@ -56,24 +59,24 @@ public override async Task WaitResponseAsync() public override Status GetStatus() => hasMetadataAndStatus ? status - : inner.GetStatus(); + : GetRequiredInner().GetStatus(); public override Task ResponseHeadersAsync => hasMetadataAndStatus - ? Task.FromResult(responseHeaders) - : inner.ResponseHeadersAsync; - public override Metadata GetTrailers() + ? Task.FromResult(responseHeaders!) + : GetRequiredInner().ResponseHeadersAsync; + public override Metadata GetTrailers() => hasMetadataAndStatus - ? trailers - : inner.GetTrailers(); + ? trailers! + : GetRequiredInner().GetTrailers(); public override Task ResponseAsync => hasValue - ? Task.FromResult(value) + ? Task.FromResult(value!) : FromRawResponseToResponseAsync(); async Task FromRawResponseToResponseAsync() - => fromRawResponseToResponse(await inner.ResponseAsync.ConfigureAwait(false)); + => fromRawResponseToResponse(await GetRequiredInner().ResponseAsync.ConfigureAwait(false)); public override void Dispose() => inner?.Dispose(); @@ -106,13 +109,13 @@ public abstract class ResponseContext : IResponseContext public ResponseContext As() { - return this as ResponseContext; + return (ResponseContext)this; } public Task GetResponseAs() { var t = this as ResponseContext; - if (t == null) return Task.FromResult(default(T)); + if (t == null) return Task.FromResult(default(T)!); return t.ResponseAsync; } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClient.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClient.cs index 4f1805f39..879e46285 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClient.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClient.cs @@ -10,7 +10,7 @@ namespace MagicOnion.Client public static partial class StreamingHubClient { [Obsolete("Use ConnectAsync instead.")] - public static TStreamingHub Connect(ChannelBase channel, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null) + public static TStreamingHub Connect(ChannelBase channel, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null) where TStreamingHub : IStreamingHub { var hubClient = Connect(channel.CreateCallInvoker(), receiver, host, option, serializerProvider, factoryProvider, logger); @@ -22,7 +22,7 @@ public static partial class StreamingHubClient return hubClient; } - public static async Task ConnectAsync(ChannelBase channel, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null, CancellationToken cancellationToken = default) + public static async Task ConnectAsync(ChannelBase channel, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null, CancellationToken cancellationToken = default) where TStreamingHub : IStreamingHub { var hubClient = await ConnectAsync(channel.CreateCallInvoker(), receiver, host, option, serializerProvider, factoryProvider, logger, cancellationToken); @@ -35,7 +35,7 @@ public static partial class StreamingHubClient } [Obsolete("Use ConnectAsync instead.")] - public static TStreamingHub Connect(CallInvoker callInvoker, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null) + public static TStreamingHub Connect(CallInvoker callInvoker, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null) where TStreamingHub : IStreamingHub { var client = CreateClient(callInvoker, receiver, host, option, serializerProvider, factoryProvider, logger); @@ -58,7 +58,7 @@ async void ConnectAndForget() return (TStreamingHub)(object)client; } - public static async Task ConnectAsync(CallInvoker callInvoker, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null, CancellationToken cancellationToken = default) + public static async Task ConnectAsync(CallInvoker callInvoker, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null, CancellationToken cancellationToken = default) where TStreamingHub : IStreamingHub { var client = CreateClient(callInvoker, receiver, host, option, serializerProvider, factoryProvider, logger); @@ -66,11 +66,12 @@ async void ConnectAndForget() return (TStreamingHub)(object)client; } - static StreamingHubClientBase CreateClient(CallInvoker callInvoker, TReceiver receiver, string host, CallOptions option, IMagicOnionSerializerProvider serializerProvider, IStreamingHubClientFactoryProvider factoryProvider, IMagicOnionClientLogger logger) + static StreamingHubClientBase CreateClient(CallInvoker callInvoker, TReceiver receiver, string? host, CallOptions option, IMagicOnionSerializerProvider? serializerProvider, IStreamingHubClientFactoryProvider? factoryProvider, IMagicOnionClientLogger? logger) where TStreamingHub : IStreamingHub { - serializerProvider = serializerProvider ?? MagicOnionSerializerProvider.Default; - factoryProvider = factoryProvider ?? StreamingHubClientFactoryProvider.Default; + serializerProvider ??= MagicOnionSerializerProvider.Default; + factoryProvider ??= StreamingHubClientFactoryProvider.Default; + logger ??= NullMagicOnionClientLogger.Instance; if (!factoryProvider.TryGetFactory(out var factory)) { diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientBase.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientBase.cs index b32564cd7..125f165a4 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientBase.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientBase.cs @@ -23,19 +23,20 @@ public abstract class StreamingHubClientBase const string StreamingHubVersionHeaderValue = "2"; #pragma warning restore IDE1006 // Naming Styles - readonly string host; + readonly string? host; readonly CallOptions option; readonly CallInvoker callInvoker; readonly IMagicOnionClientLogger logger; readonly IMagicOnionSerializer messageSerializer; readonly AsyncLock asyncLock = new AsyncLock(); - IClientStreamWriter writer; - IAsyncStreamReader reader; + IClientStreamWriter writer = default!; + IAsyncStreamReader reader = default!; - protected TReceiver receiver; - Task subscription; - TaskCompletionSource waitForDisconnect = new TaskCompletionSource(); + protected TReceiver receiver = default!; + Task subscription = default!; + + TaskCompletionSource waitForDisconnect = new TaskCompletionSource(); // {messageId, TaskCompletionSource} ConcurrentDictionary responseFutures = new ConcurrentDictionary(); @@ -43,7 +44,7 @@ public abstract class StreamingHubClientBase int messageId = 0; bool disposed; - protected StreamingHubClientBase(CallInvoker callInvoker, string host, CallOptions option, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) + protected StreamingHubClientBase(CallInvoker callInvoker, string? host, CallOptions option, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) { this.callInvoker = callInvoker ?? throw new ArgumentNullException(nameof(callInvoker)); this.host = host; @@ -65,7 +66,7 @@ public async Task __ConnectAndSubscribeAsync(TReceiver receiver, CancellationTok this.receiver = receiver; // Establish StreamingHub connection between the client and the server. - Metadata.Entry messageVersion = default; + Metadata.Entry? messageVersion; try { // The client can read the response headers before any StreamingHub's message. @@ -120,7 +121,7 @@ protected T Deserialize(ArraySegment bytes) protected abstract void OnResponseEvent(int methodId, object taskCompletionSource, ArraySegment data); protected abstract void OnBroadcastEvent(int methodId, ArraySegment data); - async Task StartSubscribe(SynchronizationContext syncContext, Task firstMoveNext) + async Task StartSubscribe(SynchronizationContext? syncContext, Task firstMoveNext) { var reader = this.reader; try @@ -138,7 +139,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo // log post on main thread. if (syncContext != null) { - syncContext.Post(state => logger.Error((Exception)state, msg), ex); + syncContext.Post(state => logger.Error((Exception)state!, msg), ex); } else { @@ -159,7 +160,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo // log post on main thread. if (syncContext != null) { - syncContext.Post(state => logger.Error((Exception)state, msg), ex); + syncContext.Post(state => logger.Error((Exception)state!, msg), ex); } else { @@ -182,7 +183,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo } finally { - waitForDisconnect.TrySetResult(null); + waitForDisconnect.TrySetResult(true); } } } @@ -191,7 +192,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo // broadcast: [methodId, [argument]] // response: [messageId, methodId, response] // error-response: [messageId, statusCode, detail, StringMessage] - void ConsumeData(SynchronizationContext syncContext, byte[] data) + void ConsumeData(SynchronizationContext? syncContext, byte[] data) { var messagePackReader = new MessagePackReader(data); var arrayLength = messagePackReader.ReadArrayHeader(); @@ -228,11 +229,11 @@ void ConsumeData(SynchronizationContext syncContext, byte[] data) var ex = default(RpcException); if (string.IsNullOrWhiteSpace(error)) { - ex = new RpcException(new Status((StatusCode)statusCode, detail)); + ex = new RpcException(new Status((StatusCode)statusCode, detail ?? string.Empty)); } else { - ex = new RpcException(new Status((StatusCode)statusCode, detail), detail + Environment.NewLine + error); + ex = new RpcException(new Status((StatusCode)statusCode, detail ?? string.Empty), detail + Environment.NewLine + error); } future.TrySetException(ex); @@ -247,7 +248,7 @@ void ConsumeData(SynchronizationContext syncContext, byte[] data) var tuple = Tuple.Create(methodId, data, offset, data.Length - offset); syncContext.Post(state => { - var t = (Tuple)state; + var t = (Tuple)state!; OnBroadcastEvent(t.Item1, new ArraySegment(t.Item2, t.Item3, t.Item4)); }, tuple); } @@ -281,7 +282,7 @@ byte[] BuildMessage() await writer.WriteAsync(v).ConfigureAwait(false); } - return default; + return default!; } protected async Task WriteMessageWithResponseAsync(int methodId, TRequest message) @@ -367,7 +368,7 @@ async Task DisposeAsyncCore(bool waitSubscription) } // cleanup completion - List aggregateException = null; + List? aggregateException = null; foreach (var item in responseFutures) { try @@ -378,11 +379,8 @@ async Task DisposeAsyncCore(bool waitSubscription) { if (!(ex is OperationCanceledException)) { - if (aggregateException != null) - { - aggregateException = new List(); - aggregateException.Add(ex); - } + aggregateException ??= new List(); + aggregateException.Add(ex); } } } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs index c976c839c..22dd65478 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs @@ -1,5 +1,6 @@ using Grpc.Core; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using MagicOnion.Serialization; @@ -21,7 +22,7 @@ public static class StreamingHubClientFactoryProvider #endif } - public delegate TStreamingHub StreamingHubClientFactoryDelegate(CallInvoker callInvoker, TReceiver receiver, string host, CallOptions callOptions, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) + public delegate TStreamingHub StreamingHubClientFactoryDelegate(CallInvoker callInvoker, TReceiver receiver, string? host, CallOptions callOptions, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) where TStreamingHub : IStreamingHub; /// @@ -36,7 +37,7 @@ public interface IStreamingHubClientFactoryProvider /// A hub receiver interface type. /// A StreamingHubClient factory of specified service type. /// The value indicates whether a factory was found or not. - bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub; + bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub; } public class ImmutableStreamingHubClientFactoryProvider : IStreamingHubClientFactoryProvider @@ -53,7 +54,7 @@ public ImmutableStreamingHubClientFactoryProvider Add(IStreamingHubClientFactory return new ImmutableStreamingHubClientFactoryProvider(providers.Append(provider).ToArray()); } - public bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub + public bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub { foreach (var provider in providers) { @@ -74,7 +75,7 @@ public class DynamicNotSupportedStreamingHubClientFactoryProvider : IStreamingHu DynamicNotSupportedStreamingHubClientFactoryProvider() { } - public bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub + public bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub { throw new InvalidOperationException($"Unable to find a client factory of type '{typeof(TStreamingHub)}'. If the application is running on IL2CPP or AOT, dynamic code generation is not supported. Please use the code generator (moc)."); } @@ -87,7 +88,7 @@ public class DynamicStreamingHubClientFactoryProvider : IStreamingHubClientFacto DynamicStreamingHubClientFactoryProvider() { } - public bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub + public bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub { factory = Cache.Factory; return true; @@ -97,7 +98,7 @@ static class Cache where TStreamingHub : IStreamingHub { public static readonly StreamingHubClientFactoryDelegate Factory = (callInvoker, receiver, host, callOptions, serializerProvider, logger) - => (TStreamingHub)Activator.CreateInstance(DynamicClient.DynamicStreamingHubClientBuilder.ClientType, callInvoker, host, callOptions, serializerProvider, logger); + => (TStreamingHub)Activator.CreateInstance(DynamicClient.DynamicStreamingHubClientBuilder.ClientType, callInvoker, host, callOptions, serializerProvider, logger)!; } } #endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp new file mode 100644 index 000000000..2dabb0c53 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp @@ -0,0 +1 @@ +-nullable \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp.meta new file mode 100644 index 000000000..a7b9671f5 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/csc.rsp.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7a2a9235f061e264e8c43a0335718cab +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProvider.cs index 45d36211c..73abccd90 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProvider.cs @@ -40,7 +40,7 @@ public static GrpcChannelx CreateChannel(this IGrpcChannelProvider provider, Grp /// public static class GrpcChannelProvider { - static IGrpcChannelProvider defaultProvider; + static IGrpcChannelProvider? defaultProvider; /// /// Gets a default channel provider. the provider will be initialized by . @@ -208,7 +208,7 @@ public GrpcNetClientGrpcChannelProvider(Func optionsFactory) protected override GrpcChannelx CreateChannelCore(int id, CreateGrpcChannelContext context) { var address = new Uri((context.Target.IsInsecure ? "http" : "https") + $"://{context.Target.Host}:{context.Target.Port}"); - var channelOptions = context.ChannelOptions.Get() ?? defaultChannelOptionsFactory(); + var channelOptions = context.ChannelOptions.GetOrDefault() ?? defaultChannelOptionsFactory(); var channel = GrpcChannel.ForAddress(address, channelOptions); var channelHolder = new GrpcChannelx( id, @@ -251,7 +251,7 @@ public GrpcCCoreGrpcChannelProvider(GrpcCCoreChannelOptions channelOptions) /// protected override GrpcChannelx CreateChannelCore(int id, CreateGrpcChannelContext context) { - var channelOptions = context.ChannelOptions.Get() ?? defaultChannelOptions; + var channelOptions = context.ChannelOptions.GetOrDefault() ?? defaultChannelOptions; var channel = new Channel(context.Target.Host, context.Target.Port, context.Target.IsInsecure ? ChannelCredentials.Insecure : channelOptions.ChannelCredentials, channelOptions.ChannelOptions); var channelHolder = new GrpcChannelx( id, @@ -270,7 +270,7 @@ public sealed class GrpcCCoreChannelOptions public IReadOnlyList ChannelOptions { get; set; } public ChannelCredentials ChannelCredentials { get; set; } - public GrpcCCoreChannelOptions(IReadOnlyList channelOptions = null, ChannelCredentials channelCredentials = null) + public GrpcCCoreChannelOptions(IReadOnlyList? channelOptions = null, ChannelCredentials? channelCredentials = null) { ChannelOptions = channelOptions ?? Array.Empty(); ChannelCredentials = channelCredentials ?? new SslCredentials(); diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProviderHost.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProviderHost.cs index f524bab51..ca48d7fe9 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProviderHost.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelProviderHost.cs @@ -24,7 +24,7 @@ namespace MagicOnion.Unity /// public class GrpcChannelProviderHost : MonoBehaviour { - public IGrpcChannelProvider Provider { get; private set; } + public IGrpcChannelProvider? Provider { get; private set; } /// /// Initialize gRPC channel provider and the host. @@ -56,12 +56,12 @@ private void InitializeCore(IGrpcChannelProvider provider) private void OnDestroy() { - Provider.ShutdownAllChannels(); + Provider?.ShutdownAllChannels(); } private void OnApplicationQuit() { - Provider.ShutdownAllChannels(); + Provider?.ShutdownAllChannels(); } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelx.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelx.cs index 51df48d26..d1a3abf60 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelx.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/GrpcChannelx.cs @@ -443,7 +443,7 @@ private Method WrapMethod(Method { private readonly SerializationContext _inner; - private IBufferWriter _bufferWriter; + private IBufferWriter? _bufferWriter; public int Written { get; private set; } public SerializationContextWrapper(SerializationContext inner) @@ -495,4 +495,4 @@ public interface IGrpcChannelxDiagnosticsInfo ChannelBase UnderlyingChannel { get; } } #endif -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/IGrpcChannelProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/IGrpcChannelProvider.cs index 008ddc45a..b60a13769 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/IGrpcChannelProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/IGrpcChannelProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Grpc.Core; #if USE_GRPC_NET_CLIENT using Grpc.Net.Client; @@ -41,7 +42,7 @@ public class CreateGrpcChannelContext public GrpcChannelTarget Target { get; } public GrpcChannelOptionsBag ChannelOptions { get; } - public CreateGrpcChannelContext(IGrpcChannelProvider provider, GrpcChannelTarget target, object channelOptions = null) + public CreateGrpcChannelContext(IGrpcChannelProvider provider, GrpcChannelTarget target, object? channelOptions = null) { Provider = provider ?? throw new ArgumentNullException(nameof(provider)); Target = target; @@ -51,19 +52,19 @@ public CreateGrpcChannelContext(IGrpcChannelProvider provider, GrpcChannelTarget public class GrpcChannelOptionsBag { - private readonly object _options; + private readonly object? _options; - public GrpcChannelOptionsBag(object options) + public GrpcChannelOptionsBag(object? options) { _options = options; } - public T Get() + public T? GetOrDefault() { return TryGet(out var value) ? value : default; } - public bool TryGet(out T value) + public bool TryGet([NotNullWhen(true)] out T? value) { if (_options is T optionT) { @@ -98,4 +99,4 @@ public IEnumerable> GetValues() #endif } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/UnityDebugLogger.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/UnityDebugLogger.cs index f2d4d9912..3783a18a1 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/UnityDebugLogger.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/UnityDebugLogger.cs @@ -6,7 +6,7 @@ namespace MagicOnion { public class UnityDebugLogger : ILogger { - readonly Type forType; + readonly Type? forType; readonly string forTypeString; readonly bool errorToWarn = true; // default is true(gRPC internal log show to warn) @@ -21,7 +21,7 @@ public UnityDebugLogger(bool errorToWarn) this.errorToWarn = errorToWarn; } - protected UnityDebugLogger(Type forType) + protected UnityDebugLogger(Type? forType) { this.forType = forType; if (forType != null) @@ -126,7 +126,7 @@ public void Error(Exception exception, string message) } /// Gets the type associated with this logger. - protected Type AssociatedType + protected Type? AssociatedType { get { return forType; } } @@ -156,4 +156,4 @@ string BuildMessage(string format, object[] args) } } } -#endif \ No newline at end of file +#endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp new file mode 100644 index 000000000..2dabb0c53 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp @@ -0,0 +1 @@ +-nullable \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp.meta new file mode 100644 index 000000000..6de9611b7 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Unity/csc.rsp.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e421e7b92e8f0324abb9e49ef9591026 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs b/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs index b594abead..2130aad07 100644 --- a/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs +++ b/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs @@ -33,7 +33,7 @@ static Type Build() EmitServiceClientClass(buildContext); - return buildContext.ServiceClientType.CreateTypeInfo(); + return buildContext.ServiceClientType.CreateTypeInfo()!; } class ServiceClientBuildContext @@ -45,13 +45,13 @@ public ServiceClientBuildContext(ServiceClientDefinition definition) public ServiceClientDefinition Definition { get; } - public TypeBuilder ClientCoreType { get; set; } // {ServiceName}Client+ClientCore - public ConstructorBuilder ClientCoreConstructor { get; set; } // {ServiceName}Client+ClientCore..ctor + public TypeBuilder ClientCoreType { get; set; } = default!; // {ServiceName}Client+ClientCore + public ConstructorBuilder ClientCoreConstructor { get; set; } = default!; // {ServiceName}Client+ClientCore..ctor - public TypeBuilder ServiceClientType { get; set; } // {ServiceName}Client - public ConstructorBuilder ServiceClientConstructor { get; set; } // {ServiceName}Client..ctor - public ConstructorBuilder ServiceClientConstructorForClone { get; set; } // {ServiceName}Client..ctor - public FieldBuilder FieldCore { get; set; } + public TypeBuilder ServiceClientType { get; set; } = default!; // {ServiceName}Client + public ConstructorBuilder ServiceClientConstructor { get; set; } = default!; // {ServiceName}Client..ctor + public ConstructorBuilder ServiceClientConstructorForClone { get; set; } = default!; // {ServiceName}Client..ctor + public FieldBuilder FieldCore { get; set; } = default!; public Dictionary FieldAndMethodInvokerTypeByMethod { get; } = new Dictionary(); } @@ -65,7 +65,7 @@ static void EmitServiceClientClass(ServiceClientBuildContext ctx) // ctx.ServiceClientType = DynamicClientAssemblyHolder.Assembly.DefineType($"MagicOnion.DynamicallyGeneratedClient.{ctx.Definition.ServiceInterfaceType.Namespace}.{ctx.Definition.ServiceInterfaceType.Name}Client", TypeAttributes.Public | TypeAttributes.Sealed, constructedBaseClientType, new[] { ctx.Definition.ServiceInterfaceType }); // Set `IgnoreAttribute` to the generated client type. Hides generated-types from building MagicOnion service definitions. - ctx.ServiceClientType.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoreAttribute).GetConstructor(Type.EmptyTypes), Array.Empty())); + ctx.ServiceClientType.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoreAttribute).GetConstructor(Type.EmptyTypes)!, Array.Empty())); { // class ClientCore { ... } EmitClientCore(ctx); @@ -98,13 +98,13 @@ static void EmitClone(ServiceClientBuildContext ctx, Type constructedBaseClientT static void EmitConstructor(ServiceClientBuildContext ctx) { - var baseCtor = ctx.ServiceClientType.BaseType.GetConstructor( + var baseCtor = ctx.ServiceClientType.BaseType!.GetConstructor( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Standard, new[] { typeof(MagicOnionClientOptions) }, Array.Empty() - ); + )!; // public {ServiceName}Client(MagicOnionClientOptions options, IMagicOnionSerializerProvider serializerProvider) { ctx.ServiceClientConstructor = ctx.ServiceClientType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, KnownTypes.ClientConstructorParameters); { @@ -165,7 +165,7 @@ static void EmitServiceMethods(ServiceClientBuildContext ctx) foreach (var method in ctx.Definition.Methods) { var hasNonGenericUnaryResult = method.MethodReturnType == typeof(UnaryResult); - var methodInvokerInvokeMethod = ctx.FieldAndMethodInvokerTypeByMethod[method.MethodName].MethodInvokerType.GetMethod($"Invoke{method.MethodType}{(hasNonGenericUnaryResult ? "NonGeneric" : "")}"); + var methodInvokerInvokeMethod = ctx.FieldAndMethodInvokerTypeByMethod[method.MethodName].MethodInvokerType.GetMethod($"Invoke{method.MethodType}{(hasNonGenericUnaryResult ? "NonGeneric" : "")}")!; var methodBuilder = ctx.ServiceClientType.DefineMethod(method.MethodName, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, methodInvokerInvokeMethod.ReturnType, method.ParameterTypes.ToArray()); var il = methodBuilder.GetILGenerator(); @@ -208,13 +208,13 @@ static void EmitServiceMethods(ServiceClientBuildContext ctx) break; } } - il.Emit(OpCodes.Newobj, method.RequestType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, method.ParameterTypes.ToArray(), Array.Empty())); + il.Emit(OpCodes.Newobj, method.RequestType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, method.ParameterTypes.ToArray(), Array.Empty())!); } } else if (method.ParameterTypes.Count == 0) { // Nil.Default - il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default", BindingFlags.Public | BindingFlags.Static)); + il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default", BindingFlags.Public | BindingFlags.Static)!); } } else diff --git a/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs b/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs index 4d36c23e3..13786bf20 100644 --- a/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs +++ b/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs @@ -53,9 +53,9 @@ static class DynamicStreamingHubClientBuilder // static readonly Type ClientFireAndForgetType; static readonly Type bytesMethod = typeof(Method<,>).MakeGenericType(new[] { typeof(byte[]), typeof(byte[]) }); - static readonly FieldInfo throughMarshaller = typeof(MagicOnionMarshallers).GetField("ThroughMarshaller", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + static readonly FieldInfo throughMarshaller = typeof(MagicOnionMarshallers).GetField("ThroughMarshaller", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)!; - static readonly ConstructorInfo notSupportedException = typeof(NotSupportedException).GetConstructor(Type.EmptyTypes); + static readonly ConstructorInfo notSupportedException = typeof(NotSupportedException).GetConstructor(Type.EmptyTypes)!; static DynamicStreamingHubClientBuilder() { @@ -85,7 +85,7 @@ static DynamicStreamingHubClientBuilder() DefineMethods(typeBuilder, t, typeof(TReceiver), methodField, clientField, methodDefinitions); } - ClientType = typeBuilder.CreateTypeInfo().AsType(); + ClientType = typeBuilder.CreateTypeInfo()!.AsType(); } static MethodDefinition[] SearchDefinitions(Type interfaceType) @@ -115,11 +115,7 @@ static MethodDefinition[] SearchDefinitions(Type interfaceType) return true; }) .Where(x => !x.IsSpecialName) - .Select(x => new MethodDefinition - { - ServiceType = interfaceType, - MethodInfo = x, - }) + .Select(x => new MethodDefinition(interfaceType, x, default, default)) .ToArray(); } @@ -188,11 +184,11 @@ static FieldInfo DefineConstructor(TypeBuilder typeBuilder, Type interfaceType, il.Emit(OpCodes.Ldarg_3); il.Emit(OpCodes.Ldarg_S, (byte)4); il.Emit(OpCodes.Ldarg_S, (byte)5); - il.Emit(OpCodes.Call, typeBuilder.BaseType + il.Emit(OpCodes.Call, typeBuilder.BaseType! .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).First()); // { this.fireAndForgetClient = new FireAndForgetClient(this); } - var clientField = typeBuilder.DefineField("fireAndForgetClient", fireAndForgetClientCtor.DeclaringType, FieldAttributes.Private); + var clientField = typeBuilder.DefineField("fireAndForgetClient", fireAndForgetClientCtor.DeclaringType!, FieldAttributes.Private); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Newobj, fireAndForgetClientCtor); @@ -229,8 +225,8 @@ static Tuple DefineFireAndForgetConstructor(Ty static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type receiverType, FieldInfo methodField, FieldInfo clientField, MethodDefinition[] definitions) { var baseType = typeof(StreamingHubClientBase<,>).MakeGenericType(interfaceType, receiverType); - var serializerOptionsField = baseType.GetField("serializerOptions", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var receiverField = baseType.GetField("receiver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var serializerOptionsField = baseType.GetField("serializerOptions", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; + var receiverField = baseType.GetField("receiver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; // protected abstract Method DuplexStreamingAsyncMethod { get; } { @@ -251,7 +247,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece var il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, baseType.GetMethod("DisposeAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)); + il.Emit(OpCodes.Call, baseType.GetMethod("DisposeAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!); il.Emit(OpCodes.Ret); } // Task WaitForDisconnect(); @@ -261,7 +257,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece var il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, baseType.GetMethod("WaitForDisconnect", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)); + il.Emit(OpCodes.Call, baseType.GetMethod("WaitForDisconnect", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!); il.Emit(OpCodes.Ret); } // TSelf FireAndForget(); @@ -318,7 +314,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldarg_2); // taskCompletionSource il.Emit(OpCodes.Ldarg_3); // data - il.Emit(OpCodes.Call, baseType.GetMethod("SetResultForResponse", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(responseType)); + il.Emit(OpCodes.Call, baseType.GetMethod("SetResultForResponse", BindingFlags.Instance | BindingFlags.NonPublic)!.MakeGenericMethod(responseType)); il.Emit(OpCodes.Ret); } @@ -352,7 +348,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece // var value = Deserialize>(data); // receiver.OnMessage(value.Item1, value.Item2); - var deserializeMethod = baseType.GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.NonPublic); + var deserializeMethod = baseType.GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.NonPublic)!; var parameters = item.def.MethodInfo.GetParameters(); if (parameters.Length == 0) { @@ -394,7 +390,7 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece for (int i = 0; i < parameters.Length; i++) { il.Emit(OpCodes.Ldloc, lc); - il.Emit(OpCodes.Ldfld, deserializeType.GetField("Item" + (i + 1))); + il.Emit(OpCodes.Ldfld, deserializeType.GetField("Item" + (i + 1))!); } il.Emit(OpCodes.Callvirt, item.def.MethodInfo); } @@ -430,12 +426,12 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece il.Emit(OpCodes.Ldarg, j + 1); } - Type callType = null; + Type callType; if (parameters.Length == 0) { // use Nil. callType = typeof(Nil); - il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")); + il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")!); } else if (parameters.Length == 1) { @@ -445,30 +441,30 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, Type rece else { // call new DynamicArgumentTuple - callType = def.RequestType; + callType = def.RequestType!; il.Emit(OpCodes.Newobj, callType.GetConstructors().First()); } if (def.MethodInfo.ReturnType == typeof(Task) || def.MethodInfo.ReturnType == typeof(ValueTask)) { - var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; il.Emit(OpCodes.Callvirt, mInfo.MakeGenericMethod(callType, typeof(Nil))); } else { - var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var mInfo = baseType.GetMethod("WriteMessageWithResponseAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!; il.Emit(OpCodes.Callvirt, mInfo.MakeGenericMethod(callType, def.MethodInfo.ReturnType.GetGenericArguments()[0])); } // If the return type is `ValueTask`, the task must be wrapped as ValueTask. if (def.MethodInfo.ReturnType == typeof(ValueTask)) { - il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })!); } else if (def.MethodInfo.IsGenericMethod && def.MethodInfo.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) { var returnTypeOfT = def.MethodInfo.ReturnType.GetGenericArguments()[0]; - il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })!); } il.Emit(OpCodes.Ret); @@ -533,12 +529,12 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy il.Emit(OpCodes.Ldarg, j + 1); } - Type requestType = null; + Type requestType; if (parameters.Length == 0) { // use Nil. requestType = typeof(Nil); - il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")); + il.Emit(OpCodes.Ldsfld, typeof(Nil).GetField("Default")!); } else if (parameters.Length == 1) { @@ -548,7 +544,7 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy else { // call new DynamicArgumentTuple - requestType = def.RequestType; + requestType = def.RequestType!; il.Emit(OpCodes.Newobj, requestType.GetConstructors().First()); } @@ -561,7 +557,7 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy { responseType = def.MethodInfo.ReturnType.GetGenericArguments()[0]; } - var mInfo = parentNestedType.BaseType + var mInfo = parentNestedType.BaseType! .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Single(x => x.Name == "WriteMessageFireAndForgetAsync"); // WriteMessageAsyncFireAndForget il.Emit(OpCodes.Callvirt, mInfo.MakeGenericMethod(requestType, responseType)); @@ -569,12 +565,12 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy // If the return type is `ValueTask`, the task must be wrapped as ValueTask. if (def.MethodInfo.ReturnType == typeof(ValueTask)) { - il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor(new [] { typeof(Task) })!); } else if (def.MethodInfo.IsGenericMethod && def.MethodInfo.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) { var returnTypeOfT = def.MethodInfo.ReturnType.GetGenericArguments()[0]; - il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })); + il.Emit(OpCodes.Newobj, typeof(ValueTask<>).MakeGenericType(returnTypeOfT).GetConstructor(new [] { typeof(Task<>).MakeGenericType(returnTypeOfT) })!); } il.Emit(OpCodes.Ret); @@ -586,11 +582,19 @@ class MethodDefinition { public string Path => ServiceType.Name + "/" + MethodInfo.Name; - public Type ServiceType; - public MethodInfo MethodInfo; - public int MethodId; + public Type ServiceType { get; set; } + public MethodInfo MethodInfo { get; set; } + public int MethodId { get; set; } + + public Type? RequestType { get; set; } - public Type RequestType; + public MethodDefinition(Type serviceType, MethodInfo methodInfo, int methodId, Type? requestType) + { + ServiceType = serviceType; + MethodInfo = methodInfo; + MethodId = methodId; + RequestType = requestType; + } } } } diff --git a/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs b/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs index 8116fbb20..74499020b 100644 --- a/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs +++ b/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs @@ -7,10 +7,10 @@ namespace MagicOnion.Client.DynamicClient { internal static class RawMethodInvokerTypes { - static readonly MethodInfo create_RefType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_RefType), BindingFlags.Static | BindingFlags.Public); - static readonly MethodInfo create_RefType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_ValueType), BindingFlags.Static | BindingFlags.Public); - static readonly MethodInfo create_ValueType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_RefType), BindingFlags.Static | BindingFlags.Public); - static readonly MethodInfo create_ValueType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_ValueType), BindingFlags.Static | BindingFlags.Public); + static readonly MethodInfo create_RefType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_RefType), BindingFlags.Static | BindingFlags.Public)!; + static readonly MethodInfo create_RefType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_ValueType), BindingFlags.Static | BindingFlags.Public)!; + static readonly MethodInfo create_ValueType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_RefType), BindingFlags.Static | BindingFlags.Public)!; + static readonly MethodInfo create_ValueType_ValueType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_ValueType_ValueType), BindingFlags.Static | BindingFlags.Public)!; public static MethodInfo GetMethodRawInvokerCreateMethod(Type requestType, Type responseType) { @@ -30,4 +30,4 @@ public static MethodInfo GetMethodRawInvokerCreateMethod(Type requestType, Type return create_RefType_RefType.MakeGenericMethod(requestType, responseType); } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs b/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs index ee925532c..94b8c361d 100644 --- a/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs +++ b/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs @@ -91,7 +91,7 @@ private void Verify() } } - private static (MethodType MethodType, Type RequestType, Type ResponseType) GetMethodTypeAndResponseTypeFromMethod(MethodInfo methodInfo) + private static (MethodType MethodType, Type? RequestType, Type ResponseType) GetMethodTypeAndResponseTypeFromMethod(MethodInfo methodInfo) { const string UnsupportedReturnTypeErrorMessage = "The method of a service must return 'UnaryResult', 'Task>', 'Task>' or 'DuplexStreamingResult'."; @@ -103,7 +103,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM } if (!returnType.IsGenericType) { - throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } var isTaskOfT = false; @@ -114,7 +114,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM returnType = returnType.GetGenericArguments()[0]; if (!returnType.IsGenericType) { - throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } returnTypeOpen = returnType.GetGenericTypeDefinition(); } @@ -123,7 +123,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (isTaskOfT) { - throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.Unary, null, typeof(Nil)); } @@ -131,7 +131,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (isTaskOfT) { - throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.Unary, null, returnType.GetGenericArguments()[0]); } @@ -139,7 +139,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (!isTaskOfT) { - throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.ClientStreaming, returnType.GetGenericArguments()[0], returnType.GetGenericArguments()[1]); } @@ -147,7 +147,7 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (!isTaskOfT) { - throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.ServerStreaming, null, returnType.GetGenericArguments()[0]); // Use method parameters as response type } @@ -155,13 +155,13 @@ private static (MethodType MethodType, Type RequestType, Type ResponseType) GetM { if (!isTaskOfT) { - throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"The return type of a Streaming method must be 'Task'. (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } return (MethodType.DuplexStreaming, returnType.GetGenericArguments()[0], returnType.GetGenericArguments()[1]); } else { - throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType.Name}.{methodInfo.Name})"); + throw new InvalidOperationException($"{UnsupportedReturnTypeErrorMessage} (Method: {methodInfo.DeclaringType!.Name}.{methodInfo.Name})"); } } } diff --git a/src/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs b/src/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs index b00ac82ed..fafaf1d52 100644 --- a/src/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs +++ b/src/MagicOnion.Client/Internal/MagicOnionMethodInvoker.cs @@ -109,7 +109,7 @@ class AsyncServerStreamingCallWrapper : IAsyncServerStreamingCallWrapper inner; readonly Func fromRawResponse; - IAsyncStreamReader responseStream; + IAsyncStreamReader? responseStream; public AsyncServerStreamingCallWrapper(AsyncServerStreamingCall inner, Func fromRawResponse) { @@ -136,7 +136,7 @@ class AsyncClientStreamingCallWrapper : IAsyncClientStreamingCallWrapper inner; readonly Func toRawRequest; readonly Func fromRawResponse; - IClientStreamWriter requestStream; + IClientStreamWriter? requestStream; public AsyncClientStreamingCallWrapper(AsyncClientStreamingCall inner, Func toRawRequest, Func fromRawResponse) { @@ -169,8 +169,8 @@ class AsyncDuplexStreamingCallWrapper : IAsyncDuplexStreamingCallWrapper inner; readonly Func toRawRequest; readonly Func fromRawResponse; - IClientStreamWriter requestStream; - IAsyncStreamReader responseStream; + IClientStreamWriter? requestStream; + IAsyncStreamReader? responseStream; public AsyncDuplexStreamingCallWrapper(AsyncDuplexStreamingCall inner, Func toRawRequest, Func fromRawResponse) { diff --git a/src/MagicOnion.Client/MagicOnion.Client.csproj b/src/MagicOnion.Client/MagicOnion.Client.csproj index 82e5b8e45..957e953fb 100644 --- a/src/MagicOnion.Client/MagicOnion.Client.csproj +++ b/src/MagicOnion.Client/MagicOnion.Client.csproj @@ -2,8 +2,10 @@ netstandard2.0;netstandard2.1;net6.0;net7.0 - 8.0 - + + $(_LangVersionUnityBaseline) + enable + $(DefineConstants);NON_UNITY @@ -18,6 +20,13 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/MagicOnion.Client/MagicOnionClientBase.cs b/src/MagicOnion.Client/MagicOnionClientBase.cs index bb1f7b6aa..9a370287c 100644 --- a/src/MagicOnion.Client/MagicOnionClientBase.cs +++ b/src/MagicOnion.Client/MagicOnionClientBase.cs @@ -8,12 +8,12 @@ namespace MagicOnion.Client { public readonly struct MagicOnionClientOptions { - public string Host { get; } + public string? Host { get; } public CallInvoker CallInvoker { get; } public IReadOnlyList Filters { get; } public CallOptions CallOptions { get; } - public MagicOnionClientOptions(CallInvoker callInvoker, string host, CallOptions callOptions, IReadOnlyList filters) + public MagicOnionClientOptions(CallInvoker callInvoker, string? host, CallOptions callOptions, IReadOnlyList filters) { Host = host; CallOptions = callOptions; @@ -23,7 +23,7 @@ public MagicOnionClientOptions(CallInvoker callInvoker, string host, CallOptions public MagicOnionClientOptions WithCallOptions(CallOptions callOptions) => new MagicOnionClientOptions(CallInvoker, Host, callOptions, Filters); - public MagicOnionClientOptions WithHost(string host) + public MagicOnionClientOptions WithHost(string? host) => new MagicOnionClientOptions(CallInvoker, host, CallOptions, Filters); public MagicOnionClientOptions WithFilters(IReadOnlyList filters) => new MagicOnionClientOptions(CallInvoker, Host, CallOptions, filters); @@ -70,4 +70,4 @@ public virtual T WithHost(string host) public virtual T WithOptions(CallOptions options) => (T)(object)Clone(Options.WithCallOptions(options)); } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs b/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs index 431a9b648..552103dca 100644 --- a/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs +++ b/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs @@ -1,5 +1,6 @@ using MagicOnion.Serialization; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace MagicOnion.Client @@ -33,7 +34,7 @@ public interface IMagicOnionClientFactoryProvider /// A MagicOnion service interface type. /// A MagicOnionClient factory of specified service type. /// The value indicates whether a factory was found or not. - bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService; + bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService; } public class ImmutableMagicOnionClientFactoryProvider : IMagicOnionClientFactoryProvider @@ -50,7 +51,7 @@ public ImmutableMagicOnionClientFactoryProvider Add(IMagicOnionClientFactoryProv return new ImmutableMagicOnionClientFactoryProvider(providers.Append(provider).ToArray()); } - public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService + public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService { foreach (var provider in providers) { @@ -71,7 +72,7 @@ public class DynamicNotSupportedMagicOnionClientFactoryProvider : IMagicOnionCli DynamicNotSupportedMagicOnionClientFactoryProvider() { } - public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService + public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService { throw new InvalidOperationException($"Unable to find a client factory of type '{typeof(T)}'. If the application is running on IL2CPP or AOT, dynamic code generation is not supported. Please use the code generator (moc)."); } @@ -87,7 +88,7 @@ public class DynamicMagicOnionClientFactoryProvider : IMagicOnionClientFactoryPr DynamicMagicOnionClientFactoryProvider() { } - public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) where T : IService + public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDelegate? factory) where T : IService { factory = Cache.Factory; return true; @@ -96,7 +97,7 @@ public bool TryGetFactory(out MagicOnionClientFactoryDelegate factory) whe static class Cache where T : IService { public static readonly MagicOnionClientFactoryDelegate Factory - = (clientOptions, serializerProvider) => (T)Activator.CreateInstance(DynamicClient.DynamicClientBuilder.ClientType, clientOptions, serializerProvider); + = (clientOptions, serializerProvider) => (T)Activator.CreateInstance(DynamicClient.DynamicClientBuilder.ClientType, clientOptions, serializerProvider)!; } } #endif diff --git a/src/MagicOnion.Client/RequestContext.cs b/src/MagicOnion.Client/RequestContext.cs index c051df59f..bd91649cb 100644 --- a/src/MagicOnion.Client/RequestContext.cs +++ b/src/MagicOnion.Client/RequestContext.cs @@ -18,7 +18,7 @@ public RequestContext(T request, MagicOnionClientBase client, string methodPath, public abstract class RequestContext { - Dictionary items; + Dictionary? items; public string MethodPath { get; } public CallOptions CallOptions { get; } @@ -43,4 +43,4 @@ internal RequestContext(MagicOnionClientBase client, string methodPath, CallOpti this.RequestMethod = requestMethod; } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Client/ResponseContext.cs b/src/MagicOnion.Client/ResponseContext.cs index ba55aee45..6604a50f5 100644 --- a/src/MagicOnion.Client/ResponseContext.cs +++ b/src/MagicOnion.Client/ResponseContext.cs @@ -7,26 +7,26 @@ namespace MagicOnion.Client { internal class ResponseContextRaw : ResponseContext { - readonly AsyncUnaryCall inner; + readonly AsyncUnaryCall? inner; readonly bool hasValue; - readonly T value; + readonly T? value; readonly bool hasMetadataAndStatus; readonly Status status; - readonly Metadata responseHeaders; - readonly Metadata trailers; + readonly Metadata? responseHeaders; + readonly Metadata? trailers; readonly Func fromRawResponseToResponse; public ResponseContextRaw(T value, Status status, Metadata responseHeaders, Metadata trailers) - : this(null, hasValue: true, value, hasMetadataAndStatus: true, status, responseHeaders: responseHeaders, trailers: trailers, x => (T)(object)x) + : this(null, hasValue: true, value, hasMetadataAndStatus: true, status, responseHeaders: responseHeaders, trailers: trailers, x => (T)(object)x!) { } public ResponseContextRaw(AsyncUnaryCall inner, Func fromRawResponseToResponse) : this(inner, hasValue: false, default, hasMetadataAndStatus: false, default, default, default, fromRawResponseToResponse) { } - public ResponseContextRaw(AsyncUnaryCall inner, bool hasValue, T value, bool hasMetadataAndStatus, Status status, Metadata responseHeaders, Metadata trailers, Func fromRawResponseToResponse) + public ResponseContextRaw(AsyncUnaryCall? inner, bool hasValue, T? value, bool hasMetadataAndStatus, Status status, Metadata? responseHeaders, Metadata? trailers, Func fromRawResponseToResponse) { if (!hasValue && inner == null) throw new ArgumentNullException(nameof(inner)); if (hasMetadataAndStatus && responseHeaders == null) throw new ArgumentNullException(nameof(responseHeaders)); @@ -45,6 +45,9 @@ public ResponseContextRaw(AsyncUnaryCall inner, bool hasValue, T value, bo this.fromRawResponseToResponse = fromRawResponseToResponse; } + AsyncUnaryCall GetRequiredInner() + => inner ?? throw new InvalidOperationException("ResponseContextRaw has no inner AsyncUnaryCall."); + public override Type ResponseType => typeof(T); public override async Task WaitResponseAsync() @@ -56,24 +59,24 @@ public override async Task WaitResponseAsync() public override Status GetStatus() => hasMetadataAndStatus ? status - : inner.GetStatus(); + : GetRequiredInner().GetStatus(); public override Task ResponseHeadersAsync => hasMetadataAndStatus - ? Task.FromResult(responseHeaders) - : inner.ResponseHeadersAsync; - public override Metadata GetTrailers() + ? Task.FromResult(responseHeaders!) + : GetRequiredInner().ResponseHeadersAsync; + public override Metadata GetTrailers() => hasMetadataAndStatus - ? trailers - : inner.GetTrailers(); + ? trailers! + : GetRequiredInner().GetTrailers(); public override Task ResponseAsync => hasValue - ? Task.FromResult(value) + ? Task.FromResult(value!) : FromRawResponseToResponseAsync(); async Task FromRawResponseToResponseAsync() - => fromRawResponseToResponse(await inner.ResponseAsync.ConfigureAwait(false)); + => fromRawResponseToResponse(await GetRequiredInner().ResponseAsync.ConfigureAwait(false)); public override void Dispose() => inner?.Dispose(); @@ -106,13 +109,13 @@ public abstract class ResponseContext : IResponseContext public ResponseContext As() { - return this as ResponseContext; + return (ResponseContext)this; } public Task GetResponseAs() { var t = this as ResponseContext; - if (t == null) return Task.FromResult(default(T)); + if (t == null) return Task.FromResult(default(T)!); return t.ResponseAsync; } diff --git a/src/MagicOnion.Client/StreamingHubClient.cs b/src/MagicOnion.Client/StreamingHubClient.cs index 4f1805f39..879e46285 100644 --- a/src/MagicOnion.Client/StreamingHubClient.cs +++ b/src/MagicOnion.Client/StreamingHubClient.cs @@ -10,7 +10,7 @@ namespace MagicOnion.Client public static partial class StreamingHubClient { [Obsolete("Use ConnectAsync instead.")] - public static TStreamingHub Connect(ChannelBase channel, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null) + public static TStreamingHub Connect(ChannelBase channel, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null) where TStreamingHub : IStreamingHub { var hubClient = Connect(channel.CreateCallInvoker(), receiver, host, option, serializerProvider, factoryProvider, logger); @@ -22,7 +22,7 @@ public static partial class StreamingHubClient return hubClient; } - public static async Task ConnectAsync(ChannelBase channel, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null, CancellationToken cancellationToken = default) + public static async Task ConnectAsync(ChannelBase channel, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null, CancellationToken cancellationToken = default) where TStreamingHub : IStreamingHub { var hubClient = await ConnectAsync(channel.CreateCallInvoker(), receiver, host, option, serializerProvider, factoryProvider, logger, cancellationToken); @@ -35,7 +35,7 @@ public static partial class StreamingHubClient } [Obsolete("Use ConnectAsync instead.")] - public static TStreamingHub Connect(CallInvoker callInvoker, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null) + public static TStreamingHub Connect(CallInvoker callInvoker, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null) where TStreamingHub : IStreamingHub { var client = CreateClient(callInvoker, receiver, host, option, serializerProvider, factoryProvider, logger); @@ -58,7 +58,7 @@ async void ConnectAndForget() return (TStreamingHub)(object)client; } - public static async Task ConnectAsync(CallInvoker callInvoker, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider serializerProvider = null, IStreamingHubClientFactoryProvider factoryProvider = null, IMagicOnionClientLogger logger = null, CancellationToken cancellationToken = default) + public static async Task ConnectAsync(CallInvoker callInvoker, TReceiver receiver, string? host = null, CallOptions option = default(CallOptions), IMagicOnionSerializerProvider? serializerProvider = null, IStreamingHubClientFactoryProvider? factoryProvider = null, IMagicOnionClientLogger? logger = null, CancellationToken cancellationToken = default) where TStreamingHub : IStreamingHub { var client = CreateClient(callInvoker, receiver, host, option, serializerProvider, factoryProvider, logger); @@ -66,11 +66,12 @@ async void ConnectAndForget() return (TStreamingHub)(object)client; } - static StreamingHubClientBase CreateClient(CallInvoker callInvoker, TReceiver receiver, string host, CallOptions option, IMagicOnionSerializerProvider serializerProvider, IStreamingHubClientFactoryProvider factoryProvider, IMagicOnionClientLogger logger) + static StreamingHubClientBase CreateClient(CallInvoker callInvoker, TReceiver receiver, string? host, CallOptions option, IMagicOnionSerializerProvider? serializerProvider, IStreamingHubClientFactoryProvider? factoryProvider, IMagicOnionClientLogger? logger) where TStreamingHub : IStreamingHub { - serializerProvider = serializerProvider ?? MagicOnionSerializerProvider.Default; - factoryProvider = factoryProvider ?? StreamingHubClientFactoryProvider.Default; + serializerProvider ??= MagicOnionSerializerProvider.Default; + factoryProvider ??= StreamingHubClientFactoryProvider.Default; + logger ??= NullMagicOnionClientLogger.Instance; if (!factoryProvider.TryGetFactory(out var factory)) { diff --git a/src/MagicOnion.Client/StreamingHubClientBase.cs b/src/MagicOnion.Client/StreamingHubClientBase.cs index b32564cd7..125f165a4 100644 --- a/src/MagicOnion.Client/StreamingHubClientBase.cs +++ b/src/MagicOnion.Client/StreamingHubClientBase.cs @@ -23,19 +23,20 @@ public abstract class StreamingHubClientBase const string StreamingHubVersionHeaderValue = "2"; #pragma warning restore IDE1006 // Naming Styles - readonly string host; + readonly string? host; readonly CallOptions option; readonly CallInvoker callInvoker; readonly IMagicOnionClientLogger logger; readonly IMagicOnionSerializer messageSerializer; readonly AsyncLock asyncLock = new AsyncLock(); - IClientStreamWriter writer; - IAsyncStreamReader reader; + IClientStreamWriter writer = default!; + IAsyncStreamReader reader = default!; - protected TReceiver receiver; - Task subscription; - TaskCompletionSource waitForDisconnect = new TaskCompletionSource(); + protected TReceiver receiver = default!; + Task subscription = default!; + + TaskCompletionSource waitForDisconnect = new TaskCompletionSource(); // {messageId, TaskCompletionSource} ConcurrentDictionary responseFutures = new ConcurrentDictionary(); @@ -43,7 +44,7 @@ public abstract class StreamingHubClientBase int messageId = 0; bool disposed; - protected StreamingHubClientBase(CallInvoker callInvoker, string host, CallOptions option, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) + protected StreamingHubClientBase(CallInvoker callInvoker, string? host, CallOptions option, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) { this.callInvoker = callInvoker ?? throw new ArgumentNullException(nameof(callInvoker)); this.host = host; @@ -65,7 +66,7 @@ public async Task __ConnectAndSubscribeAsync(TReceiver receiver, CancellationTok this.receiver = receiver; // Establish StreamingHub connection between the client and the server. - Metadata.Entry messageVersion = default; + Metadata.Entry? messageVersion; try { // The client can read the response headers before any StreamingHub's message. @@ -120,7 +121,7 @@ protected T Deserialize(ArraySegment bytes) protected abstract void OnResponseEvent(int methodId, object taskCompletionSource, ArraySegment data); protected abstract void OnBroadcastEvent(int methodId, ArraySegment data); - async Task StartSubscribe(SynchronizationContext syncContext, Task firstMoveNext) + async Task StartSubscribe(SynchronizationContext? syncContext, Task firstMoveNext) { var reader = this.reader; try @@ -138,7 +139,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo // log post on main thread. if (syncContext != null) { - syncContext.Post(state => logger.Error((Exception)state, msg), ex); + syncContext.Post(state => logger.Error((Exception)state!, msg), ex); } else { @@ -159,7 +160,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo // log post on main thread. if (syncContext != null) { - syncContext.Post(state => logger.Error((Exception)state, msg), ex); + syncContext.Post(state => logger.Error((Exception)state!, msg), ex); } else { @@ -182,7 +183,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo } finally { - waitForDisconnect.TrySetResult(null); + waitForDisconnect.TrySetResult(true); } } } @@ -191,7 +192,7 @@ async Task StartSubscribe(SynchronizationContext syncContext, Task firstMo // broadcast: [methodId, [argument]] // response: [messageId, methodId, response] // error-response: [messageId, statusCode, detail, StringMessage] - void ConsumeData(SynchronizationContext syncContext, byte[] data) + void ConsumeData(SynchronizationContext? syncContext, byte[] data) { var messagePackReader = new MessagePackReader(data); var arrayLength = messagePackReader.ReadArrayHeader(); @@ -228,11 +229,11 @@ void ConsumeData(SynchronizationContext syncContext, byte[] data) var ex = default(RpcException); if (string.IsNullOrWhiteSpace(error)) { - ex = new RpcException(new Status((StatusCode)statusCode, detail)); + ex = new RpcException(new Status((StatusCode)statusCode, detail ?? string.Empty)); } else { - ex = new RpcException(new Status((StatusCode)statusCode, detail), detail + Environment.NewLine + error); + ex = new RpcException(new Status((StatusCode)statusCode, detail ?? string.Empty), detail + Environment.NewLine + error); } future.TrySetException(ex); @@ -247,7 +248,7 @@ void ConsumeData(SynchronizationContext syncContext, byte[] data) var tuple = Tuple.Create(methodId, data, offset, data.Length - offset); syncContext.Post(state => { - var t = (Tuple)state; + var t = (Tuple)state!; OnBroadcastEvent(t.Item1, new ArraySegment(t.Item2, t.Item3, t.Item4)); }, tuple); } @@ -281,7 +282,7 @@ byte[] BuildMessage() await writer.WriteAsync(v).ConfigureAwait(false); } - return default; + return default!; } protected async Task WriteMessageWithResponseAsync(int methodId, TRequest message) @@ -367,7 +368,7 @@ async Task DisposeAsyncCore(bool waitSubscription) } // cleanup completion - List aggregateException = null; + List? aggregateException = null; foreach (var item in responseFutures) { try @@ -378,11 +379,8 @@ async Task DisposeAsyncCore(bool waitSubscription) { if (!(ex is OperationCanceledException)) { - if (aggregateException != null) - { - aggregateException = new List(); - aggregateException.Add(ex); - } + aggregateException ??= new List(); + aggregateException.Add(ex); } } } diff --git a/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs b/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs index c976c839c..22dd65478 100644 --- a/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs +++ b/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs @@ -1,5 +1,6 @@ using Grpc.Core; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using MagicOnion.Serialization; @@ -21,7 +22,7 @@ public static class StreamingHubClientFactoryProvider #endif } - public delegate TStreamingHub StreamingHubClientFactoryDelegate(CallInvoker callInvoker, TReceiver receiver, string host, CallOptions callOptions, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) + public delegate TStreamingHub StreamingHubClientFactoryDelegate(CallInvoker callInvoker, TReceiver receiver, string? host, CallOptions callOptions, IMagicOnionSerializerProvider serializerProvider, IMagicOnionClientLogger logger) where TStreamingHub : IStreamingHub; /// @@ -36,7 +37,7 @@ public interface IStreamingHubClientFactoryProvider /// A hub receiver interface type. /// A StreamingHubClient factory of specified service type. /// The value indicates whether a factory was found or not. - bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub; + bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub; } public class ImmutableStreamingHubClientFactoryProvider : IStreamingHubClientFactoryProvider @@ -53,7 +54,7 @@ public ImmutableStreamingHubClientFactoryProvider Add(IStreamingHubClientFactory return new ImmutableStreamingHubClientFactoryProvider(providers.Append(provider).ToArray()); } - public bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub + public bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub { foreach (var provider in providers) { @@ -74,7 +75,7 @@ public class DynamicNotSupportedStreamingHubClientFactoryProvider : IStreamingHu DynamicNotSupportedStreamingHubClientFactoryProvider() { } - public bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub + public bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub { throw new InvalidOperationException($"Unable to find a client factory of type '{typeof(TStreamingHub)}'. If the application is running on IL2CPP or AOT, dynamic code generation is not supported. Please use the code generator (moc)."); } @@ -87,7 +88,7 @@ public class DynamicStreamingHubClientFactoryProvider : IStreamingHubClientFacto DynamicStreamingHubClientFactoryProvider() { } - public bool TryGetFactory(out StreamingHubClientFactoryDelegate factory) where TStreamingHub : IStreamingHub + public bool TryGetFactory([NotNullWhen(true)] out StreamingHubClientFactoryDelegate? factory) where TStreamingHub : IStreamingHub { factory = Cache.Factory; return true; @@ -97,7 +98,7 @@ static class Cache where TStreamingHub : IStreamingHub { public static readonly StreamingHubClientFactoryDelegate Factory = (callInvoker, receiver, host, callOptions, serializerProvider, logger) - => (TStreamingHub)Activator.CreateInstance(DynamicClient.DynamicStreamingHubClientBuilder.ClientType, callInvoker, host, callOptions, serializerProvider, logger); + => (TStreamingHub)Activator.CreateInstance(DynamicClient.DynamicStreamingHubClientBuilder.ClientType, callInvoker, host, callOptions, serializerProvider, logger)!; } } #endif diff --git a/src/MagicOnion.Serialization.MemoryPack/MagicOnion.Serialization.MemoryPack.csproj b/src/MagicOnion.Serialization.MemoryPack/MagicOnion.Serialization.MemoryPack.csproj index 5d94bbcee..1a91dce6a 100644 --- a/src/MagicOnion.Serialization.MemoryPack/MagicOnion.Serialization.MemoryPack.csproj +++ b/src/MagicOnion.Serialization.MemoryPack/MagicOnion.Serialization.MemoryPack.csproj @@ -2,9 +2,9 @@ netstandard2.1;net7.0 - latest + enable - enable + enable MagicOnion.Serialization.MemoryPack diff --git a/src/MagicOnion.Serialization.MemoryPack/MemoryPackMagicOnionSerializer.cs b/src/MagicOnion.Serialization.MemoryPack/MemoryPackMagicOnionSerializer.cs index e476cb1a3..d14084703 100644 --- a/src/MagicOnion.Serialization.MemoryPack/MemoryPackMagicOnionSerializer.cs +++ b/src/MagicOnion.Serialization.MemoryPack/MemoryPackMagicOnionSerializer.cs @@ -24,7 +24,7 @@ static MemoryPackMagicOnionSerializerProvider() public MemoryPackMagicOnionSerializerProvider WithOptions(MemoryPackSerializerOptions serializerOptions) => new MemoryPackMagicOnionSerializerProvider(serializerOptions); - public IMagicOnionSerializer Create(MethodType methodType, MethodInfo methodInfo) + public IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo) { return new MagicOnionSerializer(serializerOptions); } @@ -41,8 +41,8 @@ public MagicOnionSerializer(MemoryPackSerializerOptions serializerOptions) public void Serialize(IBufferWriter writer, in T value) => MemoryPackSerializer.Serialize(writer, value, serializerOptions); - public T? Deserialize(in ReadOnlySequence bytes) - => MemoryPackSerializer.Deserialize(bytes, serializerOptions); + public T Deserialize(in ReadOnlySequence bytes) + => MemoryPackSerializer.Deserialize(bytes, serializerOptions)!; } } } diff --git a/src/MagicOnion.Server.HttpGateway/MagicOnion.Server.HttpGateway.csproj b/src/MagicOnion.Server.HttpGateway/MagicOnion.Server.HttpGateway.csproj index d9c8afa7a..2912829a0 100644 --- a/src/MagicOnion.Server.HttpGateway/MagicOnion.Server.HttpGateway.csproj +++ b/src/MagicOnion.Server.HttpGateway/MagicOnion.Server.HttpGateway.csproj @@ -2,9 +2,9 @@ net6.0;net7.0 - Library - default + enable + True 1701;1702;1705;1591 diff --git a/src/MagicOnion.Server.Redis/MagicOnion.Server.Redis.csproj b/src/MagicOnion.Server.Redis/MagicOnion.Server.Redis.csproj index 76e41c610..52d2e821b 100644 --- a/src/MagicOnion.Server.Redis/MagicOnion.Server.Redis.csproj +++ b/src/MagicOnion.Server.Redis/MagicOnion.Server.Redis.csproj @@ -2,9 +2,10 @@ net6.0;net7.0 - Library - default + enable + enable + $(NoWarn);1701;1702;1705;1591 $(DefineConstants);NON_UNITY diff --git a/src/MagicOnion.Server.Redis/NativeGuidArrayFormatter.cs b/src/MagicOnion.Server.Redis/NativeGuidArrayFormatter.cs index b3da58c8f..f00641ed9 100644 --- a/src/MagicOnion.Server.Redis/NativeGuidArrayFormatter.cs +++ b/src/MagicOnion.Server.Redis/NativeGuidArrayFormatter.cs @@ -9,7 +9,7 @@ internal static class NativeGuidArrayFormatter static readonly IMessagePackFormatter formatter = NativeGuidFormatter.Instance; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Serialize(ref MessagePackWriter writer, Guid[] value) + public static void Serialize(ref MessagePackWriter writer, Guid[]? value) { if (value == null) { @@ -20,12 +20,12 @@ public static void Serialize(ref MessagePackWriter writer, Guid[] value) writer.WriteArrayHeader(value.Length); for (int i = 0; i < value.Length; i++) { - formatter.Serialize(ref writer, value[i], null); + formatter.Serialize(ref writer, value[i], MessagePackSerializer.DefaultOptions); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid[] Deserialize(ref MessagePackReader reader) + public static Guid[]? Deserialize(ref MessagePackReader reader) { if (reader.TryReadNil()) { @@ -36,7 +36,7 @@ public static Guid[] Deserialize(ref MessagePackReader reader) var result = new Guid[len]; for (int i = 0; i < len; i++) { - result[i] = formatter.Deserialize(ref reader, null); + result[i] = formatter.Deserialize(ref reader, MessagePackSerializer.DefaultOptions); } return result; diff --git a/src/MagicOnion.Server.Redis/RedisGroup.cs b/src/MagicOnion.Server.Redis/RedisGroup.cs index a35d4f03a..3aa8ee63d 100644 --- a/src/MagicOnion.Server.Redis/RedisGroup.cs +++ b/src/MagicOnion.Server.Redis/RedisGroup.cs @@ -5,6 +5,7 @@ using MessagePack; using StackExchange.Redis; using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; namespace MagicOnion.Server.Redis; @@ -39,7 +40,7 @@ IGroup CreateGroup(string groupName) return new RedisGroup(groupName, messageSerializer, new ConcurrentDictionaryGroup(groupName, this, messageSerializer, logger), connection.GetSubscriber(), connection.GetDatabase(db)); } - public bool TryGet(string groupName, out IGroup group) + public bool TryGet(string groupName, [NotNullWhen(true)] out IGroup? group) { return dictionary.TryGetValue(groupName, out group); } @@ -120,13 +121,13 @@ static Task PublishFromRedisToMemoryGroup(RedisValue value, IGroup group) var isExcept = reader.ReadBoolean(); if (isExcept) { - var excludes = NativeGuidArrayFormatter.Deserialize(ref reader); + var excludes = NativeGuidArrayFormatter.Deserialize(ref reader) ?? Array.Empty(); var offset = (int)reader.Consumed; return group.WriteExceptRawAsync(new ArraySegment(buffer, offset, buffer.Length - offset), excludes, fireAndForget: true); } else { - var includes = NativeGuidArrayFormatter.Deserialize(ref reader); + var includes = NativeGuidArrayFormatter.Deserialize(ref reader) ?? Array.Empty(); var offset = (int)reader.Consumed; return group.WriteToRawAsync(new ArraySegment(buffer, offset, buffer.Length - offset), includes, fireAndForget: true); } @@ -152,7 +153,7 @@ public Task WriteExceptAsync(int methodId, T value, Guid connectionId, bool f return subscriber.PublishAsync(channel, BuildMessage(methodId, value, new[] { connectionId }, true), flags); } - public Task WriteExceptAsync(int methodId, T value, Guid[] connectionIds, bool fireAndForget) + public Task WriteExceptAsync(int methodId, T value, Guid[]? connectionIds, bool fireAndForget) { var flags = (fireAndForget) ? CommandFlags.FireAndForget : CommandFlags.None; return subscriber.PublishAsync(channel, BuildMessage(methodId, value, connectionIds, true), flags); @@ -164,13 +165,13 @@ public Task WriteToAsync(int methodId, T value, Guid connectionId, bool fireA return subscriber.PublishAsync(channel, BuildMessage(methodId, value, new[] { connectionId }, false), flags); } - public Task WriteToAsync(int methodId, T value, Guid[] connectionIds, bool fireAndForget) + public Task WriteToAsync(int methodId, T value, Guid[]? connectionIds, bool fireAndForget) { var flags = (fireAndForget) ? CommandFlags.FireAndForget : CommandFlags.None; return subscriber.PublishAsync(channel, BuildMessage(methodId, value, connectionIds, false), flags); } - byte[] BuildMessage(int methodId, T value, Guid[] connectionIds, bool isExcept) + byte[] BuildMessage(int methodId, T value, Guid[]? connectionIds, bool isExcept) { using (var buffer = ArrayPoolBufferWriter.RentThreadStaticWriter()) { @@ -193,13 +194,13 @@ byte[] BuildMessage(int methodId, T value, Guid[] connectionIds, bool isExcep } - public Task WriteExceptRawAsync(ArraySegment message, Guid[] exceptConnectionIds, bool fireAndForget) + public Task WriteExceptRawAsync(ArraySegment message, Guid[]? exceptConnectionIds, bool fireAndForget) { // only for the inmemory routing. throw new NotSupportedException(); } - public Task WriteToRawAsync(ArraySegment message, Guid[] connectionIds, bool fireAndForget) + public Task WriteToRawAsync(ArraySegment message, Guid[]? connectionIds, bool fireAndForget) { // only for the inmemory routing. throw new NotSupportedException(); diff --git a/src/MagicOnion.Server.Redis/RedisGroupOptions.cs b/src/MagicOnion.Server.Redis/RedisGroupOptions.cs index e6a186fbc..6aafb91ce 100644 --- a/src/MagicOnion.Server.Redis/RedisGroupOptions.cs +++ b/src/MagicOnion.Server.Redis/RedisGroupOptions.cs @@ -4,6 +4,6 @@ namespace MagicOnion.Server.Redis; public class RedisGroupOptions { - public ConnectionMultiplexer ConnectionMultiplexer { get; set; } + public ConnectionMultiplexer? ConnectionMultiplexer { get; set; } public int Db { get; set; } = -1; } diff --git a/src/MagicOnion.Server/Hubs/Group.cs b/src/MagicOnion.Server/Hubs/Group.cs index aa1434c30..f571ff821 100644 --- a/src/MagicOnion.Server/Hubs/Group.cs +++ b/src/MagicOnion.Server/Hubs/Group.cs @@ -197,7 +197,7 @@ public void Set(Guid id, T value) } [return: MaybeNull] - public T Get(Guid id) + public T? Get(Guid id) { return storage.TryGetValue(id, out var value) ? value diff --git a/src/MagicOnion.Server/MagicOnion.Server.csproj b/src/MagicOnion.Server/MagicOnion.Server.csproj index 8db7f2bc2..50f33a00e 100644 --- a/src/MagicOnion.Server/MagicOnion.Server.csproj +++ b/src/MagicOnion.Server/MagicOnion.Server.csproj @@ -4,9 +4,9 @@ net6.0;net7.0 enable - enable + enable + $(DefineConstants);NON_UNITY - default MagicOnion.Server diff --git a/src/MagicOnion.Shared/AsyncLock.cs b/src/MagicOnion.Shared/AsyncLock.cs index 67aae46b5..4ea9be5fa 100644 --- a/src/MagicOnion.Shared/AsyncLock.cs +++ b/src/MagicOnion.Shared/AsyncLock.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; @@ -29,9 +29,9 @@ public async Task LockAsync() public struct LockReleaser : IDisposable { - readonly SemaphoreSlim semaphore; + readonly SemaphoreSlim? semaphore; - public LockReleaser(SemaphoreSlim semaphore) + public LockReleaser(SemaphoreSlim? semaphore) { this.semaphore = semaphore; } diff --git a/src/MagicOnion.Shared/BroadcasterHelper.cs b/src/MagicOnion.Shared/BroadcasterHelper.cs index f82c3839b..4b4813a5c 100644 --- a/src/MagicOnion.Shared/BroadcasterHelper.cs +++ b/src/MagicOnion.Shared/BroadcasterHelper.cs @@ -40,11 +40,7 @@ internal static MethodDefinition[] SearchDefinitions(Type interfaceType) return true; }) .Where(x => !x.IsSpecialName) - .Select(x => new MethodDefinition - { - ReceiverType = interfaceType, - MethodInfo = x, - }) + .Select(x => new MethodDefinition(interfaceType, x, default)) .ToArray(); } @@ -74,9 +70,16 @@ internal class MethodDefinition { public string Path => ReceiverType.Name + "/" + MethodInfo.Name; - public Type ReceiverType; - public MethodInfo MethodInfo; - public int MethodId; + public Type ReceiverType { get; set; } + public MethodInfo MethodInfo { get; set; } + public int MethodId { get; set; } + + public MethodDefinition(Type receiverType, MethodInfo methodInfo, int methodId) + { + ReceiverType = receiverType; + MethodInfo = methodInfo; + MethodId = methodId; + } } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Shared/GrpcMethodHelper.cs b/src/MagicOnion.Shared/GrpcMethodHelper.cs index 7ac93817c..3654c83d1 100644 --- a/src/MagicOnion.Shared/GrpcMethodHelper.cs +++ b/src/MagicOnion.Shared/GrpcMethodHelper.cs @@ -40,11 +40,11 @@ static T FromRaw(TRaw obj) { if (typeof(TRaw) == typeof(Box)) { - return ((Box)(object)obj).Value; + return ((Box)(object)obj!).Value; } else { - return DangerousDummyNull.GetObjectOrDefault(obj); + return DangerousDummyNull.GetObjectOrDefault(obj!); } } } @@ -53,7 +53,7 @@ public static MagicOnionMethod, TRawResponse> CreateMet { return CreateMethod(methodType, serviceName, name, null, messageSerializer); } - public static MagicOnionMethod, TRawResponse> CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo methodInfo, IMagicOnionSerializer messageSerializer) + public static MagicOnionMethod, TRawResponse> CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo? methodInfo, IMagicOnionSerializer messageSerializer) { // WORKAROUND: Prior to MagicOnion 5.0, the request type for the parameter-less method was byte[]. // DynamicClient sends byte[], but GeneratedClient sends Nil, which is incompatible, @@ -76,7 +76,7 @@ public static MagicOnionMethod C { return CreateMethod(methodType, serviceName, name, null, messageSerializer); } - public static MagicOnionMethod CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo methodInfo, IMagicOnionSerializer messageSerializer) + public static MagicOnionMethod CreateMethod(MethodType methodType, string serviceName, string name, MethodInfo? methodInfo, IMagicOnionSerializer messageSerializer) { var isMethodRequestTypeBoxed = typeof(TRequest).IsValueType; var isMethodResponseTypeBoxed = typeof(TResponse).IsValueType; @@ -120,7 +120,9 @@ static Marshaller CreateMarshaller(IMagicOnionSerializer messageSerializer return new Marshaller( serializer: (obj, ctx) => { +#pragma warning disable CS8602 if (obj.GetType() == typeof(RawBytesBox)) +#pragma warning restore CS8602 { var rawBytesBox = (RawBytesBox)(object)obj; var writer = ctx.GetBufferWriter(); @@ -134,7 +136,7 @@ static Marshaller CreateMarshaller(IMagicOnionSerializer messageSerializer } ctx.Complete(); }, - deserializer: (ctx) => DangerousDummyNull.GetObjectOrDummyNull(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence()))); + deserializer: (ctx) => DangerousDummyNull.GetObjectOrDummyNull(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence()))!); } static Marshaller> CreateBoxedMarshaller(IMagicOnionSerializer messageSerializer) @@ -142,7 +144,9 @@ static Marshaller> CreateBoxedMarshaller(IMagicOnionSerializer message return new Marshaller>( serializer: (obj, ctx) => { +#pragma warning disable CS8602 if (obj.GetType() == typeof(RawBytesBox)) +#pragma warning restore CS8602 { var rawBytesBox = (RawBytesBox)(object)obj; var writer = ctx.GetBufferWriter(); @@ -156,7 +160,7 @@ static Marshaller> CreateBoxedMarshaller(IMagicOnionSerializer message } ctx.Complete(); }, - deserializer: (ctx) => Box.Create(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence())) + deserializer: (ctx) => Box.Create(messageSerializer.Deserialize(ctx.PayloadAsReadOnlySequence())!) ); } diff --git a/src/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs b/src/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs index 1b8006644..ba6ef5531 100644 --- a/src/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs +++ b/src/MagicOnion.Shared/Internal/MagicOnionClientStreamWriter.cs @@ -18,7 +18,7 @@ public MagicOnionClientStreamWriter(IClientStreamWriter inner, Func inner.WriteAsync(toRawMessage(message)); - public WriteOptions WriteOptions + public WriteOptions? WriteOptions { get => inner.WriteOptions; set => inner.WriteOptions = value; diff --git a/src/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs b/src/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs index a47bd9c3b..56b42ccde 100644 --- a/src/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs +++ b/src/MagicOnion.Shared/Internal/MagicOnionServerStreamWriter.cs @@ -18,7 +18,7 @@ public MagicOnionServerStreamWriter(IServerStreamWriter inner, Func inner.WriteAsync(toRawMessage(message)); - public WriteOptions WriteOptions + public WriteOptions? WriteOptions { get => inner.WriteOptions; set => inner.WriteOptions = value; diff --git a/src/MagicOnion.Shared/MagicOnion.Shared.csproj b/src/MagicOnion.Shared/MagicOnion.Shared.csproj index 737786875..ec170d72a 100644 --- a/src/MagicOnion.Shared/MagicOnion.Shared.csproj +++ b/src/MagicOnion.Shared/MagicOnion.Shared.csproj @@ -1,19 +1,27 @@ - netstandard2.0 - + netstandard2.0;netstandard2.1;net6.0 + + $(_LangVersionUnityBaseline) + enable $(DefineConstants);NON_UNITY MagicOnion Provides shared classes and interfaces used by MagicOnion server and client. $(MagicOnionPackageDescription) - 8.0 + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/MagicOnion.Shared/MagicOnionMarshallers.cs b/src/MagicOnion.Shared/MagicOnionMarshallers.cs index c557fbac1..178445667 100644 --- a/src/MagicOnion.Shared/MagicOnionMarshallers.cs +++ b/src/MagicOnion.Shared/MagicOnionMarshallers.cs @@ -39,7 +39,7 @@ internal static Type CreateRequestType(ParameterInfo[] parameters) } else if (parameters.Length >= 16) { - throw new InvalidOperationException($"The method '{parameters[0].Member.DeclaringType.FullName}.{parameters[0].Member.Name}' must have less than 16 parameters. (Length: {parameters.Length})"); + throw new InvalidOperationException($"The method '{parameters[0].Member.DeclaringType!.FullName}.{parameters[0].Member.Name}' must have less than 16 parameters. (Length: {parameters.Length})"); } else { @@ -54,7 +54,7 @@ public static object InstantiateDynamicArgumentTuple(Type[] typeParameters, obje { // start from T2 var tupleTypeBase = dynamicArgumentTupleTypes[arguments.Length - 2]; - return Activator.CreateInstance(tupleTypeBase.MakeGenericType(typeParameters), arguments); + return Activator.CreateInstance(tupleTypeBase.MakeGenericType(typeParameters), arguments)!; } } diff --git a/src/MagicOnion.Shared/MetadataExtensions.cs b/src/MagicOnion.Shared/MetadataExtensions.cs index 40373dc55..b56870da4 100644 --- a/src/MagicOnion.Shared/MetadataExtensions.cs +++ b/src/MagicOnion.Shared/MetadataExtensions.cs @@ -1,4 +1,4 @@ -using Grpc.Core; +using Grpc.Core; using System; namespace MagicOnion @@ -10,7 +10,7 @@ public static class MetadataExtensions /// /// Get metdata entry. If does not exists, return null. /// - public static Metadata.Entry Get(this Metadata metadata, string key, bool ignoreCase = true) + public static Metadata.Entry? GetOrDefault(this Metadata metadata, string key, bool ignoreCase = true) { for (int i = 0; i < metadata.Count; i++) { @@ -36,7 +36,7 @@ public static Metadata.Entry Get(this Metadata metadata, string key, bool ignore /// /// Get metdata string value. If does not exists, return null. /// - public static string GetValue(this Metadata metadata, string key, bool ignoreCase = true) + public static string? GetValueOrDefault(this Metadata metadata, string key, bool ignoreCase = true) { for (int i = 0; i < metadata.Count; i++) { diff --git a/src/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs b/src/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs index 53318ea93..50f992aa5 100644 --- a/src/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs +++ b/src/MagicOnion.Shared/Serialization/MagicOnionSerializer.cs @@ -15,11 +15,7 @@ public interface IMagicOnionSerializerProvider /// gRPC method type of the method. /// A method info for an implementation of the service method. It is a hint that handling request parameters on the server, which may be passed null on the client. /// -#if NET5_0_OR_GREATER IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo); -#else - IMagicOnionSerializer Create(MethodType methodType, MethodInfo methodInfo); -#endif } /// @@ -27,13 +23,8 @@ public interface IMagicOnionSerializerProvider /// public interface IMagicOnionSerializer { -#if NET5_0_OR_GREATER - void Serialize(IBufferWriter writer, in T? value); - T? Deserialize(in ReadOnlySequence bytes); -#else void Serialize(IBufferWriter writer, in T value); T Deserialize(in ReadOnlySequence bytes); -#endif } /// diff --git a/src/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs b/src/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs index a6633affb..8da58b1c3 100644 --- a/src/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs +++ b/src/MagicOnion.Shared/Serialization/MessagePackMagicOnionSerializerProvider.cs @@ -42,11 +42,7 @@ public MessagePackMagicOnionSerializerProvider WithEnableFallback(bool enableFal return new MessagePackMagicOnionSerializerProvider(SerializerOptions, enableFallback); } -#if NET5_0_OR_GREATER public IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo) -#else - public IMagicOnionSerializer Create(MethodType methodType, MethodInfo methodInfo) -#endif { var serializerOptions = EnableFallback && methodInfo != null ? WrapFallbackResolverIfNeeded(methodInfo.GetParameters()) : SerializerOptions; return new MessagePackMagicOnionSerializer(serializerOptions); @@ -96,7 +92,7 @@ MessagePackSerializerOptions WrapFallbackResolverIfNeeded(ParameterInfo[] parame } }).ToArray(); - var formatter = Activator.CreateInstance(formatterType, defaultValues); + var formatter = Activator.CreateInstance(formatterType, defaultValues)!; return SerializerOptions.WithResolver(new PriorityResolver(t, formatter, SerializerOptions.Resolver)); } @@ -111,7 +107,7 @@ public MessagePackMagicOnionSerializer(MessagePackSerializerOptions serializerOp public T Deserialize(in ReadOnlySequence bytes) => MessagePackSerializer.Deserialize(bytes, serializerOptions); - public void Serialize(IBufferWriter writer, in T value) + public void Serialize(IBufferWriter writer, in T? value) => MessagePackSerializer.Serialize(writer, value, serializerOptions); } diff --git a/src/MagicOnion.Shared/UnsafeDirectBlitResolver.cs b/src/MagicOnion.Shared/UnsafeDirectBlitResolver.cs index 64fcf8af1..868fc3d0b 100644 --- a/src/MagicOnion.Shared/UnsafeDirectBlitResolver.cs +++ b/src/MagicOnion.Shared/UnsafeDirectBlitResolver.cs @@ -31,14 +31,14 @@ public void Register() formatters.Add(typeof(T[]), new UnsafeDirectBlitArrayFormatter()); } - public IMessagePackFormatter GetFormatter() + public IMessagePackFormatter? GetFormatter() { return FormatterCache.formatter; } static class FormatterCache { - public static readonly IMessagePackFormatter formatter; + public static readonly IMessagePackFormatter? formatter; static FormatterCache() { @@ -46,7 +46,7 @@ static FormatterCache() var t = typeof(T); - object formatterObject; + object? formatterObject; if (Instance.formatters.TryGetValue(t, out formatterObject)) { formatter = (IMessagePackFormatter)formatterObject; @@ -55,7 +55,7 @@ static FormatterCache() } } - public class UnsafeDirectBlitArrayFormatter : IMessagePackFormatter + public class UnsafeDirectBlitArrayFormatter : IMessagePackFormatter where T : unmanaged { const int TypeCode = 45; @@ -70,7 +70,7 @@ public UnsafeDirectBlitArrayFormatter() this.StructLength = Unsafe.SizeOf(); } - public void Serialize(ref MessagePackWriter writer, T[] value, MessagePackSerializerOptions options) + public void Serialize(ref MessagePackWriter writer, T[]? value, MessagePackSerializerOptions options) { if (value == null) { @@ -101,7 +101,7 @@ public void Serialize(ref MessagePackWriter writer, T[] value, MessagePackSerial } } - public T[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + public T[]? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { if (reader.IsNil) { @@ -115,7 +115,7 @@ public T[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOption var isLittleEndian = extReader.ReadBoolean(); // extReader.read - byte[] rentMemory = default; + byte[]? rentMemory = default; ReadOnlySpan span = default; try { diff --git a/src/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs b/src/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs index acf6209c9..0978ea830 100644 --- a/src/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs +++ b/src/MagicOnion.Shared/Utils/ArrayPoolBufferWriter.cs @@ -1,12 +1,13 @@ -using System; +using System; using System.Buffers; +using System.Diagnostics.CodeAnalysis; namespace MagicOnion.Utils { internal sealed class ArrayPoolBufferWriter : IBufferWriter, IDisposable { [ThreadStatic] - static ArrayPoolBufferWriter staticInstance; + static ArrayPoolBufferWriter? staticInstance; public static ArrayPoolBufferWriter RentThreadStaticWriter() { @@ -20,7 +21,7 @@ public static ArrayPoolBufferWriter RentThreadStaticWriter() const int MinimumBufferSize = 32767; // use 32k buffer. - byte[] buffer; + byte[]? buffer; int index; void Prepare() @@ -37,9 +38,9 @@ void Prepare() public int WrittenCount => index; - public int Capacity => buffer.Length; + public int Capacity => buffer?.Length ?? throw new ObjectDisposedException(nameof(ArrayPoolBufferWriter)); - public int FreeCapacity => buffer.Length - index; + public int FreeCapacity => Capacity - index; public void Advance(int count) { @@ -61,6 +62,7 @@ public Span GetSpan(int sizeHint = 0) void CheckAndResizeBuffer(int sizeHint) { + if (buffer == null) throw new ObjectDisposedException(nameof(ArrayPoolBufferWriter)); if (sizeHint < 0) throw new ArgumentException(nameof(sizeHint)); if (sizeHint == 0) @@ -97,4 +99,4 @@ public void Dispose() buffer = null; } } -} \ No newline at end of file +} diff --git a/src/MagicOnion.Shared/Utils/DynamicAssembly.cs b/src/MagicOnion.Shared/Utils/DynamicAssembly.cs index 8e436b2f8..057a520e8 100644 --- a/src/MagicOnion.Shared/Utils/DynamicAssembly.cs +++ b/src/MagicOnion.Shared/Utils/DynamicAssembly.cs @@ -38,8 +38,8 @@ public DynamicAssembly(string moduleName) // HACK: Allow access to `internal` classes from dynamically generated assembly. // https://www.strathweb.com/2018/10/no-internalvisibleto-no-problem-bypassing-c-visibility-rules-with-roslyn/ - this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) }), new[] { "MagicOnion.Client" })); - this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) }), new[] { "MagicOnion.Server" })); + this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) })!, new[] { "MagicOnion.Client" })); + this.assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] { typeof(string) })!, new[] { "MagicOnion.Server" })); } // requires lock on mono environment(for example, UnityEditor). see: https://github.com/neuecc/MessagePack-CSharp/issues/161 diff --git a/src/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs b/src/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs index fc08c7f87..6430c4195 100644 --- a/src/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs +++ b/src/MagicOnion.Shared/Utils/ILGeneratorExtensions.cs @@ -1,4 +1,4 @@ -#if NON_UNITY || !NET_STANDARD_2_0 +#if NON_UNITY || !NET_STANDARD_2_0 using System; using System.Reflection; @@ -161,10 +161,10 @@ public static void EmitNullReturn(this ILGenerator il) public static void EmitThrowNotimplemented(this ILGenerator il) { - il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetConstructor(Type.EmptyTypes)); + il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetConstructor(Type.EmptyTypes)!); il.Emit(OpCodes.Throw); } } } -#endif \ No newline at end of file +#endif diff --git a/src/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs b/src/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs index af8ecfb11..1e100ebdd 100644 --- a/src/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs +++ b/src/MagicOnion.Shared/Utils/ReservedWhenAllPromise.cs @@ -13,8 +13,8 @@ public class ReservedWhenAllPromise : IValueTaskSource { static class ContinuationSentinel { - public static readonly Action AvailableContinuation = _ => { }; - public static readonly Action CompletedContinuation = _ => { }; + public static readonly Action AvailableContinuation = _ => { }; + public static readonly Action CompletedContinuation = _ => { }; } static readonly ContextCallback execContextCallback = ExecutionContextCallback; @@ -23,11 +23,11 @@ static class ContinuationSentinel int completedCount; readonly int resultCount; - ExceptionDispatchInfo exception; - Action continuation = ContinuationSentinel.AvailableContinuation; - object state; - SynchronizationContext syncContext; - ExecutionContext execContext; + ExceptionDispatchInfo? exception; + Action continuation = ContinuationSentinel.AvailableContinuation; + object? state; + SynchronizationContext? syncContext; + ExecutionContext? execContext; public ReservedWhenAllPromise(int reserveCount) { @@ -127,7 +127,7 @@ public ValueTaskSourceStatus GetStatus(short token) : ValueTaskSourceStatus.Pending; } - public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) + public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { if (Interlocked.CompareExchange(ref this.continuation, continuation, ContinuationSentinel.AvailableContinuation) != ContinuationSentinel.AvailableContinuation) { @@ -155,9 +155,11 @@ public void OnCompleted(Action continuation, object state, short token, } } - static void ExecutionContextCallback(object state) + static void ExecutionContextCallback(object? state) { - var t = (Tuple, ReservedWhenAllPromise>)state; + if (state is null) throw new ArgumentNullException(nameof(state)); + + var t = (Tuple, ReservedWhenAllPromise>)state; var self = t.Item2; if (self.syncContext != null) { @@ -171,9 +173,11 @@ static void ExecutionContextCallback(object state) } } - static void SynchronizationContextCallback(object state) + static void SynchronizationContextCallback(object? state) { - var t = (Tuple, ReservedWhenAllPromise>)state; + if (state is null) throw new ArgumentNullException(nameof(state)); + + var t = (Tuple, ReservedWhenAllPromise>)state; var self = t.Item2; var invokeState = self.state; self.state = null; @@ -182,4 +186,4 @@ static void SynchronizationContextCallback(object state) } } -#endif \ No newline at end of file +#endif diff --git a/src/MagicOnion.Shared/Utils/UniqueHashDictionary.cs b/src/MagicOnion.Shared/Utils/UniqueHashDictionary.cs index 4dcbcac17..16fb9a4dc 100644 --- a/src/MagicOnion.Shared/Utils/UniqueHashDictionary.cs +++ b/src/MagicOnion.Shared/Utils/UniqueHashDictionary.cs @@ -1,7 +1,8 @@ -#if NON_UNITY +#if NON_UNITY using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace MagicOnion.Utils @@ -31,7 +32,9 @@ public class UniqueHashDictionary public int Count => count; public int MaxConflict => maxConflict; +#pragma warning disable CS8618 // NOTE: .NET Standard 2.1 doesn't have MemberNotNull. PolySharp is only enabled when targeting .NET Standard 2.0. public UniqueHashDictionary(params (int hashCode, T value)[] values) +#pragma warning restore CS8618 { CreateTable(values); } @@ -78,7 +81,7 @@ void CreateTable((int hashCode, T value)[] values) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetValue(int hashCode, out T value) + public bool TryGetValue(int hashCode, [NotNullWhen(true)] out T? value) { ref var array = ref table[hashCode & indexFor]; if (array == null) @@ -90,7 +93,7 @@ public bool TryGetValue(int hashCode, out T value) ref var v = ref array[0]; if (v.hash == hashCode) { - value = v.value; + value = v.value!; return true; } @@ -98,14 +101,14 @@ public bool TryGetValue(int hashCode, out T value) } [MethodImpl(MethodImplOptions.NoInlining)] - bool TryGetValueSlow(ref (int, T)[] array, int hashCode, out T value) + bool TryGetValueSlow(ref (int, T)[] array, int hashCode, [NotNullWhen(true)] out T? value) { for (int i = 1; i < array.Length; i++) // 0 is already checked. { ref var v = ref array[i]; if (v.Item1 == hashCode) { - value = v.Item2; + value = v.Item2!; return true; } } @@ -155,4 +158,4 @@ public override string ToString() } } -#endif \ No newline at end of file +#endif diff --git a/tests/MagicOnion.Client.Tests/ClientFilterTest.cs b/tests/MagicOnion.Client.Tests/ClientFilterTest.cs index 517075445..db3bc815c 100644 --- a/tests/MagicOnion.Client.Tests/ClientFilterTest.cs +++ b/tests/MagicOnion.Client.Tests/ClientFilterTest.cs @@ -8,7 +8,7 @@ public async Task ReturnImmediateValue() // Arrange var clientFilterMock = Substitute.For(); clientFilterMock - .SendAsync(default, default) + .SendAsync(default!, default!) // NOTE: Mock IClientFilter returns a value immediately. (The filter will not call `next`) .ReturnsForAnyArgs(ValueTask.FromResult((ResponseContext)ResponseContext.Create("Response", Status.DefaultSuccess, Metadata.Empty, Metadata.Empty))); var callInvokerMock = Substitute.For(); @@ -82,7 +82,7 @@ public async Task FilterChain() // Arrange var calledFilters = new List(); var clientFilterMockFirst = Substitute.For(); - clientFilterMockFirst.SendAsync(default, default) + clientFilterMockFirst.SendAsync(default!, default!) .ReturnsForAnyArgs(x => { var context = x.Arg(); @@ -92,7 +92,7 @@ public async Task FilterChain() }); var clientFilterMockSecond = Substitute.For(); - clientFilterMockSecond.SendAsync(default, default) + clientFilterMockSecond.SendAsync(default!, default!) .ReturnsForAnyArgs(x => { var context = x.Arg(); diff --git a/tests/MagicOnion.Client.Tests/DynamicClient/SameInterfaceNameTest.cs b/tests/MagicOnion.Client.Tests/DynamicClient/SameInterfaceNameTest.cs index 0d11f5572..a035e58d3 100644 --- a/tests/MagicOnion.Client.Tests/DynamicClient/SameInterfaceNameTest.cs +++ b/tests/MagicOnion.Client.Tests/DynamicClient/SameInterfaceNameTest.cs @@ -20,9 +20,11 @@ public void DynamicStreamingHubClientFactoryProvider_TryGetFactory() var receiverB = Substitute.For(); DynamicStreamingHubClientFactoryProvider.Instance.TryGetFactory(out var factoryA); + Assert.NotNull(factoryA); var clientA = factoryA(callInvoker, receiverA, "", default, MagicOnionSerializerProvider.Default, NullMagicOnionClientLogger.Instance); DynamicStreamingHubClientFactoryProvider.Instance.TryGetFactory(out var factoryB); + Assert.NotNull(factoryB); var clientB = factoryB(callInvoker, receiverB, "", default, MagicOnionSerializerProvider.Default, NullMagicOnionClientLogger.Instance); } } diff --git a/tests/MagicOnion.Client.Tests/DynamicClient/ServiceClientDefinitionTest.cs b/tests/MagicOnion.Client.Tests/DynamicClient/ServiceClientDefinitionTest.cs index 4a41d7a34..a581e8915 100644 --- a/tests/MagicOnion.Client.Tests/DynamicClient/ServiceClientDefinitionTest.cs +++ b/tests/MagicOnion.Client.Tests/DynamicClient/ServiceClientDefinitionTest.cs @@ -22,7 +22,7 @@ public interface IDummyService [Fact] public void Unary() { - var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.Unary))); + var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.Unary))!); methodInfo.Should().NotBeNull(); methodInfo.MethodName.Should().Be(nameof(IDummyService.Unary)); methodInfo.MethodReturnType.Should().Be>(); @@ -32,7 +32,7 @@ public void Unary() [Fact] public void Unary_NonGeneric() { - var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.UnaryNonGeneric))); + var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.UnaryNonGeneric))!); methodInfo.Should().NotBeNull(); methodInfo.MethodName.Should().Be(nameof(IDummyService.UnaryNonGeneric)); methodInfo.MethodReturnType.Should().Be(); @@ -42,7 +42,7 @@ public void Unary_NonGeneric() [Fact] public void ClientStreaming() { - var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.ClientStreaming))); + var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.ClientStreaming))!); methodInfo.Should().NotBeNull(); methodInfo.MethodName.Should().Be(nameof(IDummyService.ClientStreaming)); methodInfo.MethodReturnType.Should().Be>>(); @@ -51,7 +51,7 @@ public void ClientStreaming() [Fact] public void ServerStreaming() { - var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.ServerStreaming))); + var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.ServerStreaming))!); methodInfo.Should().NotBeNull(); methodInfo.MethodName.Should().Be(nameof(IDummyService.ServerStreaming)); methodInfo.MethodReturnType.Should().Be>>(); @@ -60,7 +60,7 @@ public void ServerStreaming() [Fact] public void DuplexStreaming() { - var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.DuplexStreaming))); + var methodInfo = ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.DuplexStreaming))!); methodInfo.Should().NotBeNull(); methodInfo.MethodName.Should().Be(nameof(IDummyService.DuplexStreaming)); methodInfo.MethodReturnType.Should().Be>>(); @@ -70,7 +70,7 @@ public void InvalidUnaryTaskOfUnary() { var ex = Assert.Throws(() => { - ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.TaskOfUnary))); + ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.TaskOfUnary))!); }); ex.Message.Should().Contain("The return type of an Unary method must be 'UnaryResult' or 'UnaryResult'"); @@ -81,7 +81,7 @@ public void InvalidNonTaskOfClientStreamingResult() { var ex = Assert.Throws(() => { - ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.NonTaskOfClientStreamingResult))); + ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.NonTaskOfClientStreamingResult))!); }); ex.Message.Should().Contain("The return type of a Streaming method must be 'Task<"); @@ -92,7 +92,7 @@ public void InvalidNonTaskOfServerStreamingResult() { var ex = Assert.Throws(() => { - ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.NonTaskOfServerStreamingResult))); + ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.NonTaskOfServerStreamingResult))!); }); ex.Message.Should().Contain("The return type of a Streaming method must be 'Task<"); @@ -103,7 +103,7 @@ public void InvalidNonTaskOfDuplexStreamingResult() { var ex = Assert.Throws(() => { - ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.NonTaskOfDuplexStreamingResult))); + ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.NonTaskOfDuplexStreamingResult))!); }); ex.Message.Should().Contain("The return type of a Streaming method must be 'Task<"); @@ -114,7 +114,7 @@ public void InvalidUnknown() { var ex = Assert.Throws(() => { - ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.Unknown))); + ServiceClientDefinition.MagicOnionServiceMethodInfo.Create(typeof(IDummyService), typeof(IDummyService).GetMethod(nameof(IDummyService.Unknown))!); }); ex.Message.Should().Contain("The method of a service must return 'UnaryResult', 'Task>'"); diff --git a/tests/MagicOnion.Integration.Tests/XorMessagePackMagicOnionSerializer.cs b/tests/MagicOnion.Integration.Tests/XorMessagePackMagicOnionSerializer.cs index e995bf7df..ab971892f 100644 --- a/tests/MagicOnion.Integration.Tests/XorMessagePackMagicOnionSerializer.cs +++ b/tests/MagicOnion.Integration.Tests/XorMessagePackMagicOnionSerializer.cs @@ -55,7 +55,7 @@ public void Serialize(IBufferWriter writer, in T value) } } - public IMagicOnionSerializer Create(MethodType methodType, MethodInfo methodInfo) + public IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo) { return new XorMessagePackMagicOnionSerializer(serializerOptions); } diff --git a/tests/MagicOnion.Shared.Tests/BoxTest.cs b/tests/MagicOnion.Shared.Tests/BoxTest.cs index 6e4f3ccfc..ef691a369 100644 --- a/tests/MagicOnion.Shared.Tests/BoxTest.cs +++ b/tests/MagicOnion.Shared.Tests/BoxTest.cs @@ -19,12 +19,11 @@ public void Equality() box2.Equals(box2).Should().BeTrue(); box2.Equals(box2a).Should().BeTrue(); - box1.Equals(null).Should().BeFalse(); - box2.Equals(null).Should().BeFalse(); + box1.Equals(null!).Should().BeFalse(); + box2.Equals(null!).Should().BeFalse(); - (default(Box) == default(Box)).Should().BeTrue(); - (default(Box) == box1).Should().BeFalse(); - (box1 == default(Box)).Should().BeFalse(); + (default(Box)! == box1!).Should().BeFalse(); + (box1! == default(Box)!).Should().BeFalse(); } [Fact] diff --git a/tests/MagicOnion.Shared.Tests/UnaryResultNonGenericTest.cs b/tests/MagicOnion.Shared.Tests/UnaryResultNonGenericTest.cs index 1b79ca64f..c9b1011d3 100644 --- a/tests/MagicOnion.Shared.Tests/UnaryResultNonGenericTest.cs +++ b/tests/MagicOnion.Shared.Tests/UnaryResultNonGenericTest.cs @@ -31,7 +31,9 @@ public void Ctor_RawTask_Null() // Arrange var value = default(Task); // Act +#pragma warning disable CS8604 var result = Record.Exception(() => new UnaryResult(value)); +#pragma warning restore CS8604 // Assert result.Should().BeOfType(); } @@ -49,7 +51,9 @@ public void Ctor_TaskOfResponseContext_Null() // Arrange var value = default(Task>); // Act +#pragma warning disable CS8604 var result = Record.Exception(() => new UnaryResult(value)); +#pragma warning restore CS8604 // Assert result.Should().BeOfType(); } diff --git a/tests/MagicOnion.Shared.Tests/UnaryResultTest.cs b/tests/MagicOnion.Shared.Tests/UnaryResultTest.cs index bb0c3e177..ef391aeac 100644 --- a/tests/MagicOnion.Shared.Tests/UnaryResultTest.cs +++ b/tests/MagicOnion.Shared.Tests/UnaryResultTest.cs @@ -11,7 +11,7 @@ public async Task FromResult() (await UnaryResult.FromResult("foo")).Should().Be("foo"); (await UnaryResult.FromResult(default(string))).Should().BeNull(); - Assert.Throws(() => UnaryResult.FromResult(default(Task))); + Assert.Throws(() => UnaryResult.FromResult(default(Task)!)); } [Fact] @@ -36,7 +36,7 @@ public async Task Ctor_RawTask() var result2 = new UnaryResult(Task.FromResult("foo")); (await result2).Should().Be("foo"); - Assert.Throws(() => new UnaryResult(default(Task))); + Assert.Throws(() => new UnaryResult(default(Task)!)); } [Fact] @@ -48,7 +48,7 @@ public async Task Ctor_TaskOfResponseContext() var result2 = new UnaryResult(Task.FromResult(DummyResponseContext.Create("foo"))); (await result2).Should().Be("foo"); - Assert.Throws(() => new UnaryResult(default(Task>))); + Assert.Throws(() => new UnaryResult(default(Task>)!)); } static class DummyResponseContext From bd2ba9f275531e1175b7dad531b135cfbd3a5a07 Mon Sep 17 00:00:00 2001 From: Mayuki Sawatari Date: Thu, 26 Oct 2023 10:16:03 +0900 Subject: [PATCH 2/3] Add test cases --- .../GenerateNullableTest.cs | 48 ++++++++++ ...cOnion.Client.SourceGenerator.Tests.csproj | 6 +- ...cOnionClientSourceGeneratorAttributes.g.cs | 42 ++++++++ ...1_MyApplication1_GreeterServiceClient.g.cs | 50 ++++++++++ ...ation1_MagicOnionInitializer_Resolver.g.cs | 80 ++++++++++++++++ ..._MyApplication1_MagicOnionInitializer.g.cs | 95 +++++++++++++++++++ ...cOnionClientSourceGeneratorAttributes.g.cs | 42 ++++++++ ...1_MyApplication1_GreeterServiceClient.g.cs | 50 ++++++++++ ...ation1_MagicOnionInitializer_Resolver.g.cs | 83 ++++++++++++++++ ..._MyApplication1_MagicOnionInitializer.g.cs | 95 +++++++++++++++++++ .../MagicOnionSourceGeneratorVerifier.cs | 1 + 11 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/GenerateNullableTest.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0001_MyApplication1_GreeterServiceClient.g.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0003_MyApplication1_MagicOnionInitializer.g.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0001_MyApplication1_GreeterServiceClient.g.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs create mode 100644 tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0003_MyApplication1_MagicOnionInitializer.g.cs diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/GenerateNullableTest.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/GenerateNullableTest.cs new file mode 100644 index 000000000..e83c573d6 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/GenerateNullableTest.cs @@ -0,0 +1,48 @@ +using MagicOnion.Client.SourceGenerator.Tests.Verifiers; + +namespace MagicOnion.Client.SourceGenerator.Tests; + +public class GenerateNullableTest +{ + [Fact] + public async Task NullableReferenceType() + { + var source = """ + using MagicOnion; + using MagicOnion.Client; + + namespace MyApplication1; + + public interface IGreeterService : IService + { + UnaryResult HelloAsync(string? name, int age); + } + + [MagicOnionClientGeneration(typeof(IGreeterService))] + partial class MagicOnionInitializer {} + """; + + await MagicOnionSourceGeneratorVerifier.RunAsync(source); + } + + [Fact] + public async Task NullableValueType() + { + var source = """ + using MagicOnion; + using MagicOnion.Client; + + namespace MyApplication1; + + public interface IGreeterService : IService + { + UnaryResult HelloAsync(string name, int? age); + } + + [MagicOnionClientGeneration(typeof(IGreeterService))] + partial class MagicOnionInitializer {} + """; + + await MagicOnionSourceGeneratorVerifier.RunAsync(source); + } +} diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/MagicOnion.Client.SourceGenerator.Tests.csproj b/tests/MagicOnion.Client.SourceGenerator.Tests/MagicOnion.Client.SourceGenerator.Tests.csproj index b0bf051d8..2c533eeeb 100644 --- a/tests/MagicOnion.Client.SourceGenerator.Tests/MagicOnion.Client.SourceGenerator.Tests.csproj +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/MagicOnion.Client.SourceGenerator.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -43,4 +43,8 @@ + + + + diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs new file mode 100644 index 000000000..a72474002 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs @@ -0,0 +1,42 @@ +// +namespace MagicOnion.Client +{ + /// + /// Marker attribute for generating clients of MagicOnion. + /// The source generator collects the classes specified by this attribute and uses them to generate source. + /// + [global::System.Diagnostics.Conditional("__MagicOnion_Client_SourceGenerator__DesignTimeOnly__")] + [global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false)] + internal class MagicOnionClientGenerationAttribute : global::System.Attribute + { + /// + /// Gets or sets whether to disable automatically calling `Register` during start-up. (Automatic registration requires .NET 5+ or Unity) + /// + public bool DisableAutoRegistration { get; set; } + + /// + /// Gets or set the serializer used for message serialization. The default value is . + /// + public global::MagicOnion.Client.GenerateSerializerType Serializer { get; set; } = global::MagicOnion.Client.GenerateSerializerType.MessagePack; + + /// + /// Gets or set the namespace of pre-generated MessagePackFormatters. The default value is MessagePack.Formatters. + /// + public string MessagePackFormatterNamespace { get; set; } = "MessagePack.Formatters"; + + public global::System.Type[] TypesContainedInTargetAssembly { get; } + + /// Types contained in the scan target assembly + public MagicOnionClientGenerationAttribute(params global::System.Type[] typesContainedInTargetAssembly) + { + TypesContainedInTargetAssembly = typesContainedInTargetAssembly; + } + } + + // This enum must be mirror of `SerializerType` (MagicOnionClientSourceGenerator) + internal enum GenerateSerializerType + { + MessagePack = 0, + MemoryPack = 1, + } +} \ No newline at end of file diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0001_MyApplication1_GreeterServiceClient.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0001_MyApplication1_GreeterServiceClient.g.cs new file mode 100644 index 000000000..7765f5892 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0001_MyApplication1_GreeterServiceClient.g.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable CS0618 // 'member' is obsolete: 'text' +#pragma warning disable CS0612 // 'member' is obsolete +#pragma warning disable CS8019 // Unnecessary using directive. + +namespace MyApplication1 +{ + using global::System; + using global::Grpc.Core; + using global::MagicOnion; + using global::MagicOnion.Client; + using global::MessagePack; + + partial class MagicOnionInitializer + { + static partial class MagicOnionGeneratedClient + { + [global::MagicOnion.Ignore] + public class MyApplication1_GreeterServiceClient : global::MagicOnion.Client.MagicOnionClientBase, global::MyApplication1.IGreeterService + { + class ClientCore + { + public global::MagicOnion.Client.Internal.RawMethodInvoker, global::System.String> HelloAsync; + public ClientCore(global::MagicOnion.Serialization.IMagicOnionSerializerProvider serializerProvider) + { + this.HelloAsync = global::MagicOnion.Client.Internal.RawMethodInvoker.Create_ValueType_RefType, global::System.String>(global::Grpc.Core.MethodType.Unary, "IGreeterService", "HelloAsync", serializerProvider); + } + } + + readonly ClientCore core; + + public MyApplication1_GreeterServiceClient(global::MagicOnion.Client.MagicOnionClientOptions options, global::MagicOnion.Serialization.IMagicOnionSerializerProvider serializerProvider) : base(options) + { + this.core = new ClientCore(serializerProvider); + } + + private MyApplication1_GreeterServiceClient(MagicOnionClientOptions options, ClientCore core) : base(options) + { + this.core = core; + } + + protected override global::MagicOnion.Client.MagicOnionClientBase Clone(global::MagicOnion.Client.MagicOnionClientOptions options) + => new MyApplication1_GreeterServiceClient(options, core); + + public global::MagicOnion.UnaryResult HelloAsync(global::System.String name, global::System.Int32 age) + => this.core.HelloAsync.InvokeUnary(this, "IGreeterService/HelloAsync", new global::MagicOnion.DynamicArgumentTuple(name, age)); + } + } + } +} diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs new file mode 100644 index 000000000..43bfda7f3 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs @@ -0,0 +1,80 @@ +// +#pragma warning disable CS0618 // 'member' is obsolete: 'text' +#pragma warning disable CS0612 // 'member' is obsolete +#pragma warning disable CS8019 // Unnecessary using directive. +#pragma warning disable CS1522 // Empty switch block + +namespace MyApplication1 +{ + using global::System; + using global::MessagePack; + + partial class MagicOnionInitializer + { + /// + /// Gets the generated MessagePack formatter resolver. + /// + public static global::MessagePack.IFormatterResolver Resolver => MessagePackGeneratedResolver.Instance; + class MessagePackGeneratedResolver : global::MessagePack.IFormatterResolver + { + public static readonly global::MessagePack.IFormatterResolver Instance = new MessagePackGeneratedResolver(); + + MessagePackGeneratedResolver() {} + + public global::MessagePack.Formatters.IMessagePackFormatter GetFormatter() + => FormatterCache.formatter; + + static class FormatterCache + { + public static readonly global::MessagePack.Formatters.IMessagePackFormatter formatter; + + static FormatterCache() + { + var f = MessagePackGeneratedGetFormatterHelper.GetFormatter(typeof(T)); + if (f != null) + { + formatter = (global::MessagePack.Formatters.IMessagePackFormatter)f; + } + } + } + } + static class MessagePackGeneratedGetFormatterHelper + { + static readonly global::System.Collections.Generic.Dictionary lookup; + + static MessagePackGeneratedGetFormatterHelper() + { + lookup = new global::System.Collections.Generic.Dictionary(1) + { + {typeof(global::MagicOnion.DynamicArgumentTuple), 0}, + }; + } + internal static object GetFormatter(global::System.Type t) + { + int key; + if (!lookup.TryGetValue(t, out key)) + { + return null; + } + + switch (key) + { + case 0: return new global::MagicOnion.DynamicArgumentTupleFormatter(default(global::System.String), default(global::System.Int32)); + default: return null; + } + } + } + /// Type hints for Ahead-of-Time compilation. + [Preserve] + static class TypeHints + { + [Preserve] + internal static void Register() + { + _ = MessagePackGeneratedResolver.Instance.GetFormatter>(); + _ = MessagePackGeneratedResolver.Instance.GetFormatter(); + _ = MessagePackGeneratedResolver.Instance.GetFormatter(); + } + } + } +} diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0003_MyApplication1_MagicOnionInitializer.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0003_MyApplication1_MagicOnionInitializer.g.cs new file mode 100644 index 000000000..843084e18 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableReferenceType/0003_MyApplication1_MagicOnionInitializer.g.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable CS0618 // 'member' is obsolete: 'text' +#pragma warning disable CS0612 // 'member' is obsolete +#pragma warning disable CS8019 // Unnecessary using directive. +namespace MyApplication1 +{ + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::MagicOnion; + using global::MagicOnion.Client; + + partial class PreserveAttribute : global::System.Attribute {} + + partial class MagicOnionInitializer + { + static bool isRegistered = false; + readonly static MagicOnionGeneratedClientFactoryProvider provider = new(); + + /// + /// Gets the generated MagicOnionClientFactoryProvider. + /// + public static global::MagicOnion.Client.IMagicOnionClientFactoryProvider ClientFactoryProvider => provider; + + /// + /// Gets the generated StreamingHubClientFactoryProvider. + /// + public static global::MagicOnion.Client.IStreamingHubClientFactoryProvider StreamingHubClientFactoryProvider => provider; +#if UNITY_2019_4_OR_NEWER + [global::UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)] +#elif NET5_0_OR_GREATER + [global::System.Runtime.CompilerServices.ModuleInitializer] +#endif + internal static void Register() => TryRegisterProviderFactory(); + + /// + /// Register the generated client factory providers if it's not registered yet. This method will register only once. + /// + public static bool TryRegisterProviderFactory() + { + if (isRegistered) return false; + isRegistered = true; + + global::MagicOnion.Client.MagicOnionClientFactoryProvider.Default = + (global::MagicOnion.Client.MagicOnionClientFactoryProvider.Default is global::MagicOnion.Client.ImmutableMagicOnionClientFactoryProvider immutableMagicOnionClientFactoryProvider) + ? immutableMagicOnionClientFactoryProvider.Add(provider) + : new global::MagicOnion.Client.ImmutableMagicOnionClientFactoryProvider(provider); + + global::MagicOnion.Client.StreamingHubClientFactoryProvider.Default = + (global::MagicOnion.Client.StreamingHubClientFactoryProvider.Default is global::MagicOnion.Client.ImmutableStreamingHubClientFactoryProvider immutableStreamingHubClientFactoryProvider) + ? immutableStreamingHubClientFactoryProvider.Add(provider) + : new global::MagicOnion.Client.ImmutableStreamingHubClientFactoryProvider(provider); + + return true; + } + + class MagicOnionGeneratedClientFactoryProvider : global::MagicOnion.Client.IMagicOnionClientFactoryProvider, global::MagicOnion.Client.IStreamingHubClientFactoryProvider + { + bool global::MagicOnion.Client.IMagicOnionClientFactoryProvider.TryGetFactory(out global::MagicOnion.Client.MagicOnionClientFactoryDelegate factory) + => (factory = MagicOnionClientFactoryCache.Factory) != null; + + bool global::MagicOnion.Client.IStreamingHubClientFactoryProvider.TryGetFactory(out global::MagicOnion.Client.StreamingHubClientFactoryDelegate factory) + => (factory = StreamingHubClientFactoryCache.Factory) != null; + + static class MagicOnionClientFactoryCache where T : global::MagicOnion.IService + { + public readonly static global::MagicOnion.Client.MagicOnionClientFactoryDelegate Factory; + + static MagicOnionClientFactoryCache() + { + object factory = default(global::MagicOnion.Client.MagicOnionClientFactoryDelegate); + + if (typeof(T) == typeof(global::MyApplication1.IGreeterService)) + { + factory = ((global::MagicOnion.Client.MagicOnionClientFactoryDelegate)((x, y) => new MagicOnionGeneratedClient.MyApplication1_GreeterServiceClient(x, y))); + } + Factory = (global::MagicOnion.Client.MagicOnionClientFactoryDelegate)factory; + } + } + + static class StreamingHubClientFactoryCache where TStreamingHub : global::MagicOnion.IStreamingHub + { + public readonly static global::MagicOnion.Client.StreamingHubClientFactoryDelegate Factory; + + static StreamingHubClientFactoryCache() + { + object factory = default(global::MagicOnion.Client.StreamingHubClientFactoryDelegate); + + + Factory = (global::MagicOnion.Client.StreamingHubClientFactoryDelegate)factory; + } + } + } + } +} diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs new file mode 100644 index 000000000..a72474002 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0000_MagicOnionClientSourceGeneratorAttributes.g.cs @@ -0,0 +1,42 @@ +// +namespace MagicOnion.Client +{ + /// + /// Marker attribute for generating clients of MagicOnion. + /// The source generator collects the classes specified by this attribute and uses them to generate source. + /// + [global::System.Diagnostics.Conditional("__MagicOnion_Client_SourceGenerator__DesignTimeOnly__")] + [global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false)] + internal class MagicOnionClientGenerationAttribute : global::System.Attribute + { + /// + /// Gets or sets whether to disable automatically calling `Register` during start-up. (Automatic registration requires .NET 5+ or Unity) + /// + public bool DisableAutoRegistration { get; set; } + + /// + /// Gets or set the serializer used for message serialization. The default value is . + /// + public global::MagicOnion.Client.GenerateSerializerType Serializer { get; set; } = global::MagicOnion.Client.GenerateSerializerType.MessagePack; + + /// + /// Gets or set the namespace of pre-generated MessagePackFormatters. The default value is MessagePack.Formatters. + /// + public string MessagePackFormatterNamespace { get; set; } = "MessagePack.Formatters"; + + public global::System.Type[] TypesContainedInTargetAssembly { get; } + + /// Types contained in the scan target assembly + public MagicOnionClientGenerationAttribute(params global::System.Type[] typesContainedInTargetAssembly) + { + TypesContainedInTargetAssembly = typesContainedInTargetAssembly; + } + } + + // This enum must be mirror of `SerializerType` (MagicOnionClientSourceGenerator) + internal enum GenerateSerializerType + { + MessagePack = 0, + MemoryPack = 1, + } +} \ No newline at end of file diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0001_MyApplication1_GreeterServiceClient.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0001_MyApplication1_GreeterServiceClient.g.cs new file mode 100644 index 000000000..bc4295173 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0001_MyApplication1_GreeterServiceClient.g.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable CS0618 // 'member' is obsolete: 'text' +#pragma warning disable CS0612 // 'member' is obsolete +#pragma warning disable CS8019 // Unnecessary using directive. + +namespace MyApplication1 +{ + using global::System; + using global::Grpc.Core; + using global::MagicOnion; + using global::MagicOnion.Client; + using global::MessagePack; + + partial class MagicOnionInitializer + { + static partial class MagicOnionGeneratedClient + { + [global::MagicOnion.Ignore] + public class MyApplication1_GreeterServiceClient : global::MagicOnion.Client.MagicOnionClientBase, global::MyApplication1.IGreeterService + { + class ClientCore + { + public global::MagicOnion.Client.Internal.RawMethodInvoker>, global::System.Nullable> HelloAsync; + public ClientCore(global::MagicOnion.Serialization.IMagicOnionSerializerProvider serializerProvider) + { + this.HelloAsync = global::MagicOnion.Client.Internal.RawMethodInvoker.Create_ValueType_ValueType>, global::System.Nullable>(global::Grpc.Core.MethodType.Unary, "IGreeterService", "HelloAsync", serializerProvider); + } + } + + readonly ClientCore core; + + public MyApplication1_GreeterServiceClient(global::MagicOnion.Client.MagicOnionClientOptions options, global::MagicOnion.Serialization.IMagicOnionSerializerProvider serializerProvider) : base(options) + { + this.core = new ClientCore(serializerProvider); + } + + private MyApplication1_GreeterServiceClient(MagicOnionClientOptions options, ClientCore core) : base(options) + { + this.core = core; + } + + protected override global::MagicOnion.Client.MagicOnionClientBase Clone(global::MagicOnion.Client.MagicOnionClientOptions options) + => new MyApplication1_GreeterServiceClient(options, core); + + public global::MagicOnion.UnaryResult> HelloAsync(global::System.String name, global::System.Nullable age) + => this.core.HelloAsync.InvokeUnary(this, "IGreeterService/HelloAsync", new global::MagicOnion.DynamicArgumentTuple>(name, age)); + } + } + } +} diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs new file mode 100644 index 000000000..0e66d3e2a --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0002_MyApplication1_MagicOnionInitializer_Resolver.g.cs @@ -0,0 +1,83 @@ +// +#pragma warning disable CS0618 // 'member' is obsolete: 'text' +#pragma warning disable CS0612 // 'member' is obsolete +#pragma warning disable CS8019 // Unnecessary using directive. +#pragma warning disable CS1522 // Empty switch block + +namespace MyApplication1 +{ + using global::System; + using global::MessagePack; + + partial class MagicOnionInitializer + { + /// + /// Gets the generated MessagePack formatter resolver. + /// + public static global::MessagePack.IFormatterResolver Resolver => MessagePackGeneratedResolver.Instance; + class MessagePackGeneratedResolver : global::MessagePack.IFormatterResolver + { + public static readonly global::MessagePack.IFormatterResolver Instance = new MessagePackGeneratedResolver(); + + MessagePackGeneratedResolver() {} + + public global::MessagePack.Formatters.IMessagePackFormatter GetFormatter() + => FormatterCache.formatter; + + static class FormatterCache + { + public static readonly global::MessagePack.Formatters.IMessagePackFormatter formatter; + + static FormatterCache() + { + var f = MessagePackGeneratedGetFormatterHelper.GetFormatter(typeof(T)); + if (f != null) + { + formatter = (global::MessagePack.Formatters.IMessagePackFormatter)f; + } + } + } + } + static class MessagePackGeneratedGetFormatterHelper + { + static readonly global::System.Collections.Generic.Dictionary lookup; + + static MessagePackGeneratedGetFormatterHelper() + { + lookup = new global::System.Collections.Generic.Dictionary(1) + { + {typeof(global::MagicOnion.DynamicArgumentTuple>), 0}, + }; + } + internal static object GetFormatter(global::System.Type t) + { + int key; + if (!lookup.TryGetValue(t, out key)) + { + return null; + } + + switch (key) + { + case 0: return new global::MagicOnion.DynamicArgumentTupleFormatter>(default(global::System.String), default(global::System.Nullable)); + default: return null; + } + } + } + /// Type hints for Ahead-of-Time compilation. + [Preserve] + static class TypeHints + { + [Preserve] + internal static void Register() + { + _ = MessagePackGeneratedResolver.Instance.GetFormatter>>(); + _ = MessagePackGeneratedResolver.Instance.GetFormatter(); + _ = MessagePackGeneratedResolver.Instance.GetFormatter(); + _ = MessagePackGeneratedResolver.Instance.GetFormatter>(); + _ = MessagePackGeneratedResolver.Instance.GetFormatter>(); + _ = MessagePackGeneratedResolver.Instance.GetFormatter(); + } + } + } +} diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0003_MyApplication1_MagicOnionInitializer.g.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0003_MyApplication1_MagicOnionInitializer.g.cs new file mode 100644 index 000000000..843084e18 --- /dev/null +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Resources/GenerateNullableTest/NullableValueType/0003_MyApplication1_MagicOnionInitializer.g.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable CS0618 // 'member' is obsolete: 'text' +#pragma warning disable CS0612 // 'member' is obsolete +#pragma warning disable CS8019 // Unnecessary using directive. +namespace MyApplication1 +{ + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::MagicOnion; + using global::MagicOnion.Client; + + partial class PreserveAttribute : global::System.Attribute {} + + partial class MagicOnionInitializer + { + static bool isRegistered = false; + readonly static MagicOnionGeneratedClientFactoryProvider provider = new(); + + /// + /// Gets the generated MagicOnionClientFactoryProvider. + /// + public static global::MagicOnion.Client.IMagicOnionClientFactoryProvider ClientFactoryProvider => provider; + + /// + /// Gets the generated StreamingHubClientFactoryProvider. + /// + public static global::MagicOnion.Client.IStreamingHubClientFactoryProvider StreamingHubClientFactoryProvider => provider; +#if UNITY_2019_4_OR_NEWER + [global::UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)] +#elif NET5_0_OR_GREATER + [global::System.Runtime.CompilerServices.ModuleInitializer] +#endif + internal static void Register() => TryRegisterProviderFactory(); + + /// + /// Register the generated client factory providers if it's not registered yet. This method will register only once. + /// + public static bool TryRegisterProviderFactory() + { + if (isRegistered) return false; + isRegistered = true; + + global::MagicOnion.Client.MagicOnionClientFactoryProvider.Default = + (global::MagicOnion.Client.MagicOnionClientFactoryProvider.Default is global::MagicOnion.Client.ImmutableMagicOnionClientFactoryProvider immutableMagicOnionClientFactoryProvider) + ? immutableMagicOnionClientFactoryProvider.Add(provider) + : new global::MagicOnion.Client.ImmutableMagicOnionClientFactoryProvider(provider); + + global::MagicOnion.Client.StreamingHubClientFactoryProvider.Default = + (global::MagicOnion.Client.StreamingHubClientFactoryProvider.Default is global::MagicOnion.Client.ImmutableStreamingHubClientFactoryProvider immutableStreamingHubClientFactoryProvider) + ? immutableStreamingHubClientFactoryProvider.Add(provider) + : new global::MagicOnion.Client.ImmutableStreamingHubClientFactoryProvider(provider); + + return true; + } + + class MagicOnionGeneratedClientFactoryProvider : global::MagicOnion.Client.IMagicOnionClientFactoryProvider, global::MagicOnion.Client.IStreamingHubClientFactoryProvider + { + bool global::MagicOnion.Client.IMagicOnionClientFactoryProvider.TryGetFactory(out global::MagicOnion.Client.MagicOnionClientFactoryDelegate factory) + => (factory = MagicOnionClientFactoryCache.Factory) != null; + + bool global::MagicOnion.Client.IStreamingHubClientFactoryProvider.TryGetFactory(out global::MagicOnion.Client.StreamingHubClientFactoryDelegate factory) + => (factory = StreamingHubClientFactoryCache.Factory) != null; + + static class MagicOnionClientFactoryCache where T : global::MagicOnion.IService + { + public readonly static global::MagicOnion.Client.MagicOnionClientFactoryDelegate Factory; + + static MagicOnionClientFactoryCache() + { + object factory = default(global::MagicOnion.Client.MagicOnionClientFactoryDelegate); + + if (typeof(T) == typeof(global::MyApplication1.IGreeterService)) + { + factory = ((global::MagicOnion.Client.MagicOnionClientFactoryDelegate)((x, y) => new MagicOnionGeneratedClient.MyApplication1_GreeterServiceClient(x, y))); + } + Factory = (global::MagicOnion.Client.MagicOnionClientFactoryDelegate)factory; + } + } + + static class StreamingHubClientFactoryCache where TStreamingHub : global::MagicOnion.IStreamingHub + { + public readonly static global::MagicOnion.Client.StreamingHubClientFactoryDelegate Factory; + + static StreamingHubClientFactoryCache() + { + object factory = default(global::MagicOnion.Client.StreamingHubClientFactoryDelegate); + + + Factory = (global::MagicOnion.Client.StreamingHubClientFactoryDelegate)factory; + } + } + } + } +} diff --git a/tests/MagicOnion.Client.SourceGenerator.Tests/Verifiers/MagicOnionSourceGeneratorVerifier.cs b/tests/MagicOnion.Client.SourceGenerator.Tests/Verifiers/MagicOnionSourceGeneratorVerifier.cs index dda347ee2..07b8bb088 100644 --- a/tests/MagicOnion.Client.SourceGenerator.Tests/Verifiers/MagicOnionSourceGeneratorVerifier.cs +++ b/tests/MagicOnion.Client.SourceGenerator.Tests/Verifiers/MagicOnionSourceGeneratorVerifier.cs @@ -1,3 +1,4 @@ +// NOTE: To generate the Verify reference source code, temporarily uncomment the following line and run the test. //#define WRITE_EXPECTED // https://github.com/MessagePack-CSharp/MessagePack-CSharp/blob/develop/tests/MessagePack.SourceGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier%601%2BTest.cs From 5c0fa5c669994ff4283b9e6fc7ee6327c15189c5 Mon Sep 17 00:00:00 2001 From: Mayuki Sawatari Date: Thu, 26 Oct 2023 10:16:17 +0900 Subject: [PATCH 3/3] Enable nullable --- .../JwtAuthApp.Client/JwtAuthApp.Client.csproj | 2 ++ samples/JwtAuthentication/JwtAuthApp.Client/Program.cs | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/samples/JwtAuthentication/JwtAuthApp.Client/JwtAuthApp.Client.csproj b/samples/JwtAuthentication/JwtAuthApp.Client/JwtAuthApp.Client.csproj index 10b61ef6e..eda654116 100644 --- a/samples/JwtAuthentication/JwtAuthApp.Client/JwtAuthApp.Client.csproj +++ b/samples/JwtAuthentication/JwtAuthApp.Client/JwtAuthApp.Client.csproj @@ -3,6 +3,8 @@ Exe net7.0 + enable + enable diff --git a/samples/JwtAuthentication/JwtAuthApp.Client/Program.cs b/samples/JwtAuthentication/JwtAuthApp.Client/Program.cs index 827c672db..ee8977d2a 100644 --- a/samples/JwtAuthentication/JwtAuthApp.Client/Program.cs +++ b/samples/JwtAuthentication/JwtAuthApp.Client/Program.cs @@ -124,12 +124,12 @@ public async ValueTask SendAsync(RequestContext context, Func Token == null || Expiration < DateTimeOffset.Now;