diff --git a/.editorconfig b/.editorconfig index 2febbc3..557fa05 100644 --- a/.editorconfig +++ b/.editorconfig @@ -77,7 +77,7 @@ csharp_style_expression_bodied_properties = true:warning csharp_style_expression_bodied_indexers = true:warning csharp_style_expression_bodied_accessors = true:warning -csharp_style_namespace_declarations = file_scoped +csharp_style_namespace_declarations = file_scoped:silent csharp_style_pattern_matching_over_is_with_cast_check = true:warning csharp_style_pattern_matching_over_as_with_null_check = true:warning @@ -461,3 +461,86 @@ max_line_length = 120 [nuget.config] indent_size = 2 + +[*.cs] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.private_or_internal_field_should_be_begins_with_underscore.severity = suggestion +dotnet_naming_rule.private_or_internal_field_should_be_begins_with_underscore.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_begins_with_underscore.style = begins_with_underscore + +# Symbol specifications + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_underscore.required_prefix = _ +dotnet_naming_style.begins_with_underscore.required_suffix = +dotnet_naming_style.begins_with_underscore.word_separator = +dotnet_naming_style.begins_with_underscore.capitalization = camel_case +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +dotnet_diagnostic.SA1127.severity = silent +dotnet_diagnostic.SA1202.severity = silent + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +end_of_line = crlf +dotnet_diagnostic.CA1002.severity = silent +dotnet_diagnostic.CA1030.severity = silent diff --git a/Directory.Build.props b/Directory.Build.props index addf36b..0ab0bb5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,6 +18,7 @@ latest enable net7.0 + latest-Recommended diff --git a/OrleansTestKit.sln b/OrleansTestKit.sln index 70cea3d..9266d2c 100644 --- a/OrleansTestKit.sln +++ b/OrleansTestKit.sln @@ -11,6 +11,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrleansTestKit", "src\Orlea EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrleansTestKit.Tests", "test\OrleansTestKit.Tests\OrleansTestKit.Tests.csproj", "{1DBA9447-3F61-4F00-8DDF-2CA22D531D27}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5520B20D-D27A-4F49-BB24-BF34A6DC2EAC}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + CodeMaid.config = CodeMaid.config + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + global.json = global.json + LICENSE = LICENSE + omnisharp.json = omnisharp.json + README.md = README.md + stylecop.json = stylecop.json + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/OrleansTestKit/Reminders/ReminderExtensions.cs b/src/OrleansTestKit/Reminders/ReminderExtensions.cs index 07f1f81..4a085fe 100644 --- a/src/OrleansTestKit/Reminders/ReminderExtensions.cs +++ b/src/OrleansTestKit/Reminders/ReminderExtensions.cs @@ -2,29 +2,35 @@ namespace Orleans.TestKit; +/// +/// Extensions for test kit reminder firing. +/// public static class ReminderExtensions { + /// + /// Fire all reminders for this silo. + /// + /// The test kit silo. + /// The tick status. + /// A representing the result of the asynchronous operation. public static Task FireAllReminders(this TestKitSilo silo, TickStatus tickStatus = default) { - if (silo == null) - { - throw new ArgumentNullException(nameof(silo)); - } + ArgumentNullException.ThrowIfNull(silo); return silo.ReminderRegistry.FireAllReminders(tickStatus); } + /// + /// Fire specific reminder for this silo. + /// + /// The test kit silo. + /// The reminder to fire. + /// The tick status. + /// A representing the result of the asynchronous operation. public static Task FireReminder(this TestKitSilo silo, string reminderName, TickStatus tickStatus = default) { - if (silo == null) - { - throw new ArgumentNullException(nameof(silo)); - } - - if (reminderName == null) - { - throw new ArgumentNullException(nameof(reminderName)); - } + ArgumentNullException.ThrowIfNull(silo); + ArgumentNullException.ThrowIfNull(reminderName); return silo.ReminderRegistry.FireReminder(reminderName, tickStatus); } diff --git a/src/OrleansTestKit/Reminders/TestReminderRegistry.cs b/src/OrleansTestKit/Reminders/TestReminderRegistry.cs index 0b05518..b0103b5 100644 --- a/src/OrleansTestKit/Reminders/TestReminderRegistry.cs +++ b/src/OrleansTestKit/Reminders/TestReminderRegistry.cs @@ -8,7 +8,9 @@ public sealed class TestReminderRegistry : IReminderRegistry { private readonly Dictionary _reminders = new(); - private IRemindable _grain; + private IRemindable? _grain; + + private IRemindable Grain => _grain ?? throw new InvalidOperationException($"You must SetGrainTarget before invoking other methods"); public Mock Mock { get; } = new(); @@ -16,7 +18,7 @@ public async Task FireAllReminders(TickStatus tickStatus) { foreach (var reminderName in _reminders.Keys) { - await _grain.ReceiveReminder(reminderName, tickStatus); + await Grain.ReceiveReminder(reminderName, tickStatus); } } @@ -32,15 +34,12 @@ public Task FireReminder(string reminderName, TickStatus tickStatus) throw new ArgumentException($"No reminder named {reminderName} found"); } - return _grain.ReceiveReminder(reminderName, tickStatus); + return Grain.ReceiveReminder(reminderName, tickStatus); } - public async Task GetReminder(GrainId callingGrainId, string reminderName) + public async Task GetReminder(GrainId callingGrainId, string reminderName) { - if (reminderName == null) - { - throw new ArgumentNullException(nameof(reminderName)); - } + ArgumentNullException.ThrowIfNull(reminderName); await Mock.Object.GetReminder(callingGrainId, reminderName); return !_reminders.TryGetValue(reminderName, out var reminder) ? null : reminder; diff --git a/src/OrleansTestKit/Storage/TestStorage.cs b/src/OrleansTestKit/Storage/TestStorage.cs index 038868f..a0dfcd0 100644 --- a/src/OrleansTestKit/Storage/TestStorage.cs +++ b/src/OrleansTestKit/Storage/TestStorage.cs @@ -12,8 +12,8 @@ public TestStorage() InitializeState(); } - public TestStorage(TState state) : this() => - State = state; + public TestStorage(TState state) + : this() => State = state; public string Etag => throw new NotImplementedException(); diff --git a/src/OrleansTestKit/Streams/TestStream.cs b/src/OrleansTestKit/Streams/TestStream.cs index cbaac3b..452822b 100644 --- a/src/OrleansTestKit/Streams/TestStream.cs +++ b/src/OrleansTestKit/Streams/TestStream.cs @@ -5,39 +5,59 @@ namespace Orleans.TestKit.Streams; -[SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes")] -[SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix")] +/// +/// A test stream that implements IAsyncStream. +/// +/// The stream event type. +[SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes", Justification = "not needed")] public sealed class TestStream : IAsyncStream, IStreamIdentity { - private readonly List> _handlers = new List>(); + private readonly List> _handlers = new(); - private readonly Mock> _mockStream = new Mock>(); + private readonly Mock> _mockStream = new(); - private readonly List> _observers = new List>(); + private readonly List> _observers = new(); + /// + /// Initializes a new instance of the class. + /// + /// The stream id. + /// The provider name to use. public TestStream(StreamId streamId, string providerName) { StreamId = streamId; ProviderName = providerName ?? throw new ArgumentNullException(nameof(providerName)); } + /// public Guid Guid { get; } + /// public bool IsRewindable => false; - public string Namespace { get; } + /// + public string Namespace { get; } = string.Empty; + /// public string ProviderName { get; } /// Gets the number of times OneNextAsync was called. public uint Sends { get; private set; } + /// public StreamId StreamId { get; } + /// + /// Gets the count of subscribers. + /// public int Subscribed => _observers.Count; - /// Create an empty handler that can then be used to test resuming streams. - public Task> AddEmptyStreamHandler(Action> onAttachingObserver = null) + /// + /// Create an empty handler that can then be used to test resuming streams. + /// + /// action to fire. + /// stream handle. + public Task> AddEmptyStreamHandler(Action>? onAttachingObserver = null) { var handle = CreateEmptyStreamHandlerImpl(onAttachingObserver); _handlers.Add(handle); @@ -45,31 +65,35 @@ public Task> AddEmptyStreamHandler(Action>(handle); } - public int CompareTo(IAsyncStream other) => - throw new NotImplementedException(); + /// + public int CompareTo(IAsyncStream? other) => 0; - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public bool Equals(IAsyncStream other) => - throw new NotImplementedException(); + /// + public bool Equals(IAsyncStream? other) => ReferenceEquals(this, other); + /// public Task>> GetAllSubscriptionHandles() => Task.FromResult>>(new List>(_handlers)); + /// public Task OnCompletedAsync() => Task.WhenAll(_observers.ToList().Select(o => o.OnCompletedAsync())); + /// public Task OnErrorAsync(Exception ex) => Task.WhenAll(_observers.ToList().Select(o => o.OnErrorAsync(ex))); - public Task OnNextAsync(T item, StreamSequenceToken token = null) + /// + public Task OnNextAsync(T item, StreamSequenceToken? token = null) { Sends++; _mockStream.Object.OnNextAsync(item, token); return Task.WhenAll(_observers.ToList().Select(o => o.OnNextAsync(item, token))); } - [SuppressMessage("Design", "CA1031:Do not catch general exception types")] - public async Task OnNextBatchAsync(IEnumerable batch, StreamSequenceToken token = null) + /// + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "intentional")] + public async Task OnNextBatchAsync(IEnumerable? batch, StreamSequenceToken? token = null) { if (batch == null) { @@ -95,6 +119,7 @@ public async Task OnNextBatchAsync(IEnumerable batch, StreamSequenceToken tok } } + /// public Task> SubscribeAsync(IAsyncObserver observer) { if (observer == null) @@ -109,41 +134,55 @@ public Task> SubscribeAsync(IAsyncObserver observ return Task.FromResult>(handle); } - public Task> SubscribeAsync(IAsyncObserver observer, StreamSequenceToken token, string filterData = null) => + /// + public Task> SubscribeAsync(IAsyncObserver observer, StreamSequenceToken? token, string? filterData = null) => throw new NotImplementedException(); - public Task> SubscribeAsync(IAsyncBatchObserver observer) => + /// + public Task> SubscribeAsync(IAsyncBatchObserver? observer) => throw new NotImplementedException(); - public Task> SubscribeAsync(IAsyncBatchObserver observer, StreamSequenceToken token) => + /// + public Task> SubscribeAsync(IAsyncBatchObserver? observer, StreamSequenceToken? token) => throw new NotImplementedException(); + /// + /// Verify that the stream was sent. + /// + /// item to check. public void VerifySend(Func check) => VerifySend(check, Times.Once()); + /// + /// Verify that the stream was sent the specified item N times. + /// + /// item to check. + /// number of times. public void VerifySend(Func check, Times times) => _mockStream.Verify(s => s.OnNextAsync(It.Is(a => check(a)), It.IsAny()), times); private TestStreamSubscriptionHandle CreateEmptyStreamHandlerImpl( - Action> onAttachingObserver = null) + Action>? onAttachingObserver = null) { - TestStreamSubscriptionHandle handle = null; + TestStreamSubscriptionHandle? handle = null; handle = new TestStreamSubscriptionHandle( StreamId, ProviderName, observer => { - _handlers.Remove(handle); + _handlers.Remove(handle!); if (observer != null) { _observers.Remove(observer); } - }, observer => + }, + observer => { onAttachingObserver?.Invoke(observer); _observers.Add(observer); - }, observer => + }, + observer => { _observers.Remove(observer); }); diff --git a/src/OrleansTestKit/Streams/TestStreamProvider.cs b/src/OrleansTestKit/Streams/TestStreamProvider.cs index 68f8b06..b836e5a 100644 --- a/src/OrleansTestKit/Streams/TestStreamProvider.cs +++ b/src/OrleansTestKit/Streams/TestStreamProvider.cs @@ -12,9 +12,11 @@ public sealed class TestStreamProvider : IStreamProvider public TestStreamProvider(TestKitOptions options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public bool IsRewindable { get; } - public string Name { get; private set; } = ""; + /// + public string Name { get; private set; } = string.Empty; public TestStream AddStreamProbe(StreamId streamId) { @@ -23,11 +25,12 @@ public TestStream AddStreamProbe(StreamId streamId) return stream; } + /// public IAsyncStream GetStream(StreamId streamId) { if (_streams.TryGetValue(streamId, out var stream)) { - return stream as IAsyncStream; + return (IAsyncStream)stream; } if (_options.StrictStreamProbes) @@ -36,7 +39,7 @@ public IAsyncStream GetStream(StreamId streamId) } stream = AddStreamProbe(streamId); - return stream as IAsyncStream; + return (IAsyncStream)stream; } public Task Init(string name) diff --git a/src/OrleansTestKit/Streams/TestStreamProviderManager.cs b/src/OrleansTestKit/Streams/TestStreamProviderManager.cs index c66d554..7577803 100644 --- a/src/OrleansTestKit/Streams/TestStreamProviderManager.cs +++ b/src/OrleansTestKit/Streams/TestStreamProviderManager.cs @@ -7,7 +7,7 @@ public sealed class TestStreamProviderManager : IKeyedServiceCollection _streamProviders = new Dictionary(); + private readonly Dictionary _streamProviders = new(); public TestStreamProviderManager(TestKitOptions options) => _options = options ?? throw new ArgumentNullException(nameof(options)); diff --git a/src/OrleansTestKit/Streams/TestStreamSubscriptionHandle.cs b/src/OrleansTestKit/Streams/TestStreamSubscriptionHandle.cs index 53fe2da..f462811 100644 --- a/src/OrleansTestKit/Streams/TestStreamSubscriptionHandle.cs +++ b/src/OrleansTestKit/Streams/TestStreamSubscriptionHandle.cs @@ -50,6 +50,7 @@ public void AttachObserver(IAsyncObserver observer) { throw new Exception("You can only have one observer per handler"); } + _observer = observer; _onAttachingObserver?.Invoke(observer); } @@ -60,6 +61,7 @@ public void DetachCurrentObserver() { return; } + _onDetachingObserver?.Invoke(_observer); _observer = null; } diff --git a/src/OrleansTestKit/TestGrainActivationContext.cs b/src/OrleansTestKit/TestGrainActivationContext.cs index 57ec065..91d948b 100644 --- a/src/OrleansTestKit/TestGrainActivationContext.cs +++ b/src/OrleansTestKit/TestGrainActivationContext.cs @@ -1,44 +1,64 @@ -using System.Diagnostics.CodeAnalysis; -using Orleans.Runtime; +using Orleans.Runtime; namespace Orleans.TestKit; +/// +/// Grain context used for tests. +/// public sealed class TestGrainActivationContext : IGrainContext { + /// public ActivationId ActivationId { get; set; } - public IServiceProvider ActivationServices { get; set; } + /// + public IServiceProvider ActivationServices { get; set; } = default!; - public GrainAddress Address { get; set; } + /// + public GrainAddress Address { get; set; } = default!; - public Task Deactivated { get; set; } + /// + public Task Deactivated { get; set; } = Task.CompletedTask; - public GrainId GrainId { get; set; } + /// + public GrainId GrainId { get; set; } = default!; - public IAddressable GrainIdentity { get; set; } + public IAddressable GrainIdentity { get; set; } = default!; - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public object GrainInstance => throw new NotImplementedException(); + /// + public object GrainInstance { get; set; } = default!; - public GrainReference GrainReference { get; set; } + /// + public GrainReference GrainReference { get; set; } = default!; - public Type GrainType { get; set; } + /// + /// Gets or sets the grain type used for this test context. + /// + public Type GrainType { get; set; } = default!; - public IGrainLifecycle ObservableLifecycle { get; set; } + /// + public IGrainLifecycle ObservableLifecycle { get; set; } = default!; - public IWorkItemScheduler Scheduler { get; set; } + /// + public IWorkItemScheduler Scheduler { get; set; } = default!; + /// public void Activate(Dictionary requestContext, CancellationToken? cancellationToken = null) => throw new NotImplementedException(); + /// public void Deactivate(DeactivationReason deactivationReason, CancellationToken? cancellationToken = null) => throw new NotImplementedException(); - public bool Equals(IGrainContext other) => throw new NotImplementedException(); + /// + public bool Equals(IGrainContext? other) => ReferenceEquals(this, other); + /// public TComponent GetComponent() where TComponent : class => throw new NotImplementedException(); + /// public TTarget GetTarget() where TTarget : class => throw new NotImplementedException(); + /// public void ReceiveMessage(object message) => throw new NotImplementedException(); + /// public void SetComponent(TComponent value) where TComponent : class => throw new NotImplementedException(); } diff --git a/src/OrleansTestKit/TestGrainFactory.cs b/src/OrleansTestKit/TestGrainFactory.cs index 4727243..00a0023 100644 --- a/src/OrleansTestKit/TestGrainFactory.cs +++ b/src/OrleansTestKit/TestGrainFactory.cs @@ -38,13 +38,11 @@ public TGrainInterface GetGrain(string primaryKey, string? grai where TGrainInterface : IGrainWithStringKey => GetProbe(IdSpan.Create(primaryKey), grainClassNamePrefix); - public TGrainInterface GetGrain(Guid primaryKey, string? keyExtension, - string? grainClassNamePrefix = null) + public TGrainInterface GetGrain(Guid primaryKey, string? keyExtension, string? grainClassNamePrefix = null) where TGrainInterface : IGrainWithGuidCompoundKey => GetProbe(GrainIdKeyExtensions.CreateGuidKey(primaryKey, keyExtension), grainClassNamePrefix); - public TGrainInterface GetGrain(long primaryKey, string keyExtension, - string? grainClassNamePrefix = null) + public TGrainInterface GetGrain(long primaryKey, string keyExtension, string? grainClassNamePrefix = null) where TGrainInterface : IGrainWithIntegerCompoundKey => GetProbe(GrainIdKeyExtensions.CreateIntegerKey(primaryKey, keyExtension), grainClassNamePrefix); @@ -88,7 +86,7 @@ internal void AddProbe(Func factory) internal void AddProbe(Func> factory) where T : class, IGrain { - var adaptedFactory = new Func(grainIdentity => factory(grainIdentity)?.Object); + var adaptedFactory = new Func(grainIdentity => factory(grainIdentity).Object); AddProbe(adaptedFactory); } diff --git a/src/OrleansTestKit/TestKitSilo.cs b/src/OrleansTestKit/TestKitSilo.cs index 2682504..c8ce92a 100644 --- a/src/OrleansTestKit/TestKitSilo.cs +++ b/src/OrleansTestKit/TestKitSilo.cs @@ -93,7 +93,7 @@ public Task CreateGrainAsync(long id, string keyExtension) /// Reason which will passed to the Grian . /// /// Token which will passed to the grain. - /// Deactivation task. + /// A task that represents the asynchronous operation. public async Task DeactivateAsync(Grain grain, DeactivationReason? deactivationReason = null, CancellationToken cancellationToken = default) { if (grain == null) diff --git a/src/OrleansTestKit/Timers/TestTimerRegistry.cs b/src/OrleansTestKit/Timers/TestTimerRegistry.cs index 6deb125..430d589 100644 --- a/src/OrleansTestKit/Timers/TestTimerRegistry.cs +++ b/src/OrleansTestKit/Timers/TestTimerRegistry.cs @@ -20,8 +20,7 @@ public async Task FireAllAsync() } } - public Task FireAsync(int index) => - _timers[index].FireAsync(); + public Task FireAsync(int index) => _timers[index].FireAsync(); public IDisposable RegisterTimer(IGrainContext grainContext, Func asyncCallback, object state, TimeSpan dueTime, TimeSpan period) { diff --git a/src/OrleansTestKit/TypeHelper.cs b/src/OrleansTestKit/TypeHelper.cs index 7118a21..ec76152 100644 --- a/src/OrleansTestKit/TypeHelper.cs +++ b/src/OrleansTestKit/TypeHelper.cs @@ -1,6 +1,4 @@ -using System.Reflection; - -namespace Orleans.TestKit; +namespace Orleans.TestKit; public static class TypeHelper { @@ -8,8 +6,9 @@ public static class TypeHelper /// Alternative version of that supports raw generic types (generic types /// without any type parameters). /// - /// The base type class for which the check is made. /// To type to determine for whether it derives from . + /// The base type class for which the check is made. + /// Result of the check. public static bool IsSubclassOfRawGeneric(this Type toCheck, Type baseType) { if (toCheck == null) @@ -30,7 +29,7 @@ public static bool IsSubclassOfRawGeneric(this Type toCheck, Type baseType) return true; } - toCheck = toCheck?.BaseType; + toCheck = toCheck?.BaseType!; } return false;