diff --git a/.github/csharp.instructions.md b/.github/csharp.instructions.md deleted file mode 100644 index f76f12ec..00000000 --- a/.github/csharp.instructions.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -description: 'Guidelines for building C# applications' -applyTo: '**/*.cs' ---- - -# C# Development - -## C# Instructions - -- Always use the latest version C#, currently C# 14 features. -- Write clear and concise comments for each function. - -## General Instructions - -- Make only high confidence suggestions when reviewing code changes. -- Write code with good maintainability practices, including comments on why certain design decisions were made. -- Handle edge cases and write clear exception handling. -- For libraries or external dependencies, mention their usage and purpose in comments. -- Used language for comments, documentation and code should always be English. - -## Naming Conventions - -- Follow PascalCase for component names, method names, and public members. -- Use camelCase for private fields and local variables. -- Prefix interface names with "I" (e.g., IUserService). - -## Formatting - -- Apply code-formatting style defined in `.editorconfig`. -- Prefer file-scoped namespace declarations and single-line using directives. -- Insert a newline before the opening curly brace of any code block (e.g., after `if`, `for`, `while`, `foreach`, - `using`, `try`, etc.). -- Ensure that the final return statement of a method is on its own line. -- Use pattern matching and switch expressions wherever possible. -- Use `nameof` instead of string literals when referring to member names. -- Ensure that XML doc comments are created for any public APIs. When applicable, include `` and `` - documentation in the comments. - -## Project Setup and Structure - -- Guide users through creating a new .NET project with the appropriate templates. -- Explain the purpose of each generated file and folder to build understanding of the project structure. -- Demonstrate how to organize code using feature folders or domain-driven design principles. -- Show proper separation of concerns with models, services, and data access layers. -- Explain the Program.cs and configuration system in ASP.NET Core 10 including environment-specific settings. - -## Nullable Reference Types - -- Declare variables non-nullable, and check for `null` at entry points. -- Always use `is null` or `is not null` instead of `== null` or `!= null`. -- Trust the C# null annotations and don't add null checks when the type system says a value cannot be null. - -## Data Access Patterns - -- Guide the implementation of a data access layer using Entity Framework Core. -- Explain different options (SQL Server, SQLite, In-Memory) for development and production. -- Demonstrate repository pattern implementation and when it's beneficial. -- Show how to implement database migrations and data seeding. -- Explain efficient query patterns to avoid common performance issues. - -## Authentication and Authorization - -- Guide users through implementing authentication using JWT Bearer tokens. -- Explain OAuth 2.0 and OpenID Connect concepts as they relate to ASP.NET Core. -- Show how to implement role-based and policy-based authorization. -- Demonstrate integration with Microsoft Entra ID (formerly Azure AD). -- Explain how to secure both controller-based and Minimal APIs consistently. - -## Validation and Error Handling - -- Guide the implementation of model validation using data annotations and FluentValidation. -- Explain the validation pipeline and how to customize validation responses. -- Demonstrate a global exception handling strategy using middleware. -- Show how to create consistent error responses across the API. -- Explain problem details (RFC 7807) implementation for standardized error responses. - -## API Versioning and Documentation - -- Guide users through implementing and explaining API versioning strategies. -- Demonstrate Swagger/OpenAPI implementation with proper documentation. -- Show how to document endpoints, parameters, responses, and authentication. -- Explain versioning in both controller-based and Minimal APIs. -- Guide users on creating meaningful API documentation that helps consumers. - -## Logging and Monitoring - -- Guide the implementation of structured logging using Serilog or other providers. -- Explain the logging levels and when to use each. -- Demonstrate integration with Application Insights for telemetry collection. -- Show how to implement custom telemetry and correlation IDs for request tracking. -- Explain how to monitor API performance, errors, and usage patterns. - -## Testing - -- Always include test cases for critical paths of the application. -- Guide users through creating unit tests. -- Copy existing style in nearby files for test method names and capitalization. -- Explain integration testing approaches for API endpoints. -- Demonstrate how to mock dependencies for effective testing. -- Show how to test authentication and authorization logic. -- Explain test-driven development principles as applied to API development. -- Use awesomeassertions for asserting expected results. -- Use xUnit for unit testing. -- Use FakeItEasy for mocking dependencies. -- Always separate a test method into the blocks Arrange, Act, and Assert. Mark this blocks with comments. - -## Performance Optimization - -- Guide users on implementing caching strategies (in-memory, distributed, response caching). -- Explain asynchronous programming patterns and why they matter for API performance. -- Demonstrate pagination, filtering, and sorting for large data sets. -- Show how to implement compression and other performance optimizations. -- Explain how to measure and benchmark API performance. - -## Deployment and DevOps - -- Guide users through containerizing their API using .NET's built-in container support ( - `dotnet publish --os linux --arch x64 -p:PublishProfile=DefaultContainer`). -- Explain the differences between manual Dockerfile creation and .NET's container publishing features. -- Explain CI/CD pipelines for NET applications. -- Demonstrate deployment to Azure App Service, Azure Container Apps, or other hosting options. -- Show how to implement health checks and readiness probes. -- Explain environment-specific configurations for different deployment stages. diff --git a/.gitignore b/.gitignore index ff766f9d..fcf2a272 100644 --- a/.gitignore +++ b/.gitignore @@ -330,3 +330,4 @@ ASALocalRun/ .DS_Store .ai-plans +.tokensave diff --git a/Core.sln b/Core.sln index bfff9a72..7db5c496 100644 --- a/Core.sln +++ b/Core.sln @@ -3,9 +3,6 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{D0A3F2FC-664B-4A9D-ACF9-F00EF8E9DFE1}" - ProjectSection(SolutionItems) = preProject - source\Core\README.md = source\Core\README.md - EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreativeCoders.Core", "source\Core\CreativeCoders.Core\CreativeCoders.Core.csproj", "{606AB477-7D63-48EB-814C-46DFE03CB6DE}" EndProject diff --git a/Directory.Packages.props b/Directory.Packages.props index ac1c1aea..f745ccc3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,47 +11,47 @@ - - - - - - - + + + + + + + - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - + - + \ No newline at end of file diff --git a/samples/NetSampleApp/AvmTest.cs b/samples/NetSampleApp/AvmTest.cs index c4636531..49f0f938 100644 --- a/samples/NetSampleApp/AvmTest.cs +++ b/samples/NetSampleApp/AvmTest.cs @@ -42,9 +42,5 @@ public static async Task Run() .ConfigureAwait(false); hostEntries.ForEach(x => Console.WriteLine($"{x.HostName}: {x.IpAddress}")); - - //var device = fritzBox.Wlan.GetWlanDeviceInfo("8C:B8:4A:CA:8F:29"); - - //Console.WriteLine($"Ip: {device.IpAddress}"); } } diff --git a/source/AspNetCore/CreativeCoders.AspNetCore/RawRequestBodyFormatter.cs b/source/AspNetCore/CreativeCoders.AspNetCore/RawRequestBodyFormatter.cs index 5915eb62..77876a77 100644 --- a/source/AspNetCore/CreativeCoders.AspNetCore/RawRequestBodyFormatter.cs +++ b/source/AspNetCore/CreativeCoders.AspNetCore/RawRequestBodyFormatter.cs @@ -19,7 +19,7 @@ public RawRequestBodyFormatter() public override bool CanRead(InputFormatterContext context) { - Ensure.IsNotNull(context, nameof(context)); + Ensure.IsNotNull(context); var contentType = context.HttpContext.Request.ContentType; return string.IsNullOrEmpty(contentType) || diff --git a/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompilationResult.cs b/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompilationResult.cs index 3a0ca34a..2c9a77b1 100644 --- a/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompilationResult.cs +++ b/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompilationResult.cs @@ -16,8 +16,8 @@ internal class RoslynCompilationResult : ICompilationResult public RoslynCompilationResult(CSharpCompilation compilation, ICompilationOutputData outputData) { - Ensure.IsNotNull(compilation, nameof(compilation)); - Ensure.IsNotNull(outputData, nameof(outputData)); + Ensure.IsNotNull(compilation); + Ensure.IsNotNull(outputData); _compilation = compilation; _outputData = outputData; diff --git a/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompiler.cs b/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompiler.cs index f406ef8f..d104f52c 100644 --- a/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompiler.cs +++ b/source/CodeCompilation/CreativeCoders.CodeCompilation.Roslyn/RoslynCompiler.cs @@ -16,7 +16,7 @@ public class RoslynCompiler : ICompiler public ICompilationResult Compile(CompilationPackage compilationPackage, CompilationOutput compilationOutput) { - Ensure.IsNotNull(compilationOutput, nameof(compilationOutput)); + Ensure.IsNotNull(compilationOutput); var syntaxTrees = CreateSyntaxTrees(compilationPackage.SourceCodes).ToArray(); diff --git a/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilationMessage.cs b/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilationMessage.cs index 2c12123a..4896f69f 100644 --- a/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilationMessage.cs +++ b/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilationMessage.cs @@ -9,8 +9,8 @@ public class CompilationMessage { public CompilationMessage(CompilationMessageType messageType, TextSpan sourceSpan, string message) { - Ensure.IsNotNull(messageType, nameof(messageType)); - Ensure.IsNotNull(sourceSpan, nameof(sourceSpan)); + Ensure.IsNotNull(messageType); + Ensure.IsNotNull(sourceSpan); MessageType = messageType; SourceSpan = sourceSpan; diff --git a/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilerFactory.cs b/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilerFactory.cs index 02a875a1..02806622 100644 --- a/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilerFactory.cs +++ b/source/CodeCompilation/CreativeCoders.CodeCompilation/CompilerFactory.cs @@ -11,7 +11,7 @@ public class CompilerFactory : ICompilerFactory public CompilerFactory(Func createCompiler) { - Ensure.IsNotNull(createCompiler, nameof(createCompiler)); + Ensure.IsNotNull(createCompiler); _createCompiler = createCompiler; } diff --git a/source/Config/CreativeCoders.Config.Sources/ConfigurationSource.cs b/source/Config/CreativeCoders.Config.Sources/ConfigurationSource.cs index 078f9113..44194317 100644 --- a/source/Config/CreativeCoders.Config.Sources/ConfigurationSource.cs +++ b/source/Config/CreativeCoders.Config.Sources/ConfigurationSource.cs @@ -15,8 +15,8 @@ public class ConfigurationSource : IConfigurationSource public ConfigurationSource(Func getSettingObject, Func getDefaultSettingObject) { - Ensure.IsNotNull(getSettingObject, nameof(getSettingObject)); - Ensure.IsNotNull(getDefaultSettingObject, nameof(getDefaultSettingObject)); + Ensure.IsNotNull(getSettingObject); + Ensure.IsNotNull(getDefaultSettingObject); _getSettingObject = getSettingObject; _getDefaultSettingObject = getDefaultSettingObject; diff --git a/source/Config/CreativeCoders.Config.Sources/InMemoryConfigurationSource.cs b/source/Config/CreativeCoders.Config.Sources/InMemoryConfigurationSource.cs index 9dc9decb..3a932bfa 100644 --- a/source/Config/CreativeCoders.Config.Sources/InMemoryConfigurationSource.cs +++ b/source/Config/CreativeCoders.Config.Sources/InMemoryConfigurationSource.cs @@ -8,12 +8,12 @@ public class InMemoryConfigurationSource : ConfigurationSource { public InMemoryConfigurationSource(T data) : base(() => data) { - Ensure.IsNotNull(data, nameof(data)); + Ensure.IsNotNull(data); } public InMemoryConfigurationSource(T data, Func getDefaultSettingObject) : base(() => data, getDefaultSettingObject) { - Ensure.IsNotNull(data, nameof(data)); + Ensure.IsNotNull(data); } } diff --git a/source/Config/CreativeCoders.Config.Sources/Json/JsonConfigurationSource.cs b/source/Config/CreativeCoders.Config.Sources/Json/JsonConfigurationSource.cs index 268feb25..9345195f 100644 --- a/source/Config/CreativeCoders.Config.Sources/Json/JsonConfigurationSource.cs +++ b/source/Config/CreativeCoders.Config.Sources/Json/JsonConfigurationSource.cs @@ -22,8 +22,8 @@ public class JsonConfigurationSource : IConfigurationSource public JsonConfigurationSource(string jsonFileName, Func getDefaultSettingObject) { - Ensure.IsNotNullOrWhitespace(jsonFileName, nameof(jsonFileName)); - Ensure.IsNotNull(getDefaultSettingObject, nameof(getDefaultSettingObject)); + Ensure.IsNotNullOrWhitespace(jsonFileName); + Ensure.IsNotNull(getDefaultSettingObject); _jsonFileName = jsonFileName; _getDefaultSettingObject = getDefaultSettingObject; @@ -43,7 +43,7 @@ private T LoadSettingFromFile(string jsonFileName) public static IEnumerable> FromFiles(IEnumerable jsonFileNames) { - Ensure.IsNotNull(jsonFileNames, nameof(jsonFileNames)); + Ensure.IsNotNull(jsonFileNames); return jsonFileNames.Select(fileName => new JsonConfigurationSource(fileName)).ToArray(); } @@ -51,7 +51,7 @@ public static IEnumerable> FromFiles(IEnumerable> FromFiles(IEnumerable jsonFileNames, Func getDefaultSetting) { - Ensure.IsNotNull(jsonFileNames, nameof(jsonFileNames)); + Ensure.IsNotNull(jsonFileNames); return jsonFileNames.Select(fileName => new JsonConfigurationSource(fileName, getDefaultSetting)) .ToArray(); diff --git a/source/Config/CreativeCoders.Config/Configuration.cs b/source/Config/CreativeCoders.Config/Configuration.cs index 10c1879c..b2de86c1 100644 --- a/source/Config/CreativeCoders.Config/Configuration.cs +++ b/source/Config/CreativeCoders.Config/Configuration.cs @@ -43,7 +43,7 @@ private T InvokeWithExceptionHandling(IConfigurationSource source, public IConfiguration AddSource(IConfigurationSource source) where T : class { - Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(source); _sourceRegistrations.Add(new SourceRegistration(typeof(T), source)); return this; @@ -52,7 +52,7 @@ public IConfiguration AddSource(IConfigurationSource source) public IConfiguration AddSources(IEnumerable> sources) where T : class { - Ensure.IsNotNull(sources, nameof(sources)); + Ensure.IsNotNull(sources); sources.ForEach(source => AddSource(source)); return this; @@ -79,7 +79,7 @@ public IEnumerable GetItems() public void OnSourceException( Action onSourceException) { - Ensure.IsNotNull(onSourceException, nameof(onSourceException)); + Ensure.IsNotNull(onSourceException); _onSourceExceptions.Add(onSourceException); } diff --git a/source/Config/CreativeCoders.Config/ServiceCollectionExtensions.cs b/source/Config/CreativeCoders.Config/ServiceCollectionExtensions.cs index 57742759..17176a81 100644 --- a/source/Config/CreativeCoders.Config/ServiceCollectionExtensions.cs +++ b/source/Config/CreativeCoders.Config/ServiceCollectionExtensions.cs @@ -11,7 +11,7 @@ public static class ServiceCollectionExtensions [PublicAPI] public static void AddConfigSystem(this IServiceCollection services, IConfiguration configuration) { - Ensure.NotNull(configuration, nameof(configuration)); + Ensure.NotNull(configuration); services.TryAddSingleton(typeof(ISettingFactory<>), typeof(SettingFactory<>)); services.TryAddSingleton(typeof(ISettingsFactory<>), typeof(SettingsFactory<>)); diff --git a/source/Config/CreativeCoders.Config/Setting.cs b/source/Config/CreativeCoders.Config/Setting.cs index 14ea3702..a339cce5 100644 --- a/source/Config/CreativeCoders.Config/Setting.cs +++ b/source/Config/CreativeCoders.Config/Setting.cs @@ -12,7 +12,7 @@ public class Setting : ISetting public Setting(ISettingFactory settingFactory) { - Ensure.IsNotNull(settingFactory, nameof(settingFactory)); + Ensure.IsNotNull(settingFactory); _settingFactory = settingFactory; } diff --git a/source/Config/CreativeCoders.Config/SettingFactory.cs b/source/Config/CreativeCoders.Config/SettingFactory.cs index f66859ac..1eb791c2 100644 --- a/source/Config/CreativeCoders.Config/SettingFactory.cs +++ b/source/Config/CreativeCoders.Config/SettingFactory.cs @@ -10,7 +10,7 @@ public class SettingFactory : ISettingFactory public SettingFactory(IConfiguration configuration) { - Ensure.IsNotNull(configuration, nameof(configuration)); + Ensure.IsNotNull(configuration); _configuration = configuration; } diff --git a/source/Config/CreativeCoders.Config/Settings.cs b/source/Config/CreativeCoders.Config/Settings.cs index 44bf7347..dbe8eb4d 100644 --- a/source/Config/CreativeCoders.Config/Settings.cs +++ b/source/Config/CreativeCoders.Config/Settings.cs @@ -13,7 +13,7 @@ public class Settings : ISettings public Settings(ISettingsFactory settingsFactory) { - Ensure.IsNotNull(settingsFactory, nameof(settingsFactory)); + Ensure.IsNotNull(settingsFactory); _settingsFactory = settingsFactory; } diff --git a/source/Config/CreativeCoders.Config/SettingsFactory.cs b/source/Config/CreativeCoders.Config/SettingsFactory.cs index 4e396c4b..d884ddf5 100644 --- a/source/Config/CreativeCoders.Config/SettingsFactory.cs +++ b/source/Config/CreativeCoders.Config/SettingsFactory.cs @@ -11,7 +11,7 @@ public class SettingsFactory : ISettingsFactory public SettingsFactory(IConfiguration configuration) { - Ensure.IsNotNull(configuration, nameof(configuration)); + Ensure.IsNotNull(configuration); _configuration = configuration; } diff --git a/source/Core/CreativeCoders.Core/Caching/CacheBase.cs b/source/Core/CreativeCoders.Core/Caching/CacheBase.cs index 6d25c206..ffa3780a 100644 --- a/source/Core/CreativeCoders.Core/Caching/CacheBase.cs +++ b/source/Core/CreativeCoders.Core/Caching/CacheBase.cs @@ -3,43 +3,64 @@ namespace CreativeCoders.Core.Caching; +/// +/// Provides an abstract base class for implementations. Overloads without +/// an expiration policy delegate to the overloads that accept an +/// using . +/// +/// The type of the cache key. +/// The type of the cached value. public abstract class CacheBase : ICache { + /// public virtual TValue GetOrAdd(TKey key, Func getValue, string regionName = null) { return GetOrAdd(key, getValue, CacheExpirationPolicy.NeverExpire, regionName); } + /// public abstract TValue GetOrAdd(TKey key, Func getValue, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// public virtual Task GetOrAddAsync(TKey key, Func getValue, string regionName = null) { return GetOrAddAsync(key, getValue, CacheExpirationPolicy.NeverExpire, regionName); } + /// public abstract Task GetOrAddAsync(TKey key, Func getValue, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// public abstract bool TryGet(TKey key, out TValue value, string regionName = null); + /// public abstract Task> TryGetAsync(TKey key, string regionName = null); + /// public abstract void AddOrUpdate(TKey key, TValue value, string regionName = null); + /// public abstract void AddOrUpdate(TKey key, TValue value, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// public abstract Task AddOrUpdateAsync(TKey key, TValue value, string regionName = null); + /// public abstract Task AddOrUpdateAsync(TKey key, TValue value, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// public abstract void Clear(string regionName = null); + /// public abstract Task ClearAsync(string regionName = null); + /// public abstract void Remove(TKey key, string regionName = null); + /// public abstract Task RemoveAsync(TKey key, string regionName = null); } diff --git a/source/Core/CreativeCoders.Core/Caching/CacheEntryNotFoundException.cs b/source/Core/CreativeCoders.Core/Caching/CacheEntryNotFoundException.cs index 19b43a16..2b06214e 100644 --- a/source/Core/CreativeCoders.Core/Caching/CacheEntryNotFoundException.cs +++ b/source/Core/CreativeCoders.Core/Caching/CacheEntryNotFoundException.cs @@ -2,9 +2,17 @@ namespace CreativeCoders.Core.Caching; +/// +/// The exception that is thrown when a cache entry with the specified key and region is not found. +/// [Serializable] public class CacheEntryNotFoundException : Exception { + /// + /// Initializes a new instance of the class. + /// + /// Cache key that was not found. + /// Cache region name that was searched, or for the default region. public CacheEntryNotFoundException(string key, string regionName) : base($"No cache entry for key = '{key}' and region = '{regionName ?? string.Empty}' found") { @@ -12,7 +20,13 @@ public CacheEntryNotFoundException(string key, string regionName) : RegionName = regionName ?? string.Empty; } + /// + /// Gets the cache key that was not found. + /// public string Key { get; } + /// + /// Gets the name of the cache region that was searched. + /// public string RegionName { get; } } diff --git a/source/Core/CreativeCoders.Core/Caching/CacheExpirationMode.cs b/source/Core/CreativeCoders.Core/Caching/CacheExpirationMode.cs index 56a65f5e..5372008a 100644 --- a/source/Core/CreativeCoders.Core/Caching/CacheExpirationMode.cs +++ b/source/Core/CreativeCoders.Core/Caching/CacheExpirationMode.cs @@ -1,7 +1,17 @@ namespace CreativeCoders.Core.Caching; +/// +/// Specifies the mode used to determine when a cache entry expires. +/// public enum CacheExpirationMode { + /// + /// The cache entry never expires. + /// NeverExpire, + + /// + /// The cache entry expires at a specific absolute date and time. + /// AbsoluteDateTime } diff --git a/source/Core/CreativeCoders.Core/Caching/CacheExpirationPolicy.cs b/source/Core/CreativeCoders.Core/Caching/CacheExpirationPolicy.cs index 2c2345fe..f14811dc 100644 --- a/source/Core/CreativeCoders.Core/Caching/CacheExpirationPolicy.cs +++ b/source/Core/CreativeCoders.Core/Caching/CacheExpirationPolicy.cs @@ -3,17 +3,33 @@ namespace CreativeCoders.Core.Caching; +/// +/// Provides a default implementation of with support for +/// never-expire and absolute-date-time expiration modes. +/// [PublicAPI] public class CacheExpirationPolicy : ICacheExpirationPolicy { + /// + /// A shared policy instance that indicates cache entries should never expire. + /// public static readonly CacheExpirationPolicy NeverExpire = new CacheExpirationPolicy(CacheExpirationMode.NeverExpire); + /// + /// Initializes a new instance of the class. + /// + /// Expiration mode for this policy. public CacheExpirationPolicy(CacheExpirationMode expirationMode) { ExpirationMode = expirationMode; } + /// + /// Creates a new expiration policy that expires at the specified absolute date and time. + /// + /// Absolute date and time at which the cache entry expires. The value is converted to UTC. + /// A new configured for absolute-date-time expiration. public static CacheExpirationPolicy AfterAbsoluteDateTime(DateTime absoluteDateTime) { return new CacheExpirationPolicy(CacheExpirationMode.AbsoluteDateTime) @@ -22,7 +38,9 @@ public static CacheExpirationPolicy AfterAbsoluteDateTime(DateTime absoluteDateT }; } + /// public CacheExpirationMode ExpirationMode { get; } + /// public DateTime AbsoluteDateTime { get; private set; } } diff --git a/source/Core/CreativeCoders.Core/Caching/CacheExtensions.cs b/source/Core/CreativeCoders.Core/Caching/CacheExtensions.cs index 937bcf51..1bd05bb0 100644 --- a/source/Core/CreativeCoders.Core/Caching/CacheExtensions.cs +++ b/source/Core/CreativeCoders.Core/Caching/CacheExtensions.cs @@ -2,8 +2,22 @@ namespace CreativeCoders.Core.Caching; +/// +/// Provides extension methods for that simplify common value retrieval patterns. +/// public static class CacheExtensions { + /// + /// Gets the value associated with the specified key, optionally throwing an exception when the key is not found. + /// + /// The type of the cache key. + /// The type of the cached value. + /// Cache to retrieve the value from. + /// Cache key to look up. + /// When , a is thrown if the key is not found; otherwise, the default value is returned. + /// Optional cache region name. When , the default region is used. + /// The cached value, or the default value when is and the key is not found. + /// The does not exist and is . public static TValue GetValue(this ICache cache, TKey key, bool throwExceptionIfKeyNotExists, string regionName = null) { @@ -20,6 +34,17 @@ public static TValue GetValue(this ICache cache, TKe return default; } + /// + /// Asynchronously gets the value associated with the specified key, optionally throwing an exception when the key is not found. + /// + /// The type of the cache key. + /// The type of the cached value. + /// Cache to retrieve the value from. + /// Cache key to look up. + /// When , a is thrown if the key is not found; otherwise, the default value is returned. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. The task result contains the cached value, or the default value when is and the key is not found. + /// The does not exist and is . public static async Task GetValueAsync(this ICache cache, TKey key, bool throwExceptionIfKeyNotExists, string regionName = null) { @@ -38,24 +63,64 @@ public static async Task GetValueAsync(this ICache + /// Gets the value associated with the specified key, throwing a when the key is not found. + /// + /// The type of the cache key. + /// The type of the cached value. + /// Cache to retrieve the value from. + /// Cache key to look up. + /// Optional cache region name. When , the default region is used. + /// The cached value. + /// The does not exist in the cache. public static TValue GetValue(this ICache cache, TKey key, string regionName = null) { return cache.GetValue(key, true, regionName); } + /// + /// Asynchronously gets the value associated with the specified key, throwing a when the key is not found. + /// + /// The type of the cache key. + /// The type of the cached value. + /// Cache to retrieve the value from. + /// Cache key to look up. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. The task result contains the cached value. + /// The does not exist in the cache. public static Task GetValueAsync(this ICache cache, TKey key, string regionName = null) { return cache.GetValueAsync(key, true, regionName); } + /// + /// Gets the value associated with the specified key, or returns a default value when the key is not found. + /// + /// The type of the cache key. + /// The type of the cached value. + /// Cache to retrieve the value from. + /// Cache key to look up. + /// Value to return when the key is not found. + /// Optional cache region name. When , the default region is used. + /// The cached value if the key exists; otherwise, . public static TValue GetValueOrDefault(this ICache cache, TKey key, TValue defaultValue, string regionName = null) { return cache.TryGet(key, out var value, regionName) ? value : defaultValue; } + /// + /// Asynchronously gets the value associated with the specified key, or returns a default value when the key is not found. + /// + /// The type of the cache key. + /// The type of the cached value. + /// Cache to retrieve the value from. + /// Cache key to look up. + /// Value to return when the key is not found. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. The task result contains the cached value if the key exists; otherwise, . public static async Task GetValueOrDefaultAsync(this ICache cache, TKey key, TValue defaultValue, string regionName = null) diff --git a/source/Core/CreativeCoders.Core/Caching/CacheRequestResult.cs b/source/Core/CreativeCoders.Core/Caching/CacheRequestResult.cs index 7f23640a..67ee96d2 100644 --- a/source/Core/CreativeCoders.Core/Caching/CacheRequestResult.cs +++ b/source/Core/CreativeCoders.Core/Caching/CacheRequestResult.cs @@ -1,14 +1,29 @@ namespace CreativeCoders.Core.Caching; +/// +/// Represents the result of a cache lookup, indicating whether the entry exists and its value. +/// +/// The type of the cached value. public class CacheRequestResult { + /// + /// Initializes a new instance of the class. + /// + /// Indicates whether the cache entry was found. + /// Cached value, or the default value if the entry does not exist. public CacheRequestResult(bool entryExists, TValue value) { EntryExists = entryExists; Value = value; } + /// + /// Gets a value indicating whether the requested cache entry exists. + /// public bool EntryExists { get; } + /// + /// Gets the cached value. The value is meaningful only when is . + /// public TValue Value { get; } } diff --git a/source/Core/CreativeCoders.Core/Caching/CachedValue.cs b/source/Core/CreativeCoders.Core/Caching/CachedValue.cs index d34e880a..a31c7fb9 100644 --- a/source/Core/CreativeCoders.Core/Caching/CachedValue.cs +++ b/source/Core/CreativeCoders.Core/Caching/CachedValue.cs @@ -4,6 +4,12 @@ namespace CreativeCoders.Core.Caching; +/// +/// Provides a lazily resolved cached value backed by an . The retrieval +/// strategy is determined by the selected at construction time. +/// +/// The type of the cache key. +/// The type of the cached value. [PublicAPI] public class CachedValue : ICachedValue { @@ -21,6 +27,15 @@ public class CachedValue : ICachedValue private readonly CachedValueMode _cachedValueMode; + /// + /// Initializes a new instance of the class that uses the + /// strategy with a factory delegate and expiration policy. + /// + /// Underlying cache to retrieve or store values in. + /// Cache key used for lookup. + /// Factory delegate invoked to produce the value when the key is not found. + /// Expiration policy applied to newly added entries. + /// Optional cache region name. When , the default region is used. public CachedValue(ICache cache, TKey key, Func getValue, ICacheExpirationPolicy expirationPolicy, string regionName = null) { @@ -33,6 +48,13 @@ public CachedValue(ICache cache, TKey key, Func getValue, _regionName = regionName; } + /// + /// Initializes a new instance of the class that uses the + /// strategy, throwing an exception when the key is not found. + /// + /// Underlying cache to retrieve values from. + /// Cache key used for lookup. + /// Optional cache region name. When , the default region is used. public CachedValue(ICache cache, TKey key, string regionName = null) { _cachedValueMode = CachedValueMode.GetValue; @@ -42,6 +64,14 @@ public CachedValue(ICache cache, TKey key, string regionName = nul _regionName = regionName; } + /// + /// Initializes a new instance of the class that uses the + /// strategy, returning a default value when the key is not found. + /// + /// Underlying cache to retrieve values from. + /// Cache key used for lookup. + /// Value returned when the key is not found in the cache. + /// Optional cache region name. When , the default region is used. public CachedValue(ICache cache, TKey key, TValue defaultValue, string regionName = null) { _cachedValueMode = CachedValueMode.GetValueOrDefault; @@ -59,10 +89,11 @@ private TValue GetValue() CachedValueMode.GetOrAdd => _cache.GetOrAdd(_key, _getValue, _expirationPolicy, _regionName), CachedValueMode.GetValue => _cache.GetValue(_key, true, _regionName), CachedValueMode.GetValueOrDefault => _cache.GetValueOrDefault(_key, _defaultValue, _regionName), - _ => throw new ArgumentOutOfRangeException() + _ => throw new ArgumentException("Invalid cached value mode") }; } + /// public Task GetValueAsync() { return _cachedValueMode switch @@ -71,9 +102,10 @@ public Task GetValueAsync() CachedValueMode.GetValue => _cache.GetValueAsync(_key, true, _regionName), CachedValueMode.GetValueOrDefault => _cache.GetValueOrDefaultAsync(_key, _defaultValue, _regionName), - _ => throw new ArgumentOutOfRangeException() + _ => throw new ArgumentException("Invalid cached value mode") }; } + /// public TValue Value => GetValue(); } diff --git a/source/Core/CreativeCoders.Core/Caching/CachedValueMode.cs b/source/Core/CreativeCoders.Core/Caching/CachedValueMode.cs index dca6254f..4e9bd4a9 100644 --- a/source/Core/CreativeCoders.Core/Caching/CachedValueMode.cs +++ b/source/Core/CreativeCoders.Core/Caching/CachedValueMode.cs @@ -1,8 +1,22 @@ namespace CreativeCoders.Core.Caching; +/// +/// Specifies the retrieval strategy used by a instance. +/// public enum CachedValueMode { + /// + /// Gets the cached value or adds it using a factory delegate if the key does not exist. + /// GetOrAdd, + + /// + /// Gets the cached value, throwing an exception if the key does not exist. + /// GetValue, + + /// + /// Gets the cached value, returning a default value if the key does not exist. + /// GetValueOrDefault } diff --git a/source/Core/CreativeCoders.Core/Caching/Default/CacheManager.cs b/source/Core/CreativeCoders.Core/Caching/Default/CacheManager.cs index b9380092..7a63af9e 100644 --- a/source/Core/CreativeCoders.Core/Caching/Default/CacheManager.cs +++ b/source/Core/CreativeCoders.Core/Caching/Default/CacheManager.cs @@ -1,7 +1,16 @@ namespace CreativeCoders.Core.Caching.Default; +/// +/// Provides a static factory for creating default in-memory cache instances. +/// public static class CacheManager { + /// + /// Creates a new instance. + /// + /// The type of the cache key. + /// The type of the cached value. + /// A new backed by an in-memory dictionary. public static ICache CreateCache() { return new DictionaryCache(); diff --git a/source/Core/CreativeCoders.Core/Caching/Default/DictionaryCache.cs b/source/Core/CreativeCoders.Core/Caching/Default/DictionaryCache.cs index bf880a23..5cdc645a 100644 --- a/source/Core/CreativeCoders.Core/Caching/Default/DictionaryCache.cs +++ b/source/Core/CreativeCoders.Core/Caching/Default/DictionaryCache.cs @@ -3,10 +3,16 @@ namespace CreativeCoders.Core.Caching.Default; +/// +/// Provides a dictionary-based in-memory implementation of . +/// +/// The type of the cache key. +/// The type of the cached value. public class DictionaryCache : CacheBase { private readonly CacheRegions _regions = new CacheRegions(); + /// public override TValue GetOrAdd(TKey key, Func getValue, ICacheExpirationPolicy expirationPolicy, string regionName = null) { @@ -21,12 +27,14 @@ public override TValue GetOrAdd(TKey key, Func getValue, ICacheExpiratio return newValue; } + /// public override Task GetOrAddAsync(TKey key, Func getValue, ICacheExpirationPolicy expirationPolicy, string regionName = null) { return Task.FromResult(GetOrAdd(key, getValue, expirationPolicy, regionName)); } + /// public override bool TryGet(TKey key, out TValue value, string regionName = null) { if (!_regions.TryGetValue(key, out var cacheEntry, regionName)) @@ -46,6 +54,7 @@ public override bool TryGet(TKey key, out TValue value, string regionName = null return true; } + /// public override Task> TryGetAsync(TKey key, string regionName = null) { return Task.FromResult( @@ -55,6 +64,7 @@ public override Task> TryGetAsync(TKey key, string re ); } + /// public override void AddOrUpdate(TKey key, TValue value, string regionName = null) { _regions.Set(key, @@ -62,12 +72,14 @@ public override void AddOrUpdate(TKey key, TValue value, string regionName = nul regionName); } + /// public override void AddOrUpdate(TKey key, TValue value, ICacheExpirationPolicy expirationPolicy, string regionName = null) { _regions.Set(key, new CacheEntry(key, expirationPolicy) { Value = value }, regionName); } + /// public override Task AddOrUpdateAsync(TKey key, TValue value, string regionName = null) { AddOrUpdate(key, value, regionName); @@ -75,6 +87,7 @@ public override Task AddOrUpdateAsync(TKey key, TValue value, string regionName return Task.CompletedTask; } + /// public override Task AddOrUpdateAsync(TKey key, TValue value, ICacheExpirationPolicy expirationPolicy, string regionName = null) { @@ -83,22 +96,26 @@ public override Task AddOrUpdateAsync(TKey key, TValue value, ICacheExpirationPo return Task.CompletedTask; } + /// public override void Clear(string regionName = null) { _regions.Clear(regionName); } + /// public override Task ClearAsync(string regionName = null) { Clear(regionName); return Task.CompletedTask; } + /// public override void Remove(TKey key, string regionName = null) { _regions.TryRemove(key, regionName); } + /// public override Task RemoveAsync(TKey key, string regionName = null) { Remove(key, regionName); diff --git a/source/Core/CreativeCoders.Core/Caching/ICache.cs b/source/Core/CreativeCoders.Core/Caching/ICache.cs index 1a8c771d..4eaa1660 100644 --- a/source/Core/CreativeCoders.Core/Caching/ICache.cs +++ b/source/Core/CreativeCoders.Core/Caching/ICache.cs @@ -4,38 +4,134 @@ namespace CreativeCoders.Core.Caching; +/// +/// Defines a key-value cache that supports regions, expiration policies, and asynchronous operations. +/// +/// The type of the cache key. +/// The type of the cached value. [PublicAPI] public interface ICache { + /// + /// Gets the value associated with the specified key, or adds a new value using the provided factory if the key does not exist. + /// + /// Cache key to look up or insert. + /// Factory delegate invoked to produce the value when the key is not found. + /// Optional cache region name. When , the default region is used. + /// The cached or newly added value. TValue GetOrAdd(TKey key, Func getValue, string regionName = null); + /// + /// Gets the value associated with the specified key, or adds a new value with the given expiration policy if the key does not exist. + /// + /// Cache key to look up or insert. + /// Factory delegate invoked to produce the value when the key is not found. + /// Expiration policy applied to the new cache entry. + /// Optional cache region name. When , the default region is used. + /// The cached or newly added value. TValue GetOrAdd(TKey key, Func getValue, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// + /// Asynchronously gets the value associated with the specified key, or adds a new value using the provided factory if the key does not exist. + /// + /// Cache key to look up or insert. + /// Factory delegate invoked to produce the value when the key is not found. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. The task result contains the cached or newly added value. Task GetOrAddAsync(TKey key, Func getValue, string regionName = null); + /// + /// Asynchronously gets the value associated with the specified key, or adds a new value with the given expiration policy if the key does not exist. + /// + /// Cache key to look up or insert. + /// Factory delegate invoked to produce the value when the key is not found. + /// Expiration policy applied to the new cache entry. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. The task result contains the cached or newly added value. Task GetOrAddAsync(TKey key, Func getValue, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// + /// Attempts to get the value associated with the specified key. + /// + /// Cache key to look up. + /// When this method returns, contains the cached value if the key was found; otherwise, the default value. + /// Optional cache region name. When , the default region is used. + /// if the key exists in the cache; otherwise, . bool TryGet(TKey key, out TValue value, string regionName = null); + /// + /// Asynchronously attempts to get the value associated with the specified key. + /// + /// Cache key to look up. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. The task result contains a indicating whether the entry exists and its value. Task> TryGetAsync(TKey key, string regionName = null); + /// + /// Adds a new entry or updates an existing entry in the cache with the specified key and value. + /// + /// Cache key to add or update. + /// Value to store in the cache. + /// Optional cache region name. When , the default region is used. void AddOrUpdate(TKey key, TValue value, string regionName = null); + /// + /// Adds a new entry or updates an existing entry in the cache with the specified key, value, and expiration policy. + /// + /// Cache key to add or update. + /// Value to store in the cache. + /// Expiration policy applied to the cache entry. + /// Optional cache region name. When , the default region is used. void AddOrUpdate(TKey key, TValue value, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// + /// Asynchronously adds a new entry or updates an existing entry in the cache with the specified key and value. + /// + /// Cache key to add or update. + /// Value to store in the cache. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. Task AddOrUpdateAsync(TKey key, TValue value, string regionName = null); + /// + /// Asynchronously adds a new entry or updates an existing entry in the cache with the specified key, value, and expiration policy. + /// + /// Cache key to add or update. + /// Value to store in the cache. + /// Expiration policy applied to the cache entry. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. Task AddOrUpdateAsync(TKey key, TValue value, ICacheExpirationPolicy expirationPolicy, string regionName = null); + /// + /// Removes all entries from the cache, optionally scoped to a specific region. + /// + /// Optional cache region name. When , the default region is cleared. void Clear(string regionName = null); + /// + /// Asynchronously removes all entries from the cache, optionally scoped to a specific region. + /// + /// Optional cache region name. When , the default region is cleared. + /// A task that represents the asynchronous operation. Task ClearAsync(string regionName = null); + /// + /// Removes the entry with the specified key from the cache. + /// + /// Cache key to remove. + /// Optional cache region name. When , the default region is used. void Remove(TKey key, string regionName = null); + /// + /// Asynchronously removes the entry with the specified key from the cache. + /// + /// Cache key to remove. + /// Optional cache region name. When , the default region is used. + /// A task that represents the asynchronous operation. Task RemoveAsync(TKey key, string regionName = null); } diff --git a/source/Core/CreativeCoders.Core/Caching/ICacheEntry.cs b/source/Core/CreativeCoders.Core/Caching/ICacheEntry.cs index cafde480..0be40a04 100644 --- a/source/Core/CreativeCoders.Core/Caching/ICacheEntry.cs +++ b/source/Core/CreativeCoders.Core/Caching/ICacheEntry.cs @@ -2,12 +2,26 @@ namespace CreativeCoders.Core.Caching; +/// +/// Represents a single entry stored in a cache, including its key, value, and expiration policy. +/// +/// The type of the cache key. +/// The type of the cached value. [PublicAPI] public interface ICacheEntry { + /// + /// Gets the key that identifies this cache entry. + /// TKey Key { get; } + /// + /// Gets or sets the value stored in this cache entry. + /// TValue Value { get; set; } + /// + /// Gets the expiration policy governing when this cache entry expires. + /// ICacheExpirationPolicy ExpirationPolicy { get; } } diff --git a/source/Core/CreativeCoders.Core/Caching/ICacheExpirationPolicy.cs b/source/Core/CreativeCoders.Core/Caching/ICacheExpirationPolicy.cs index 03104745..066776a5 100644 --- a/source/Core/CreativeCoders.Core/Caching/ICacheExpirationPolicy.cs +++ b/source/Core/CreativeCoders.Core/Caching/ICacheExpirationPolicy.cs @@ -2,9 +2,18 @@ namespace CreativeCoders.Core.Caching; +/// +/// Defines the expiration behavior for a cache entry. +/// public interface ICacheExpirationPolicy { + /// + /// Gets the expiration mode that determines how the cache entry expires. + /// CacheExpirationMode ExpirationMode { get; } + /// + /// Gets the absolute UTC date and time at which the cache entry expires. + /// DateTime AbsoluteDateTime { get; } } diff --git a/source/Core/CreativeCoders.Core/Caching/ICacheManager.cs b/source/Core/CreativeCoders.Core/Caching/ICacheManager.cs index 380ac5d0..4d4e722c 100644 --- a/source/Core/CreativeCoders.Core/Caching/ICacheManager.cs +++ b/source/Core/CreativeCoders.Core/Caching/ICacheManager.cs @@ -2,8 +2,18 @@ namespace CreativeCoders.Core.Caching; +/// +/// Provides access to named cache instances. +/// [PublicAPI] public interface ICacheManager { + /// + /// Gets or creates a cache instance with the specified name. + /// + /// The type of the cache key. + /// The type of the cached value. + /// Unique name identifying the cache. + /// The instance associated with the given name. ICache GetCache(string name); } diff --git a/source/Core/CreativeCoders.Core/Caching/ICachedValue.cs b/source/Core/CreativeCoders.Core/Caching/ICachedValue.cs index 4a005cc1..d0b0ac22 100644 --- a/source/Core/CreativeCoders.Core/Caching/ICachedValue.cs +++ b/source/Core/CreativeCoders.Core/Caching/ICachedValue.cs @@ -3,10 +3,21 @@ namespace CreativeCoders.Core.Caching; +/// +/// Represents a lazily resolved cached value that retrieves its data from an underlying cache. +/// +/// The type of the cached value. [PublicAPI] public interface ICachedValue { + /// + /// Asynchronously retrieves the cached value. + /// + /// A task that represents the asynchronous operation. The task result contains the cached value. Task GetValueAsync(); + /// + /// Gets the cached value synchronously. + /// TValue Value { get; } } diff --git a/source/Core/CreativeCoders.Core/Collections/CollectionCounter.cs b/source/Core/CreativeCoders.Core/Collections/CollectionCounter.cs index 348b62ab..9fcdd494 100644 --- a/source/Core/CreativeCoders.Core/Collections/CollectionCounter.cs +++ b/source/Core/CreativeCoders.Core/Collections/CollectionCounter.cs @@ -6,11 +6,25 @@ #nullable enable namespace CreativeCoders.Core.Collections; +/// +/// Provides pre-compiled, high-performance counting delegates for collections of type +/// . When exposes a Count property, +/// it is accessed directly via a compiled expression; otherwise, the sequence is enumerated. +/// +/// The collection type, which must implement . public static class CollectionCounter where T : IEnumerable { + /// + /// Gets a delegate that counts the elements in a collection, stopping at the specified maximum. + /// The first parameter is the collection to count; the second parameter is the maximum count + /// (0 means no limit). + /// public static Func CountMax { get; } = GetCountFunc(); + /// + /// Gets a delegate that counts all elements in a collection with no upper limit. + /// public static Func Count { get; } = items => CountMax(items, 0); private static Func GetCountFunc() diff --git a/source/Core/CreativeCoders.Core/Collections/CollectionsExtensions.cs b/source/Core/CreativeCoders.Core/Collections/CollectionsExtensions.cs index 614614ad..e3779557 100644 --- a/source/Core/CreativeCoders.Core/Collections/CollectionsExtensions.cs +++ b/source/Core/CreativeCoders.Core/Collections/CollectionsExtensions.cs @@ -6,21 +6,23 @@ #nullable enable namespace CreativeCoders.Core.Collections; +/// +/// Provides fast counting and emptiness-check extension methods for sequences. +/// [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "")] public static class CollectionsExtensions { - ///------------------------------------------------------------------------------------------------- /// - /// An IEnumerable extension method that do a fast count. The method first tries if - /// is a collection and reads Count if possible. + /// Counts the elements in the source sequence using the fastest available strategy. + /// If implements a known collection interface, the Count + /// property is read directly; otherwise the sequence is enumerated. /// - /// - /// The items to act on. - /// (Optional) Number of maximum count if must - /// be enumerated for counting. - /// - /// The count. - ///------------------------------------------------------------------------------------------------- + /// The source sequence to count. + /// + /// Maximum number of elements to count when the sequence must be enumerated. + /// A value of 0 means no limit. + /// + /// The number of elements in the sequence, capped at when applicable. [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public static int FastCount(this IEnumerable items, int maxCount = 0) { @@ -29,17 +31,17 @@ public static int FastCount(this IEnumerable items, int maxCount = 0) : items.EnumerableCount(maxCount); } - ///------------------------------------------------------------------------------------------------- /// - /// An IEnumerable extension method that fast checks if the items count is in range. + /// Determines whether the number of elements in the source sequence falls within the specified range, + /// using the fastest available counting strategy. /// - /// - /// The items to act on. - /// Number of minimum count. - /// Number of maximum count. - /// - /// True if items count is in range, false otherwise. - ///------------------------------------------------------------------------------------------------- + /// The source sequence to evaluate. + /// The inclusive lower bound of the acceptable count. + /// The exclusive upper bound of the acceptable count. + /// + /// if the element count is greater than or equal to + /// and less than ; otherwise, . + /// [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public static bool FastCountInRange(this IEnumerable items, int minCount, int maxCount) { @@ -82,13 +84,13 @@ private static int EnumerableCount(this IEnumerable items, int maxCount) return count; } - ///------------------------------------------------------------------------------------------------- - /// An IEnumerable extension method that fast empty. - /// - /// The items to act on. - /// - /// True if it succeeds, false if it fails. - ///------------------------------------------------------------------------------------------------- + /// + /// Determines whether the source sequence contains no elements, using the fastest available strategy. + /// + /// The source sequence to evaluate. + /// + /// if the sequence is empty; otherwise, . + /// [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public static bool FastEmpty(this IEnumerable items) { @@ -108,17 +110,19 @@ bool MoveNext() } } - ///------------------------------------------------------------------------------------------------- /// - /// An IEnumerable extension method that attempts to get the collection count from the - /// given IEnumerable. + /// Attempts to retrieve the element count directly from a known collection interface + /// without enumerating the sequence. /// - /// - /// The items to act on. - /// [out] Count in items. - /// - /// True if it Count can be read from a collection, false otherwise. - ///------------------------------------------------------------------------------------------------- + /// The source sequence to inspect. + /// + /// When this method returns , contains the number of elements; + /// otherwise, 0. + /// + /// + /// if the count was obtained from a collection property; + /// otherwise, . + /// public static bool TryGetCollectionCount(this IEnumerable items, out int count) { switch (items) diff --git a/source/Core/CreativeCoders.Core/Collections/DictionaryExtensions.cs b/source/Core/CreativeCoders.Core/Collections/DictionaryExtensions.cs index cfc7b2b6..03a55e6c 100644 --- a/source/Core/CreativeCoders.Core/Collections/DictionaryExtensions.cs +++ b/source/Core/CreativeCoders.Core/Collections/DictionaryExtensions.cs @@ -5,8 +5,23 @@ namespace CreativeCoders.Core.Collections; +/// +/// Provides extension methods for and , +/// including reverse-lookup and typed conversion operations. +/// public static class DictionaryExtensions { + /// + /// Returns the key associated with the specified value in the dictionary. + /// + /// The type of keys in the dictionary. + /// The type of values in the dictionary. + /// The dictionary to search. + /// The value whose associated key is returned. + /// The key associated with . + /// + /// No entry with the specified exists in the dictionary. + /// public static TKey GetKeyByValue(this IDictionary dictionary, TValue value) { if (TryGetKeyByValue(dictionary, value, out var key)) @@ -17,6 +32,20 @@ public static TKey GetKeyByValue(this IDictionary di throw new KeyNotFoundException(); } + /// + /// Attempts to find the key associated with the specified value in the dictionary. + /// + /// The type of keys in the dictionary. + /// The type of values in the dictionary. + /// The dictionary to search. + /// The value whose associated key is returned. + /// + /// When this method returns , contains the key associated with ; + /// otherwise, the default value of . + /// + /// + /// if a matching entry was found; otherwise, . + /// public static bool TryGetKeyByValue(this IDictionary dictionary, TValue value, out TKey key) { @@ -31,6 +60,22 @@ public static bool TryGetKeyByValue(this IDictionary return false; } + /// + /// Converts a non-generic to a strongly-typed + /// by casting each key and value. + /// + /// The target key type. + /// The target value type. + /// The non-generic dictionary to convert. + /// + /// to silently skip entries whose key or value cannot be cast; + /// otherwise, to throw an . + /// + /// A new strongly-typed dictionary containing the converted entries. + /// + /// is and an entry's key or value + /// cannot be cast to the target type. + /// public static IDictionary ToDictionary(this IDictionary dictionary, bool skipNotMatchingEntries) { @@ -64,6 +109,24 @@ public static IDictionary ToDictionary(this IDiction return convertedDictionary; } + /// + /// Converts a non-generic to a strongly-typed + /// by casting each key and transforming each value + /// with the specified selector. + /// + /// The target key type. + /// The target value type. + /// The non-generic dictionary to convert. + /// The function that transforms each raw value into . + /// + /// to silently skip entries whose key cannot be cast; + /// otherwise, to throw an . + /// + /// A new strongly-typed dictionary containing the converted entries. + /// + /// is and an entry's key + /// cannot be cast to . + /// public static IDictionary ToDictionary(this IDictionary dictionary, Func valueSelector, bool skipNotMatchingEntries) { diff --git a/source/Core/CreativeCoders.Core/Collections/EnumerableExtension.cs b/source/Core/CreativeCoders.Core/Collections/EnumerableExtension.cs index 6ccdf342..ea5dbe54 100644 --- a/source/Core/CreativeCoders.Core/Collections/EnumerableExtension.cs +++ b/source/Core/CreativeCoders.Core/Collections/EnumerableExtension.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -9,9 +9,23 @@ namespace CreativeCoders.Core.Collections; +/// +/// Provides extension methods for and +/// sequences, including iteration, filtering, projection, sorting, and duplicate detection. +/// [PublicAPI] public static class EnumerableExtension { + /// + /// Returns the single element of the source sequence, or the default value if the sequence + /// contains zero elements or more than one element. + /// + /// The type of elements in the sequence. + /// The source sequence to evaluate. + /// + /// The single element when the sequence contains exactly one element; + /// otherwise, the default value of . + /// public static T OnlySingleOrDefault(this IEnumerable items) { using var enumerator = items.GetEnumerator(); @@ -28,6 +42,12 @@ public static T OnlySingleOrDefault(this IEnumerable items) : firstElement; } + /// + /// Executes the specified action on each element of the source sequence. + /// + /// The type of elements in the sequence. + /// The source sequence to iterate. + /// The action to execute on each element. public static void ForEach(this IEnumerable self, Action action) { Ensure.IsNotNull(action); @@ -38,6 +58,13 @@ public static void ForEach(this IEnumerable self, Action action) } } + /// + /// Executes the specified action on each element of the non-generic source sequence + /// that can be cast to . Elements that cannot be cast are skipped. + /// + /// The target type to which elements are cast before invoking the action. + /// The non-generic source sequence to iterate. + /// The action to execute on each matching element. public static void ForEach(this IEnumerable self, Action action) { Ensure.IsNotNull(action); @@ -51,6 +78,13 @@ public static void ForEach(this IEnumerable self, Action action) } } + /// + /// Executes the specified action on each element of the source sequence, + /// providing the zero-based index of each element. + /// + /// The type of elements in the sequence. + /// The source sequence to iterate. + /// The action to execute, receiving the element and its zero-based index. public static void ForEach(this IEnumerable source, Action action) { Ensure.IsNotNull(action); @@ -62,6 +96,12 @@ public static void ForEach(this IEnumerable source, Action action) } } + /// + /// Executes the specified action on each element of the non-generic source sequence, + /// providing the zero-based index of each element. + /// + /// The non-generic source sequence to iterate. + /// The action to execute, receiving the element and its zero-based index. public static void ForEach(this IEnumerable source, Action action) { Ensure.IsNotNull(action); @@ -73,6 +113,13 @@ public static void ForEach(this IEnumerable source, Action action) } } + /// + /// Executes the specified action on each element of the array that can be cast to + /// . Elements that cannot be cast are skipped. + /// + /// The target type to which elements are cast before invoking the action. + /// The source array to iterate. + /// The action to execute on each matching element. public static void ForEach(this Array self, Action action) { Ensure.IsNotNull(action); @@ -80,6 +127,14 @@ public static void ForEach(this Array self, Action action) (self as IEnumerable).ForEach(action); } + /// + /// Asynchronously executes the specified function on each element of the source sequence, awaiting + /// each invocation sequentially. + /// + /// The type of elements in the sequence. + /// The source sequence to iterate. + /// The asynchronous function to execute on each element. + /// A that completes when all elements have been processed. public static async Task ForEachAsync(this IEnumerable self, Func actionAsync) { Ensure.IsNotNull(actionAsync); @@ -90,6 +145,14 @@ public static async Task ForEachAsync(this IEnumerable self, Func } } + /// + /// Asynchronously executes the specified function on each element of the source sequence, + /// providing the zero-based index and awaiting each invocation sequentially. + /// + /// The type of elements in the sequence. + /// The source sequence to iterate. + /// The asynchronous function to execute, receiving the element and its zero-based index. + /// A that completes when all elements have been processed. public static async Task ForEachAsync(this IEnumerable source, Func actionAsync) { Ensure.IsNotNull(actionAsync); @@ -101,6 +164,14 @@ public static async Task ForEachAsync(this IEnumerable source, Func + /// Executes a side-effect action on each element as it passes through the sequence, + /// yielding every element unchanged. The action is invoked lazily during enumeration. + /// + /// The type of elements in the sequence. + /// The source sequence to observe. + /// The action to execute on each element for side effects. + /// A sequence containing all elements of in their original order. public static IEnumerable Pipe(this IEnumerable items, Action pipeAction) { Ensure.IsNotNull(pipeAction); @@ -117,6 +188,20 @@ IEnumerable PipeCore() } } + /// + /// Returns elements from the source sequence until the predicate is satisfied. + /// The element that satisfies the predicate is included in the result. + /// + /// The type of elements in the sequence. + /// The source sequence to take from. + /// + /// The function that tests each element. + /// Enumeration stops after the first element for which this returns . + /// + /// + /// A sequence containing elements up to and including the first element that satisfies + /// . + /// public static IEnumerable TakeUntil(this IEnumerable items, Func predicate) { Ensure.IsNotNull(predicate); @@ -137,6 +222,17 @@ IEnumerable TakeUntilCore() } } + /// + /// Skips elements from the source sequence until the predicate is satisfied, + /// then yields all remaining elements. The element that satisfies the predicate is not included. + /// + /// The type of elements in the sequence. + /// The source sequence to skip from. + /// + /// The function that tests each element. + /// Elements are skipped until this returns ; subsequent elements are yielded. + /// + /// A sequence of elements following the first element that satisfies . public static IEnumerable SkipUntil(this IEnumerable items, Func predicate) { Ensure.IsNotNull(predicate); @@ -164,11 +260,25 @@ IEnumerable SkipUntilCore() } } + /// + /// Returns every -th element from the source sequence, + /// starting with the first element (index 0). + /// + /// The type of elements in the sequence. + /// The source sequence. + /// The interval between selected elements. + /// A sequence containing every -th element. public static IEnumerable TakeEvery(this IEnumerable items, int step) { return items.Where((_, index) => index % step == 0); } + /// + /// Removes all elements in from the source collection. + /// + /// The type of elements in the collection. + /// The collection from which elements are removed. + /// The elements to remove. public static void Remove(this ICollection self, IEnumerable removeEntries) { Ensure.IsNotNull(removeEntries); @@ -179,6 +289,12 @@ public static void Remove(this ICollection self, IEnumerable removeEntr } } + /// + /// Removes all elements that match the specified predicate from the source collection. + /// + /// The type of elements in the collection. + /// The collection from which elements are removed. + /// The condition that determines which elements to remove. public static void Remove(this ICollection self, Predicate predicate) { Ensure.IsNotNull(predicate); @@ -187,12 +303,28 @@ public static void Remove(this ICollection self, Predicate predicate) self.Remove(removeEntries); } + /// + /// Filters out elements from the source sequence. + /// + /// The type of elements in the sequence. + /// The source sequence to filter. + /// A sequence containing only the non- elements. public static IEnumerable WhereNotNull(this IEnumerable self) where T : class { return self.Where(item => item != null); } + /// + /// Determines whether the source sequence contains exactly one element that satisfies the predicate. + /// + /// The type of elements in the sequence. + /// The source sequence to evaluate. + /// The condition to test each element against. + /// + /// if exactly one element satisfies ; + /// otherwise, . + /// public static bool IsSingle(this IEnumerable items, Func predicate) { Ensure.IsNotNull(predicate); @@ -212,22 +344,61 @@ public static bool IsSingle(this IEnumerable items, Func predicat return num == 1; } + /// + /// Determines whether the source sequence contains exactly one element. + /// + /// The type of elements in the sequence. + /// The source sequence to evaluate. + /// + /// if the sequence contains exactly one element; + /// otherwise, . + /// public static bool IsSingle(this IEnumerable items) { return items.IsSingle(_ => true); } + /// + /// Returns distinct elements from the source sequence by comparing a single key. + /// + /// The type of elements in the sequence. + /// The type of the key used for equality comparison. + /// The source sequence. + /// The function that extracts the comparison key from each element. + /// A sequence of distinct elements based on the selected key. public static IEnumerable Distinct(this IEnumerable items, Func keySelector) { return items.Distinct(new FuncEqualityComparer(keySelector)); } + /// + /// Returns distinct elements from the source sequence by comparing two keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The source sequence. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// A sequence of distinct elements based on the selected keys. public static IEnumerable Distinct(this IEnumerable items, Func keySelector1, Func keySelector2) { return items.Distinct(new MultiFuncEqualityComparer(keySelector1, keySelector2)); } + /// + /// Returns distinct elements from the source sequence by comparing three keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The type of the third comparison key. + /// The source sequence. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// The function that extracts the third comparison key. + /// A sequence of distinct elements based on the selected keys. public static IEnumerable Distinct(this IEnumerable items, Func keySelector1, Func keySelector2, Func keySelector3) { @@ -236,6 +407,20 @@ public static IEnumerable Distinct(this IEnumerable + /// Returns distinct elements from the source sequence by comparing four keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The type of the third comparison key. + /// The type of the fourth comparison key. + /// The source sequence. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// The function that extracts the third comparison key. + /// The function that extracts the fourth comparison key. + /// A sequence of distinct elements based on the selected keys. public static IEnumerable Distinct(this IEnumerable items, Func keySelector1, Func keySelector2, Func keySelector3, Func keySelector4) @@ -245,6 +430,22 @@ public static IEnumerable Distinct(this IEnume keySelector3, keySelector4)); } + /// + /// Returns distinct elements from the source sequence by comparing five keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The type of the third comparison key. + /// The type of the fourth comparison key. + /// The type of the fifth comparison key. + /// The source sequence. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// The function that extracts the third comparison key. + /// The function that extracts the fourth comparison key. + /// The function that extracts the fifth comparison key. + /// A sequence of distinct elements based on the selected keys. public static IEnumerable Distinct(this IEnumerable items, Func keySelector1, Func keySelector2, Func keySelector3, Func keySelector4, Func keySelector5) @@ -254,22 +455,56 @@ public static IEnumerable Distinct(this keySelector3, keySelector4, keySelector5)); } + /// + /// Returns distinct elements from the source sequence by comparing an arbitrary number + /// of keys of the same type. + /// + /// The type of elements in the sequence. + /// The type of each comparison key. + /// The source sequence. + /// The functions that extract comparison keys from each element. + /// A sequence of distinct elements based on the selected keys. public static IEnumerable Distinct(this IEnumerable items, params Func[] keySelectors) { return items.Distinct(new MultiFuncEqualityComparer(keySelectors)); } + /// + /// Returns all duplicate elements from the source sequence, compared using the default + /// equality comparer for . + /// + /// The type of elements in the sequence. + /// The source sequence to evaluate. + /// A sequence containing all elements that appear more than once. public static IEnumerable NotDistinct(this IEnumerable items) { return items.NotDistinct(x => x); } + /// + /// Returns all duplicate elements from the source sequence, compared by a single key. + /// + /// The type of elements in the sequence. + /// The type of the key used for equality comparison. + /// The source sequence to evaluate. + /// The function that extracts the comparison key from each element. + /// A sequence containing all elements whose key appears more than once. public static IEnumerable NotDistinct(this IEnumerable items, Func keySelector) { return items.NotDistinct(new FuncEqualityComparer(keySelector)); } + /// + /// Returns all duplicate elements from the source sequence, compared by two keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The source sequence to evaluate. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// A sequence containing all elements whose combined keys appear more than once. public static IEnumerable NotDistinct(this IEnumerable items, Func keySelector1, Func keySelector2) { @@ -277,6 +512,18 @@ public static IEnumerable NotDistinct(this IEnumerable it new MultiFuncEqualityComparer(keySelector1, keySelector2)); } + /// + /// Returns all duplicate elements from the source sequence, compared by three keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The type of the third comparison key. + /// The source sequence to evaluate. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// The function that extracts the third comparison key. + /// A sequence containing all elements whose combined keys appear more than once. public static IEnumerable NotDistinct(this IEnumerable items, Func keySelector1, Func keySelector2, Func keySelector3) { @@ -285,6 +532,20 @@ public static IEnumerable NotDistinct(this IEnumerabl keySelector3)); } + /// + /// Returns all duplicate elements from the source sequence, compared by four keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The type of the third comparison key. + /// The type of the fourth comparison key. + /// The source sequence to evaluate. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// The function that extracts the third comparison key. + /// The function that extracts the fourth comparison key. + /// A sequence containing all elements whose combined keys appear more than once. public static IEnumerable NotDistinct(this IEnumerable items, Func keySelector1, Func keySelector2, Func keySelector3, Func keySelector4) @@ -294,6 +555,22 @@ public static IEnumerable NotDistinct(this IEn keySelector3, keySelector4)); } + /// + /// Returns all duplicate elements from the source sequence, compared by five keys. + /// + /// The type of elements in the sequence. + /// The type of the first comparison key. + /// The type of the second comparison key. + /// The type of the third comparison key. + /// The type of the fourth comparison key. + /// The type of the fifth comparison key. + /// The source sequence to evaluate. + /// The function that extracts the first comparison key. + /// The function that extracts the second comparison key. + /// The function that extracts the third comparison key. + /// The function that extracts the fourth comparison key. + /// The function that extracts the fifth comparison key. + /// A sequence containing all elements whose combined keys appear more than once. public static IEnumerable NotDistinct(this IEnumerable items, Func keySelector1, Func keySelector2, Func keySelector3, Func keySelector4, Func keySelector5) @@ -303,12 +580,28 @@ public static IEnumerable NotDistinct(t keySelector3, keySelector4, keySelector5)); } + /// + /// Returns all duplicate elements from the source sequence, compared by an arbitrary number + /// of keys of the same type. + /// + /// The type of elements in the sequence. + /// The type of each comparison key. + /// The source sequence to evaluate. + /// The functions that extract comparison keys from each element. + /// A sequence containing all elements whose combined keys appear more than once. public static IEnumerable NotDistinct(this IEnumerable items, params Func[] keySelectors) { return items.NotDistinct(new MultiFuncEqualityComparer(keySelectors)); } + /// + /// Returns all duplicate elements from the source sequence, using the specified equality comparer. + /// + /// The type of elements in the sequence. + /// The source sequence to evaluate. + /// The equality comparer used to detect duplicates. + /// A sequence containing all elements that appear more than once according to . [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public static IEnumerable NotDistinct(this IEnumerable items, IEqualityComparer comparer) { @@ -344,11 +637,28 @@ IEnumerable NotDistinctCore() } } + /// + /// Projects each element of the source sequence into an + /// that pairs the element with its zero-based index. + /// + /// The type of elements in the sequence. + /// The source sequence to project. + /// A sequence of instances. public static IEnumerable> SelectWithIndex(this IEnumerable items) { return items.Select((x, index) => new ItemWithIndex(index, x)); } + /// + /// Sorts the source sequence in ascending order by two keys. + /// + /// The type of elements in the sequence. + /// The type of the first sort key. + /// The type of the second sort key. + /// The source sequence to sort. + /// The function that extracts the first sort key. + /// The function that extracts the second sort key. + /// An ordered sequence sorted in ascending order by both keys. public static IOrderedEnumerable OrderBy(this IEnumerable items, Func keySelector1, Func keySelector2) @@ -357,6 +667,16 @@ public static IOrderedEnumerable OrderBy(this IEnumerable new SortFieldInfo(keySelector2, SortOrder.Ascending)); } + /// + /// Sorts the source sequence in descending order by two keys. + /// + /// The type of elements in the sequence. + /// The type of the first sort key. + /// The type of the second sort key. + /// The source sequence to sort. + /// The function that extracts the first sort key. + /// The function that extracts the second sort key. + /// An ordered sequence sorted in descending order by both keys. public static IOrderedEnumerable OrderByDescending(this IEnumerable items, Func keySelector1, Func keySelector2) { @@ -364,12 +684,34 @@ public static IOrderedEnumerable OrderByDescending(this IEnu new SortFieldInfo(keySelector2, SortOrder.Descending)); } + /// + /// Sorts the source sequence using two sort field descriptors that each specify a key and sort order. + /// + /// The type of elements in the sequence. + /// The type of the first sort key. + /// The type of the second sort key. + /// The source sequence to sort. + /// The first sort field descriptor. + /// The second sort field descriptor. + /// An ordered sequence sorted according to the specified sort field descriptors. public static IOrderedEnumerable Sort(this IEnumerable items, SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2) { return items.OrderBy(x => x, new MultiFuncComparer(sortFieldInfo1, sortFieldInfo2)); } + /// + /// Sorts the source sequence using three sort field descriptors that each specify a key and sort order. + /// + /// The type of elements in the sequence. + /// The type of the first sort key. + /// The type of the second sort key. + /// The type of the third sort key. + /// The source sequence to sort. + /// The first sort field descriptor. + /// The second sort field descriptor. + /// The third sort field descriptor. + /// An ordered sequence sorted according to the specified sort field descriptors. public static IOrderedEnumerable Sort(this IEnumerable items, SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2, SortFieldInfo sortFieldInfo3) @@ -378,6 +720,20 @@ public static IOrderedEnumerable Sort(this IEnumerabl new MultiFuncComparer(sortFieldInfo1, sortFieldInfo2, sortFieldInfo3)); } + /// + /// Sorts the source sequence using four sort field descriptors that each specify a key and sort order. + /// + /// The type of elements in the sequence. + /// The type of the first sort key. + /// The type of the second sort key. + /// The type of the third sort key. + /// The type of the fourth sort key. + /// The source sequence to sort. + /// The first sort field descriptor. + /// The second sort field descriptor. + /// The third sort field descriptor. + /// The fourth sort field descriptor. + /// An ordered sequence sorted according to the specified sort field descriptors. public static IOrderedEnumerable Sort(this IEnumerable items, SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2, SortFieldInfo sortFieldInfo3, SortFieldInfo sortFieldInfo4) @@ -388,6 +744,22 @@ public static IOrderedEnumerable Sort(this IEn sortFieldInfo4)); } + /// + /// Sorts the source sequence using five sort field descriptors that each specify a key and sort order. + /// + /// The type of elements in the sequence. + /// The type of the first sort key. + /// The type of the second sort key. + /// The type of the third sort key. + /// The type of the fourth sort key. + /// The type of the fifth sort key. + /// The source sequence to sort. + /// The first sort field descriptor. + /// The second sort field descriptor. + /// The third sort field descriptor. + /// The fourth sort field descriptor. + /// The fifth sort field descriptor. + /// An ordered sequence sorted according to the specified sort field descriptors. public static IOrderedEnumerable Sort(this IEnumerable items, SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2, SortFieldInfo sortFieldInfo3, SortFieldInfo sortFieldInfo4, @@ -398,6 +770,19 @@ public static IOrderedEnumerable Sort(t sortFieldInfo3, sortFieldInfo4, sortFieldInfo5)); } + /// + /// Filters and transforms elements from the source sequence in a single pass. + /// Each element is evaluated by , and only elements for which + /// the function indicates selection are projected into the result. + /// + /// The type of elements in the source sequence. + /// The type of elements in the resulting sequence. + /// The source sequence to filter and transform. + /// + /// The function that evaluates each element, returning a tuple whose IsChoosen flag indicates + /// selection and whose Value contains the projected result. + /// + /// A sequence of projected values for the chosen elements. public static IEnumerable Choose(this IEnumerable items, Func choose) { @@ -410,6 +795,19 @@ public static IEnumerable Choose(this IEnumerable items, } #nullable enable + /// + /// Filters the elements of the non-generic source sequence by the specified runtime + /// , using reflection to invoke . + /// + /// The non-generic source sequence to filter. + /// The runtime type to filter elements by. + /// + /// An containing only the elements of , + /// or if the method cannot be resolved. + /// + /// + /// The method cannot be found via reflection. + /// public static object? OfType(this IEnumerable source, Type itemType) { var ofTypeMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.OfType)) diff --git a/source/Core/CreativeCoders.Core/Collections/ExtendedObservableCollection.cs b/source/Core/CreativeCoders.Core/Collections/ExtendedObservableCollection.cs index 5c135d86..1a78109c 100644 --- a/source/Core/CreativeCoders.Core/Collections/ExtendedObservableCollection.cs +++ b/source/Core/CreativeCoders.Core/Collections/ExtendedObservableCollection.cs @@ -14,6 +14,13 @@ namespace CreativeCoders.Core.Collections; +/// +/// A thread-safe observable collection that implements , +/// , , +/// and . Supports batch updates, item movement, +/// and configurable synchronization context dispatch. +/// +/// The type of elements in the collection. [PublicAPI] public class ExtendedObservableCollection : IList, IReadOnlyList, INotifyPropertyChanged, INotifyCollectionChanged @@ -34,18 +41,51 @@ public class ExtendedObservableCollection : IList, IReadOnlyList, INoti private readonly SynchronizedValue _updateCounter; + /// + /// Initializes a new instance of the class + /// that is empty, uses the current synchronization context with , + /// and a . + /// public ExtendedObservableCollection() : this(SynchronizationContext.Current, SynchronizationMethod.Send, () => new LockSlimLockingMechanism(), []) { } + /// + /// Initializes a new instance of the class + /// that contains the specified items, uses the current synchronization context with + /// , and a . + /// + /// The initial elements to populate the collection with. public ExtendedObservableCollection(IEnumerable items) : this(SynchronizationContext.Current, SynchronizationMethod.Send, () => new LockSlimLockingMechanism(), items) { } + /// + /// Initializes a new instance of the class + /// that is empty, using the specified synchronization context, method, and locking mechanism. + /// + /// + /// The synchronization context for dispatching change notifications, + /// or for no synchronization. + /// + /// The method used to dispatch notifications. + /// The factory that creates the locking mechanism for thread safety. public ExtendedObservableCollection(SynchronizationContext? synchronizationContext, SynchronizationMethod synchronizationMethod, Func lockingMechanism) : this(synchronizationContext, synchronizationMethod, lockingMechanism, []) { } + /// + /// Initializes a new instance of the class + /// that contains the specified items, using the specified synchronization context, method, + /// and locking mechanism. + /// + /// + /// The synchronization context for dispatching change notifications, + /// or for no synchronization. + /// + /// The method used to dispatch notifications. + /// The factory that creates the locking mechanism for thread safety. + /// The initial elements to populate the collection with. public ExtendedObservableCollection(SynchronizationContext? synchronizationContext, SynchronizationMethod synchronizationMethod, Func lockingMechanism, IEnumerable items) @@ -65,6 +105,11 @@ public ExtendedObservableCollection(SynchronizationContext? synchronizationConte _reentrancyMonitor = new SimpleMonitor(); } + /// + /// Adds the elements of the specified sequence to the end of the collection + /// and raises a reset notification. + /// + /// The elements to add. public void AddRange(IEnumerable items) { _lockingMechanism.Write(() => @@ -77,6 +122,11 @@ public void AddRange(IEnumerable items) NotifyItemsReset(); } + /// + /// Moves an element from one position to another within the collection. + /// + /// The zero-based index of the element to move. + /// The zero-based destination index. public void Move(int oldIndex, int newIndex) { var movedItem = _lockingMechanism.Write(() => @@ -107,6 +157,11 @@ private void NotifyItemMoved(T item, int oldIndex, int newIndex) }); } + /// + /// Begins a batch update scope. Change notifications are deferred until the returned + /// is disposed. + /// + /// An that ends the batch update when disposed. public IDisposable Update() { BeginUpdate(); @@ -114,8 +169,17 @@ public IDisposable Update() return new DelegateDisposable(EndUpdate, true); } + /// + /// Begins a batch update, deferring change notifications until is called. + /// Multiple calls can be nested; notifications resume only when every + /// has a matching . + /// public void BeginUpdate() => _updateCounter.SetValue(x => x + 1); + /// + /// Ends a batch update previously started with . If this is the + /// outermost update and the collection changed during the batch, a reset notification is raised. + /// [SuppressMessage("csharpsquid", "S2583", Justification = "Notify is set inside a lambda expression")] public void EndUpdate() { @@ -253,12 +317,21 @@ private void NotifyItemsReset() }); } + /// + /// Raises the event. + /// + /// The name of the property that changed. [NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + /// + /// Raises the event, or defers the notification + /// when a batch update is in progress. + /// + /// The event arguments describing the change. protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (_updateCounter.Value > 0) @@ -270,14 +343,17 @@ protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) CollectionChanged?.Invoke(this, e); } + /// [MustDisposeResource] public IEnumerator GetEnumerator() => _lockingMechanism.Read([MustDisposeResource]() => _items.ToList().GetEnumerator()); + /// [MustDisposeResource] IEnumerator IEnumerable.GetEnumerator() => _lockingMechanism.Read([MustDisposeResource]() => _items.ToList().GetEnumerator()); + /// public void Add(T item) { var index = _lockingMechanism.Write(() => @@ -292,6 +368,7 @@ public void Add(T item) NotifyItemAdded(item, index); } + /// public void Clear() { var itemsCleared = _lockingMechanism.Write(() => @@ -314,13 +391,16 @@ public void Clear() } } + /// public bool Contains(T item) => _lockingMechanism.Read(() => _items.Contains(item)); + /// public void CopyTo(T[] array, int arrayIndex) { _lockingMechanism.Read(() => Array.Copy(_items.ToArray(), 0, array, arrayIndex, _items.Count)); } + /// public bool Remove(T item) { var isRemoved = _lockingMechanism.Write(() => @@ -340,8 +420,10 @@ public bool Remove(T item) return isRemoved; } + /// public int IndexOf(T item) => _lockingMechanism.Read(() => _items.IndexOf(item)); + /// public void Insert(int index, T item) { _lockingMechanism.Write(() => @@ -354,6 +436,7 @@ public void Insert(int index, T item) NotifyItemAdded(item, index); } + /// public void RemoveAt(int index) { var removedItem = _lockingMechanism.Write(() => @@ -372,16 +455,23 @@ public void RemoveAt(int index) NotifyItemRemoved(removedItem, index); } + /// public int Count => _lockingMechanism.Read(() => _items.Count); + /// public bool IsReadOnly => false; + /// + /// Gets or sets the total number of elements the internal data structure can hold + /// without resizing. + /// public int Capacity { get => _lockingMechanism.Read(() => _items.Capacity); set => _lockingMechanism.Write(() => _items.Capacity = value); } + /// public T this[int index] { get => _lockingMechanism.Read(() => _items[index]); @@ -404,7 +494,9 @@ public T this[int index] } } + /// public event PropertyChangedEventHandler? PropertyChanged; + /// public event NotifyCollectionChangedEventHandler? CollectionChanged; } diff --git a/source/Core/CreativeCoders.Core/Collections/ItemWithIndex.cs b/source/Core/CreativeCoders.Core/Collections/ItemWithIndex.cs index 7997856b..c924233b 100644 --- a/source/Core/CreativeCoders.Core/Collections/ItemWithIndex.cs +++ b/source/Core/CreativeCoders.Core/Collections/ItemWithIndex.cs @@ -2,15 +2,31 @@ namespace CreativeCoders.Core.Collections; +/// +/// Pairs an element of type with its zero-based index +/// within a sequence. +/// +/// The type of the element. public class ItemWithIndex { + /// + /// Initializes a new instance of the class. + /// + /// The zero-based position of the element in the source sequence. + /// The element value. public ItemWithIndex(int index, T data) { Index = index; Data = data; } + /// + /// Gets the zero-based position of the element in the source sequence. + /// public int Index { get; } + /// + /// Gets the element value. + /// public T Data { get; } } diff --git a/source/Core/CreativeCoders.Core/Collections/ListExtensions.cs b/source/Core/CreativeCoders.Core/Collections/ListExtensions.cs index 564d18ff..2eb1deb3 100644 --- a/source/Core/CreativeCoders.Core/Collections/ListExtensions.cs +++ b/source/Core/CreativeCoders.Core/Collections/ListExtensions.cs @@ -5,30 +5,54 @@ namespace CreativeCoders.Core.Collections; +/// +/// Provides extension methods for , including bulk-add and replacement operations. +/// [PublicAPI] public static class ListExtensions { + /// + /// Adds all elements from the specified sequence to the end of the list. + /// + /// The type of elements in the list. + /// The target list to add elements to. + /// The elements to add. + /// The same instance, for chaining. public static IList AddRange(this IList list, IEnumerable items) { - Ensure.IsNotNull(items, nameof(items)); + Ensure.IsNotNull(items); items.ForEach(list.Add); return list; } + /// + /// Adds the specified elements to the end of the list. + /// + /// The type of elements in the list. + /// The target list to add elements to. + /// The elements to add. + /// The same instance, for chaining. public static IList AddRange(this IList list, params T[] items) { - Ensure.IsNotNull(items, nameof(items)); + Ensure.IsNotNull(items); items.ForEach(list.Add); return list; } + /// + /// Replaces all elements in the list with the elements from the specified sequence. + /// The list is cleared before the new elements are added. + /// + /// The type of elements in the list. + /// The target list whose contents are replaced. + /// The new elements to populate the list with. public static void SetItems(this IList list, IEnumerable items) { - Ensure.IsNotNull(items, nameof(items)); + Ensure.IsNotNull(items); list.Clear(); list.AddRange(items); diff --git a/source/Core/CreativeCoders.Core/Collections/ObservableCollectionSynchronizer.cs b/source/Core/CreativeCoders.Core/Collections/ObservableCollectionSynchronizer.cs index ac1cc148..37173ac6 100644 --- a/source/Core/CreativeCoders.Core/Collections/ObservableCollectionSynchronizer.cs +++ b/source/Core/CreativeCoders.Core/Collections/ObservableCollectionSynchronizer.cs @@ -10,6 +10,13 @@ namespace CreativeCoders.Core.Collections; +/// +/// Keeps a replica synchronized with a source +/// by subscribing to +/// and mirroring all changes. +/// +/// The type of elements in the source collection. +/// The type of elements in the replica collection. [PublicAPI] public sealed class ObservableCollectionSynchronizer : IDisposable { @@ -21,6 +28,20 @@ public sealed class ObservableCollectionSynchronizer _getReplicaItemForSourceItem; + /// + /// Initializes a new instance of the + /// class, + /// populates the replica with existing source elements, and begins listening for source changes. + /// + /// The source collection whose changes are mirrored. + /// The replica collection that is kept in sync. + /// + /// The factory that creates a new replica element for a given source element. + /// + /// + /// The function that retrieves the existing replica element corresponding to a given source element, + /// used during remove operations. + /// public ObservableCollectionSynchronizer(ObservableCollection sourceCollection, ObservableCollection replicaCollection, Func createReplicaItemForSourceItem, @@ -105,6 +126,9 @@ private void RemoveElements(int oldStartingIndex, IEnumerable so } } + /// + /// Unsubscribes from the source collection's change notifications, stopping synchronization. + /// public void Dispose() { _sourceCollection.CollectionChanged -= SourceCollectionOnCollectionChanged; diff --git a/source/Core/CreativeCoders.Core/Comparing/ComparableObject.cs b/source/Core/CreativeCoders.Core/Comparing/ComparableObject.cs index 3b1a34cf..8c8c1a13 100644 --- a/source/Core/CreativeCoders.Core/Comparing/ComparableObject.cs +++ b/source/Core/CreativeCoders.Core/Comparing/ComparableObject.cs @@ -5,6 +5,10 @@ #nullable enable namespace CreativeCoders.Core.Comparing; +/// +/// Provides a base class for objects that support equality and comparison based on a configurable property. +/// +/// The derived type that extends this base class. public class ComparableObject : IEquatable, IComparable where T : ComparableObject { @@ -15,6 +19,11 @@ public class ComparableObject : IEquatable, IComparable private static Func __getHashCodeFunc = RuntimeHelpers.GetHashCode; + /// + /// Initializes the equality comparer, comparer, and hash code function based on the specified property selector. + /// + /// The type of the property used for comparison. + /// Function that selects the property to use for comparison and equality. protected static void InitComparableObject(Func getCompareProperty) { __equalityComparer = new FuncEqualityComparer(getCompareProperty); @@ -24,25 +33,35 @@ protected static void InitComparableObject(Func getComp __getHashCodeFunc = x => __equalityComparer.GetHashCode(x); } + /// public override bool Equals(object? obj) => Equals(obj as T); + /// public override int GetHashCode() { // ReSharper disable once NonReadonlyMemberInGetHashCode return __getHashCodeFunc((T)this); } + /// public int CompareTo(T? other) { return __comparer.Compare((T)this, other); } + /// public bool Equals(T? other) { return __equalityComparer.Equals((T)this, other); } } +/// +/// Provides a base class for objects that support equality and comparison through an interface type, +/// based on a configurable property. +/// +/// The concrete derived type that extends this base class and implements . +/// The interface type used for equality and comparison operations. public class ComparableObject : IEquatable, IComparable where TObject : ComparableObject, TInterface where TInterface : class @@ -54,6 +73,11 @@ public class ComparableObject : IEquatable, ICo private static Func __getHashCodeFunc = RuntimeHelpers.GetHashCode; + /// + /// Initializes the equality comparer, comparer, and hash code function based on the specified property selector. + /// + /// The type of the property used for comparison. + /// Function that selects the property to use for comparison and equality. protected static void InitComparableObject(Func getCompareProperty) { __equalityComparer = new FuncEqualityComparer(getCompareProperty); @@ -63,19 +87,23 @@ protected static void InitComparableObject(Func __equalityComparer.GetHashCode(x); } + /// public override bool Equals(object? obj) => Equals(obj as TInterface); + /// public override int GetHashCode() { // ReSharper disable once NonReadonlyMemberInGetHashCode return __getHashCodeFunc((TInterface)(object)this); } + /// public int CompareTo(TInterface? other) { return __comparer.Compare((TInterface)(object)this, other); } + /// public bool Equals(TInterface? other) { return __equalityComparer.Equals((TInterface)(object)this, other); diff --git a/source/Core/CreativeCoders.Core/Comparing/DelegateEqualityComparer.cs b/source/Core/CreativeCoders.Core/Comparing/DelegateEqualityComparer.cs index 5851d805..5a151749 100644 --- a/source/Core/CreativeCoders.Core/Comparing/DelegateEqualityComparer.cs +++ b/source/Core/CreativeCoders.Core/Comparing/DelegateEqualityComparer.cs @@ -4,6 +4,10 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Implements using delegate functions for equality comparison and hash code generation. +/// +/// The type of objects to compare. [PublicAPI] public class DelegateEqualityComparer : IEqualityComparer { @@ -11,21 +15,34 @@ public class DelegateEqualityComparer : IEqualityComparer private readonly Func _getHashCode; + /// + /// Initializes a new instance of the class with a comparison delegate. + /// The default is used for hash code generation. + /// + /// Delegate that determines whether two objects are equal. public DelegateEqualityComparer(Func compare) : this(compare, null) { } + /// + /// Initializes a new instance of the class with a comparison delegate + /// and a hash code delegate. + /// + /// Delegate that determines whether two objects are equal. + /// Delegate that computes the hash code for an object, or to use the default. public DelegateEqualityComparer(Func compare, Func getHashCode) { - Ensure.IsNotNull(compare, nameof(compare)); + Ensure.IsNotNull(compare); _compare = compare; _getHashCode = getHashCode; } + /// public bool Equals(T x, T y) { return _compare(x, y); } + /// public int GetHashCode(T obj) { return _getHashCode?.Invoke(obj) ?? obj.GetHashCode(); diff --git a/source/Core/CreativeCoders.Core/Comparing/FuncComparer.cs b/source/Core/CreativeCoders.Core/Comparing/FuncComparer.cs index 99630615..7895c621 100644 --- a/source/Core/CreativeCoders.Core/Comparing/FuncComparer.cs +++ b/source/Core/CreativeCoders.Core/Comparing/FuncComparer.cs @@ -4,20 +4,32 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Implements by extracting a key from each object using a delegate function +/// and comparing the keys with respect to a specified . +/// +/// The type of objects to compare. +/// The type of the key used for comparison. public class FuncComparer : IComparer { private readonly Func _keySelector; private readonly SortOrder _sortOrder; + /// + /// Initializes a new instance of the class. + /// + /// Function that extracts the comparison key from an object. + /// The direction in which to sort. public FuncComparer(Func keySelector, SortOrder sortOrder) { - Ensure.IsNotNull(keySelector, nameof(keySelector)); + Ensure.IsNotNull(keySelector); _keySelector = keySelector; _sortOrder = sortOrder; } + /// [SuppressMessage("ReSharper", "CompareNonConstrainedGenericWithNull")] public int Compare(T x, T y) { @@ -26,12 +38,12 @@ public int Compare(T x, T y) return 0; } - if (x == null) + if (x is null) { return GetCompareResult(-1); } - if (y == null) + if (y is null) { return GetCompareResult(1); } diff --git a/source/Core/CreativeCoders.Core/Comparing/FuncEqualityComparer.cs b/source/Core/CreativeCoders.Core/Comparing/FuncEqualityComparer.cs index a09b5939..9cde01a0 100644 --- a/source/Core/CreativeCoders.Core/Comparing/FuncEqualityComparer.cs +++ b/source/Core/CreativeCoders.Core/Comparing/FuncEqualityComparer.cs @@ -3,17 +3,28 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Implements by extracting a key from each object using a delegate function +/// and comparing the keys for equality. +/// +/// The type of objects to compare. +/// The type of the key used for equality comparison. public class FuncEqualityComparer : IEqualityComparer { private readonly Func _keySelector; + /// + /// Initializes a new instance of the class. + /// + /// Function that extracts the equality key from an object. public FuncEqualityComparer(Func keySelector) { - Ensure.IsNotNull(keySelector, nameof(keySelector)); + Ensure.IsNotNull(keySelector); _keySelector = keySelector; } + /// public bool Equals(T x, T y) { if (ReferenceEquals(x, y)) @@ -32,6 +43,7 @@ public bool Equals(T x, T y) return EqualityComparer.Default.Equals(xValue, yValue); } + /// public int GetHashCode(T obj) { return _keySelector(obj)?.GetHashCode() ?? 0; diff --git a/source/Core/CreativeCoders.Core/Comparing/MultiComparer.cs b/source/Core/CreativeCoders.Core/Comparing/MultiComparer.cs index 9fe0c174..3297d13f 100644 --- a/source/Core/CreativeCoders.Core/Comparing/MultiComparer.cs +++ b/source/Core/CreativeCoders.Core/Comparing/MultiComparer.cs @@ -4,16 +4,26 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Implements by combining multiple comparers, evaluating them in order +/// and returning the first non-zero result. +/// +/// The type of objects to compare. [PublicAPI] public class MultiComparer : IComparer { private readonly IComparer[] _comparerList; + /// + /// Initializes a new instance of the class. + /// + /// Ordered collection of comparers to evaluate sequentially. public MultiComparer(params IComparer[] comparerList) { _comparerList = comparerList; } + /// public int Compare(T x, T y) { return _comparerList diff --git a/source/Core/CreativeCoders.Core/Comparing/MultiEqualityComparer.cs b/source/Core/CreativeCoders.Core/Comparing/MultiEqualityComparer.cs index e29aa821..e98edc75 100644 --- a/source/Core/CreativeCoders.Core/Comparing/MultiEqualityComparer.cs +++ b/source/Core/CreativeCoders.Core/Comparing/MultiEqualityComparer.cs @@ -4,28 +4,39 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Implements by combining multiple equality comparers. +/// Two objects are considered equal only if all comparers agree they are equal. +/// +/// The type of objects to compare. [PublicAPI] public class MultiEqualityComparer : IEqualityComparer { private readonly IEqualityComparer[] _comparerList; + /// + /// Initializes a new instance of the class. + /// + /// Ordered collection of equality comparers that must all agree for two objects to be considered equal. public MultiEqualityComparer(params IEqualityComparer[] comparerList) { - Ensure.IsNotNullOrEmpty(comparerList, nameof(comparerList)); + Ensure.IsNotNullOrEmpty(comparerList); _comparerList = comparerList; } + /// public bool Equals(T x, T y) { return _comparerList.All(comparer => comparer.Equals(x, y)); } + /// public int GetHashCode(T obj) { if (_comparerList.Length == 1) { - return _comparerList.First().GetHashCode(obj); + return _comparerList[0].GetHashCode(obj); } var hashCodes = _comparerList.Select(comparer => comparer.GetHashCode(obj)); diff --git a/source/Core/CreativeCoders.Core/Comparing/MultiFuncComparer.cs b/source/Core/CreativeCoders.Core/Comparing/MultiFuncComparer.cs index fff7e4e7..21b818a6 100644 --- a/source/Core/CreativeCoders.Core/Comparing/MultiFuncComparer.cs +++ b/source/Core/CreativeCoders.Core/Comparing/MultiFuncComparer.cs @@ -7,15 +7,39 @@ // } //} +/// +/// Implements using two key selector functions defined by instances. +/// +/// The type of objects to compare. +/// The type of the first sort key. +/// The type of the second sort key. public class MultiFuncComparer : MultiComparer { + /// + /// Initializes a new instance of the class. + /// + /// Sort field definition for the first key. + /// Sort field definition for the second key. public MultiFuncComparer(SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2) : base(new FuncComparer(sortFieldInfo1.KeySelector, sortFieldInfo1.SortOrder), new FuncComparer(sortFieldInfo2.KeySelector, sortFieldInfo2.SortOrder)) { } } +/// +/// Implements using three key selector functions defined by instances. +/// +/// The type of objects to compare. +/// The type of the first sort key. +/// The type of the second sort key. +/// The type of the third sort key. public class MultiFuncComparer : MultiComparer { + /// + /// Initializes a new instance of the class. + /// + /// Sort field definition for the first key. + /// Sort field definition for the second key. + /// Sort field definition for the third key. public MultiFuncComparer(SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2, SortFieldInfo sortFieldInfo3) : base(new FuncComparer(sortFieldInfo1.KeySelector, sortFieldInfo1.SortOrder), @@ -23,8 +47,23 @@ public MultiFuncComparer(SortFieldInfo sortFieldInfo1, SortFieldInfo(sortFieldInfo3.KeySelector, sortFieldInfo3.SortOrder)) { } } +/// +/// Implements using four key selector functions defined by instances. +/// +/// The type of objects to compare. +/// The type of the first sort key. +/// The type of the second sort key. +/// The type of the third sort key. +/// The type of the fourth sort key. public class MultiFuncComparer : MultiComparer { + /// + /// Initializes a new instance of the class. + /// + /// Sort field definition for the first key. + /// Sort field definition for the second key. + /// Sort field definition for the third key. + /// Sort field definition for the fourth key. public MultiFuncComparer(SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2, SortFieldInfo sortFieldInfo3, SortFieldInfo sortFieldInfo4) : base(new FuncComparer(sortFieldInfo1.KeySelector, sortFieldInfo1.SortOrder), @@ -33,8 +72,25 @@ public MultiFuncComparer(SortFieldInfo sortFieldInfo1, SortFieldInfo(sortFieldInfo4.KeySelector, sortFieldInfo4.SortOrder)) { } } +/// +/// Implements using five key selector functions defined by instances. +/// +/// The type of objects to compare. +/// The type of the first sort key. +/// The type of the second sort key. +/// The type of the third sort key. +/// The type of the fourth sort key. +/// The type of the fifth sort key. public class MultiFuncComparer : MultiComparer { + /// + /// Initializes a new instance of the class. + /// + /// Sort field definition for the first key. + /// Sort field definition for the second key. + /// Sort field definition for the third key. + /// Sort field definition for the fourth key. + /// Sort field definition for the fifth key. public MultiFuncComparer(SortFieldInfo sortFieldInfo1, SortFieldInfo sortFieldInfo2, SortFieldInfo sortFieldInfo3, SortFieldInfo sortFieldInfo4, SortFieldInfo sortFieldInfo5) : diff --git a/source/Core/CreativeCoders.Core/Comparing/MultiFuncEqualitiyComparer.cs b/source/Core/CreativeCoders.Core/Comparing/MultiFuncEqualitiyComparer.cs index 076d6027..1dfa7b6a 100644 --- a/source/Core/CreativeCoders.Core/Comparing/MultiFuncEqualitiyComparer.cs +++ b/source/Core/CreativeCoders.Core/Comparing/MultiFuncEqualitiyComparer.cs @@ -4,22 +4,55 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Implements using multiple key selector functions of the same key type. +/// +/// The type of objects to compare. +/// The type of the key used for equality comparison. public class MultiFuncEqualityComparer : MultiEqualityComparer { + /// + /// Initializes a new instance of the class. + /// + /// Collection of functions that extract equality keys from an object. public MultiFuncEqualityComparer(params Func[] keySelectors) : base(keySelectors.Select(IEqualityComparer (func) => new FuncEqualityComparer(func)) .ToArray()) { } } +/// +/// Implements using two key selector functions with distinct key types. +/// +/// The type of objects to compare. +/// The type of the first equality key. +/// The type of the second equality key. public class MultiFuncEqualityComparer : MultiEqualityComparer { + /// + /// Initializes a new instance of the class. + /// + /// Function that extracts the first equality key from an object. + /// Function that extracts the second equality key from an object. public MultiFuncEqualityComparer(Func keySelector1, Func keySelector2) : base(new FuncEqualityComparer(keySelector1), new FuncEqualityComparer(keySelector2)) { } } +/// +/// Implements using three key selector functions with distinct key types. +/// +/// The type of objects to compare. +/// The type of the first equality key. +/// The type of the second equality key. +/// The type of the third equality key. public class MultiFuncEqualityComparer : MultiEqualityComparer { + /// + /// Initializes a new instance of the class. + /// + /// Function that extracts the first equality key from an object. + /// Function that extracts the second equality key from an object. + /// Function that extracts the third equality key from an object. public MultiFuncEqualityComparer(Func keySelector1, Func keySelector2, Func keySelector3) : base(new FuncEqualityComparer(keySelector1), @@ -27,8 +60,23 @@ public MultiFuncEqualityComparer(Func keySelector1, Func key new FuncEqualityComparer(keySelector3)) { } } +/// +/// Implements using four key selector functions with distinct key types. +/// +/// The type of objects to compare. +/// The type of the first equality key. +/// The type of the second equality key. +/// The type of the third equality key. +/// The type of the fourth equality key. public class MultiFuncEqualityComparer : MultiEqualityComparer { + /// + /// Initializes a new instance of the class. + /// + /// Function that extracts the first equality key from an object. + /// Function that extracts the second equality key from an object. + /// Function that extracts the third equality key from an object. + /// Function that extracts the fourth equality key from an object. public MultiFuncEqualityComparer(Func keySelector1, Func keySelector2, Func keySelector3, Func keySelector4) : base(new FuncEqualityComparer(keySelector1), @@ -37,8 +85,25 @@ public MultiFuncEqualityComparer(Func keySelector1, Func key new FuncEqualityComparer(keySelector4)) { } } +/// +/// Implements using five key selector functions with distinct key types. +/// +/// The type of objects to compare. +/// The type of the first equality key. +/// The type of the second equality key. +/// The type of the third equality key. +/// The type of the fourth equality key. +/// The type of the fifth equality key. public class MultiFuncEqualityComparer : MultiEqualityComparer { + /// + /// Initializes a new instance of the class. + /// + /// Function that extracts the first equality key from an object. + /// Function that extracts the second equality key from an object. + /// Function that extracts the third equality key from an object. + /// Function that extracts the fourth equality key from an object. + /// Function that extracts the fifth equality key from an object. public MultiFuncEqualityComparer(Func keySelector1, Func keySelector2, Func keySelector3, Func keySelector4, Func keySelector5) : base(new FuncEqualityComparer(keySelector1), diff --git a/source/Core/CreativeCoders.Core/Comparing/SortFieldInfo.cs b/source/Core/CreativeCoders.Core/Comparing/SortFieldInfo.cs index 1166b0b0..cb063fc0 100644 --- a/source/Core/CreativeCoders.Core/Comparing/SortFieldInfo.cs +++ b/source/Core/CreativeCoders.Core/Comparing/SortFieldInfo.cs @@ -2,15 +2,31 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Represents a sort field definition consisting of a key selector function and a sort order. +/// +/// The type of the object to extract the sort key from. +/// The type of the sort key. public class SortFieldInfo { + /// + /// Initializes a new instance of the class. + /// + /// Function that extracts the sort key from an object. + /// The direction in which to sort. public SortFieldInfo(Func keySelector, SortOrder sortOrder) { KeySelector = keySelector; SortOrder = sortOrder; } + /// + /// Gets the function that extracts the sort key from an object. + /// public Func KeySelector { get; } + /// + /// Gets the direction in which to sort. + /// public SortOrder SortOrder { get; } } diff --git a/source/Core/CreativeCoders.Core/Comparing/SortOrder.cs b/source/Core/CreativeCoders.Core/Comparing/SortOrder.cs index bf4da0ae..bc701f00 100644 --- a/source/Core/CreativeCoders.Core/Comparing/SortOrder.cs +++ b/source/Core/CreativeCoders.Core/Comparing/SortOrder.cs @@ -1,7 +1,17 @@ namespace CreativeCoders.Core.Comparing; +/// +/// Specifies the direction in which a collection is sorted. +/// public enum SortOrder { + /// + /// Sorts in ascending order, from smallest to largest. + /// Ascending, + + /// + /// Sorts in descending order, from largest to smallest. + /// Descending } diff --git a/source/Core/CreativeCoders.Core/CreativeCoders.Core.csproj b/source/Core/CreativeCoders.Core/CreativeCoders.Core.csproj index 8720768a..84d2272a 100644 --- a/source/Core/CreativeCoders.Core/CreativeCoders.Core.csproj +++ b/source/Core/CreativeCoders.Core/CreativeCoders.Core.csproj @@ -4,6 +4,8 @@ Library Basic core classes and types README.md + true + $(NoWarn);1591 diff --git a/source/Core/CreativeCoders.Core/DelegateAsyncDisposable.cs b/source/Core/CreativeCoders.Core/DelegateAsyncDisposable.cs index 7ef60730..ce598cdc 100644 --- a/source/Core/CreativeCoders.Core/DelegateAsyncDisposable.cs +++ b/source/Core/CreativeCoders.Core/DelegateAsyncDisposable.cs @@ -3,6 +3,9 @@ namespace CreativeCoders.Core; +/// +/// Wraps a delegate as an implementation. +/// public sealed class DelegateAsyncDisposable : IAsyncDisposable { private readonly Func _dispose; @@ -11,14 +14,22 @@ public sealed class DelegateAsyncDisposable : IAsyncDisposable private bool _isDisposed; + /// + /// Initializes a new instance of the class. + /// + /// The function to invoke on asynchronous disposal. + /// + /// to invoke the dispose function only once; otherwise, . + /// public DelegateAsyncDisposable(Func dispose, bool onlyDisposeOnce) { - Ensure.IsNotNull(dispose, nameof(dispose)); + Ensure.IsNotNull(dispose); _dispose = dispose; _onlyDisposeOnce = onlyDisposeOnce; } + /// public ValueTask DisposeAsync() { if (_onlyDisposeOnce && _isDisposed) diff --git a/source/Core/CreativeCoders.Core/DelegateDisposable.cs b/source/Core/CreativeCoders.Core/DelegateDisposable.cs index 9837d08a..1b315eca 100644 --- a/source/Core/CreativeCoders.Core/DelegateDisposable.cs +++ b/source/Core/CreativeCoders.Core/DelegateDisposable.cs @@ -4,6 +4,9 @@ namespace CreativeCoders.Core; +/// +/// Wraps a delegate as an implementation. +/// public sealed class DelegateDisposable : IDisposable { private readonly Action _dispose; @@ -12,14 +15,22 @@ public sealed class DelegateDisposable : IDisposable private bool _isDisposed; + /// + /// Initializes a new instance of the class. + /// + /// The action to invoke on disposal. + /// + /// to invoke the dispose action only once; otherwise, . + /// public DelegateDisposable(Action dispose, bool onlyDisposeOnce) { - Ensure.IsNotNull(dispose, nameof(dispose)); + Ensure.IsNotNull(dispose); _dispose = dispose; _onlyDisposeOnce = onlyDisposeOnce; } + /// public void Dispose() { if (_onlyDisposeOnce && _isDisposed) diff --git a/source/Core/CreativeCoders.Core/Dependencies/CircularReferenceException.cs b/source/Core/CreativeCoders.Core/Dependencies/CircularReferenceException.cs index 813b99bf..8798a677 100644 --- a/source/Core/CreativeCoders.Core/Dependencies/CircularReferenceException.cs +++ b/source/Core/CreativeCoders.Core/Dependencies/CircularReferenceException.cs @@ -2,35 +2,24 @@ namespace CreativeCoders.Core.Dependencies; -///------------------------------------------------------------------------------------------------- /// -/// Exception for signalling a circular reference in the dependency collection. +/// Represents an exception that is thrown when a circular reference is detected in a dependency collection. /// -/// /// -///------------------------------------------------------------------------------------------------- public class CircularReferenceException : Exception { - ///------------------------------------------------------------------------------------------------- /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// The message. - /// The possible circular references. - ///------------------------------------------------------------------------------------------------- + /// The error message that describes the circular reference. + /// The elements that may be involved in the circular reference. public CircularReferenceException(string message, object[] possibleCircularReferences) : base(message) { PossibleCircularReferences = possibleCircularReferences; } - ///------------------------------------------------------------------------------------------------- - /// Gets the possible circular references. - /// - /// - /// The possible circular references. Contains the elements left over, which can be - /// responsible for the circular reference. - /// - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the elements that may be responsible for the circular reference. + /// public object[] PossibleCircularReferences { get; } } diff --git a/source/Core/CreativeCoders.Core/Dependencies/DependencyObject.cs b/source/Core/CreativeCoders.Core/Dependencies/DependencyObject.cs index 62386ae7..c1289a62 100644 --- a/source/Core/CreativeCoders.Core/Dependencies/DependencyObject.cs +++ b/source/Core/CreativeCoders.Core/Dependencies/DependencyObject.cs @@ -2,19 +2,17 @@ namespace CreativeCoders.Core.Dependencies; -///------------------------------------------------------------------------------------------------- -/// A dependency object holding the element and its dependencies. -/// -/// Generic type parameter of the . -///------------------------------------------------------------------------------------------------- +/// +/// Represents an element and its dependencies within a dependency graph. +/// +/// The type of the element. public class DependencyObject where T : class { - ///------------------------------------------------------------------------------------------------- - /// Initializes a new instance of the class. - /// - /// The element. - ///------------------------------------------------------------------------------------------------- + /// + /// Initializes a new instance of the class. + /// + /// The element to wrap. public DependencyObject(T element) { Element = element; @@ -22,17 +20,13 @@ public DependencyObject(T element) DependsOn = []; } - ///------------------------------------------------------------------------------------------------- - /// Gets the element. - /// - /// The element. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the wrapped element. + /// public T Element { get; } - ///------------------------------------------------------------------------------------------------- - /// Gets the objects it is dependent on. - /// - /// A list of this object depends on. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the list of instances this object depends on. + /// public List> DependsOn { get; } } diff --git a/source/Core/CreativeCoders.Core/Dependencies/DependencyObjectCollection.cs b/source/Core/CreativeCoders.Core/Dependencies/DependencyObjectCollection.cs index 4b88c9d1..e7db316a 100644 --- a/source/Core/CreativeCoders.Core/Dependencies/DependencyObjectCollection.cs +++ b/source/Core/CreativeCoders.Core/Dependencies/DependencyObjectCollection.cs @@ -6,35 +6,33 @@ namespace CreativeCoders.Core.Dependencies; -/// ------------------------------------------------------------------------------------------------- -/// Collection of dependency objects. -/// Generic type parameter of the elements. -/// ------------------------------------------------------------------------------------------------- +/// +/// Represents a collection of instances that models a dependency graph. +/// +/// The type of the elements in the dependency graph. [PublicAPI] public class DependencyObjectCollection where T : class { private readonly List> _dependencyObjects; - /// ------------------------------------------------------------------------------------------------- /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// ------------------------------------------------------------------------------------------------- public DependencyObjectCollection() { _dependencyObjects = []; } - /// ------------------------------------------------------------------------------------------------- /// - /// Adds an element to the collection and returns the corresponding - /// . If element is already added, returns the already - /// existing . + /// Adds an element to the collection and returns the corresponding . + /// If the element already exists, returns the existing . /// - /// The element to add. - /// A - /// ------------------------------------------------------------------------------------------------- + /// The element to add. + /// + /// The for the element, or if + /// is . + /// public DependencyObject AddElement(T element) { if (element == null) @@ -56,14 +54,11 @@ public DependencyObject AddElement(T element) return newDependencyObject; } - /// ------------------------------------------------------------------------------------------------- - /// Adds a dependency to the collection. - /// The element to add. - /// - /// A list of elements the depends - /// on. - /// - /// ------------------------------------------------------------------------------------------------- + /// + /// Adds a dependency relationship where depends on the specified elements. + /// + /// The element that has dependencies. + /// The elements that depends on. public void AddDependency(T element, params T[] dependsOnElements) { var dependencyObject = AddElement(element); @@ -78,23 +73,22 @@ public void AddDependency(T element, params T[] dependsOnElements) }); } - /// ------------------------------------------------------------------------------------------------- - /// Gets dependency object corresponding to the . - /// - /// The element for which the is - /// returned. - /// + /// + /// Gets the corresponding to the specified element. + /// + /// The element to look up. /// - /// The dependency object. If is not in the collection, null is - /// returned. + /// The for the element, or if the element + /// is not in the collection. /// - /// ------------------------------------------------------------------------------------------------- public DependencyObject GetDependencyObject(T element) { return _dependencyObjects.FirstOrDefault(x => x.Element.Equals(element)); } - /// Removes redundant dependencies. + /// + /// Removes redundant (transitively implied) dependencies from all objects in the collection. + /// public void RemoveRedundancies() { foreach (var dependencyObject in _dependencyObjects) @@ -103,12 +97,12 @@ public void RemoveRedundancies() } } - /// ------------------------------------------------------------------------------------------------- - /// Determines if there are circular references in the collection. + /// + /// Checks whether the collection contains any circular references. + /// /// - /// True if there are circular references, false if the collection has no circular references. + /// if circular references exist; otherwise, . /// - /// ------------------------------------------------------------------------------------------------- public bool CheckForCircularReferences() { var sortObjects = _dependencyObjects @@ -165,9 +159,8 @@ private static bool ObjectIsSubObject(IReadOnlyCollection> d ObjectIsSubObject(childDependencyObject.DependsOn, dependencyObject)); } - /// ------------------------------------------------------------------------------------------------- - /// Gets the dependency objects. - /// The dependency objects. - /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the dependency objects in the collection. + /// public IReadOnlyCollection> DependencyObjects => _dependencyObjects; } diff --git a/source/Core/CreativeCoders.Core/Dependencies/DependencyResolver.cs b/source/Core/CreativeCoders.Core/Dependencies/DependencyResolver.cs index cbff009b..e6522892 100644 --- a/source/Core/CreativeCoders.Core/Dependencies/DependencyResolver.cs +++ b/source/Core/CreativeCoders.Core/Dependencies/DependencyResolver.cs @@ -3,38 +3,29 @@ namespace CreativeCoders.Core.Dependencies; -///------------------------------------------------------------------------------------------------- -/// A dependency resolver, which resolves dependencies for a element. -/// -/// Generic type parameter of the element. -///------------------------------------------------------------------------------------------------- +/// +/// Resolves all transitive dependencies for an element within a . +/// +/// The type of the elements in the dependency graph. public class DependencyResolver where T : class { private readonly DependencyObjectCollection _dependencyObjectCollection; - ///------------------------------------------------------------------------------------------------- /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// Collection of dependency objects. - ///------------------------------------------------------------------------------------------------- + /// The collection of dependency objects to resolve against. public DependencyResolver(DependencyObjectCollection dependencyObjectCollection) { _dependencyObjectCollection = dependencyObjectCollection; } - ///------------------------------------------------------------------------------------------------- - /// Resolves all dependencies for . - /// - /// The element. - /// - /// - /// The dependencies for . Only unique dependencies, duplicates are - /// removed. - /// - ///------------------------------------------------------------------------------------------------- + /// + /// Resolves all unique transitive dependencies for the specified element. + /// + /// The element whose dependencies are resolved. + /// A distinct collection of all dependencies for . public IEnumerable Resolve(T element) { return InternalResolve(element).Distinct().ToArray(); diff --git a/source/Core/CreativeCoders.Core/Dependencies/DependencySorter.cs b/source/Core/CreativeCoders.Core/Dependencies/DependencySorter.cs index 0aa8bc2e..5e5c0e12 100644 --- a/source/Core/CreativeCoders.Core/Dependencies/DependencySorter.cs +++ b/source/Core/CreativeCoders.Core/Dependencies/DependencySorter.cs @@ -4,35 +4,29 @@ namespace CreativeCoders.Core.Dependencies; -/// ------------------------------------------------------------------------------------------------- -/// A dependency sorter. -/// Generic type parameter of teh elements. -/// ------------------------------------------------------------------------------------------------- +/// +/// Performs a topological sort on elements in a based on their dependencies. +/// +/// The type of the elements to sort. public class DependencySorter where T : class { private readonly DependencyObjectCollection _dependencyObjectCollection; - /// ------------------------------------------------------------------------------------------------- - /// Initializes a new instance of the class. - /// Collection of dependency objects. - /// ------------------------------------------------------------------------------------------------- + /// + /// Initializes a new instance of the class. + /// + /// The collection of dependency objects to sort. public DependencySorter(DependencyObjectCollection dependencyObjectCollection) { _dependencyObjectCollection = dependencyObjectCollection; } - /// ------------------------------------------------------------------------------------------------- - /// Sorts the elements. - /// - /// Thrown when a Circular Reference error - /// condition occurs. - /// - /// - /// Sorted list of the elements respecting the dependencies. First element is the least - /// depending element. - /// - /// ------------------------------------------------------------------------------------------------- + /// + /// Sorts the elements in topological order so that dependencies appear before the elements that depend on them. + /// + /// The elements sorted in dependency order, with the least dependent elements first. + /// The dependency graph contains a circular reference. public IEnumerable Sort() { var sortedList = new List(); diff --git a/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeBuilder.cs b/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeBuilder.cs index 2d5b165c..071e68c2 100644 --- a/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeBuilder.cs +++ b/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeBuilder.cs @@ -3,38 +3,31 @@ namespace CreativeCoders.Core.Dependencies; -///------------------------------------------------------------------------------------------------- -/// A dependency tree builder. -/// -/// Generic type parameter of the elements. -///------------------------------------------------------------------------------------------------- +/// +/// Builds a hierarchical dependency tree from a . +/// +/// The type of the elements in the dependency tree. public class DependencyTreeBuilder where T : class { private readonly DependencyObjectCollection _dependencyObjectCollection; - ///------------------------------------------------------------------------------------------------- /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// Collection of dependency objects. - ///------------------------------------------------------------------------------------------------- + /// The collection of dependency objects to build the tree from. public DependencyTreeBuilder(DependencyObjectCollection dependencyObjectCollection) { _dependencyObjectCollection = dependencyObjectCollection; } - ///------------------------------------------------------------------------------------------------- - /// Builds the dependency tree for the given . - /// - /// The element for which the dependency tree should be build. - /// + /// + /// Builds a dependency tree rooted at the specified element. + /// + /// The root element of the dependency tree. /// - /// The root node of the dependency tree. The root node is the given - /// and all dependencies are arranged below. + /// A representing the root, with all dependencies arranged as sub-nodes. /// - ///------------------------------------------------------------------------------------------------- public DependencyTreeNode Build(T element) { return new DependencyTreeNode(element, GetSubNodes(_dependencyObjectCollection.GetDependencyObject(element))); diff --git a/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeNode.cs b/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeNode.cs index 3d39759e..a2825f02 100644 --- a/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeNode.cs +++ b/source/Core/CreativeCoders.Core/Dependencies/DependencyTreeNode.cs @@ -3,11 +3,10 @@ namespace CreativeCoders.Core.Dependencies; -///------------------------------------------------------------------------------------------------- -/// A dependency tree node. -/// -/// Generic type parameter. -///------------------------------------------------------------------------------------------------- +/// +/// Represents a node in a dependency tree, containing an element and its child dependency nodes. +/// +/// The type of the element in the tree node. public class DependencyTreeNode where T : class { @@ -17,17 +16,13 @@ internal DependencyTreeNode(T element, IEnumerable> subNod SubNodes = subNodes.ToArray(); } - ///------------------------------------------------------------------------------------------------- - /// Gets the element of the tree node. - /// - /// The element. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the element associated with this tree node. + /// public T Element { get; } - ///------------------------------------------------------------------------------------------------- - /// Gets the sub nodes. - /// - /// The sub nodes. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the child nodes representing the dependencies of this node's element. + /// public IReadOnlyCollection> SubNodes { get; } } diff --git a/source/Core/CreativeCoders.Core/Ensure.cs b/source/Core/CreativeCoders.Core/Ensure.cs index 883c23a5..aa8e9800 100644 --- a/source/Core/CreativeCoders.Core/Ensure.cs +++ b/source/Core/CreativeCoders.Core/Ensure.cs @@ -11,7 +11,9 @@ namespace CreativeCoders.Core; -/// Static class with methods for parameter checking. +/// +/// Provides static methods for validating method arguments and preconditions. +/// [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "")] [SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Global")] [PublicAPI] @@ -19,18 +21,14 @@ public static class Ensure { private const string UnknownParamName = "[unknown]"; - ///------------------------------------------------------------------------------------------------- - /// Ensures that is not null. - /// - /// Thrown when one or more required arguments are - /// null. - /// - /// Generic type parameter. - /// The value to check. - /// Name of the parameter. - /// - /// The . - ///------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified is not . + /// + /// The type of the value. + /// The value to validate. + /// The name of the parameter being validated. + /// The validated non-null value. + /// is . [ContractAnnotation("value: null => halt; value: notnull => notnull")] [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: System.Diagnostics.CodeAnalysis.NotNull] @@ -40,6 +38,12 @@ public static T NotNull([System.Diagnostics.CodeAnalysis.NotNull] [NoEnumerat return value ?? throw new ArgumentNullException(paramName); } + /// + /// Ensures that the specified is not . + /// + /// The value to validate. + /// The name of the parameter being validated. + /// is . [ContractAnnotation("value: null => halt")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsNotNull([System.Diagnostics.CodeAnalysis.NotNull] [NoEnumeration] object? value, @@ -51,14 +55,12 @@ public static void IsNotNull([System.Diagnostics.CodeAnalysis.NotNull] [NoEnumer } } - ///------------------------------------------------------------------------------------------------- - /// Ensures that is not null. - /// - /// Type of the exception. - /// The value to check. - /// The which creates the exception that - /// gets thrown, if is null. - ///------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified is not , throwing a custom exception. + /// + /// The type of the exception to throw. + /// The value to validate. + /// The factory that creates the exception to throw when is . [ContractAnnotation("halt <= value: null")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsNotNull([System.Diagnostics.CodeAnalysis.NotNull] [NoEnumeration] object? value, @@ -70,17 +72,13 @@ public static void IsNotNull([System.Diagnostics.CodeAnalysis.NotNull] [NoEnu } } - ///------------------------------------------------------------------------------------------------- - /// Ensures that is not null or empty. - /// - /// Thrown when is null or - /// empty. - /// - /// The value to check. - /// Name of the parameter. - /// - /// The . - ///------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified string is not or empty. + /// + /// The string value to validate. + /// The name of the parameter being validated. + /// The validated non-null and non-empty string. + /// is or empty. [ContractAnnotation("halt <= value: null; value: notnull => notnull")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string IsNotNullOrEmpty([System.Diagnostics.CodeAnalysis.NotNull] string? value, @@ -91,16 +89,13 @@ public static string IsNotNullOrEmpty([System.Diagnostics.CodeAnalysis.NotNull] : value; } - ///------------------------------------------------------------------------------------------------- - /// Ensures that is not null or empty. - /// - /// Thrown when is null or has - /// no elements. - /// - /// Type parameter of an element. - /// The value to check. - /// Name of the parameter. - ///------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified enumerable is not or empty. + /// + /// The type of the elements in the enumerable. + /// The enumerable value to validate. + /// The name of the parameter being validated. + /// is or has no elements. [ContractAnnotation("halt <= value: null")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsNotNullOrEmpty(IEnumerable? value, @@ -112,17 +107,13 @@ public static void IsNotNullOrEmpty(IEnumerable? value, } } - ///------------------------------------------------------------------------------------------------- - /// Ensure is not null or whitespace. - /// - /// Thrown when is null or - /// whitespace. - /// - /// The value to check. - /// Name of the parameter. - /// - /// The . - ///------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified string is not or whitespace. + /// + /// The string value to validate. + /// The name of the parameter being validated. + /// The validated non-null and non-whitespace string. + /// is or whitespace. [ContractAnnotation("halt <= value: null; value: notnull => notnull")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string IsNotNullOrWhitespace(string? value, @@ -133,15 +124,12 @@ public static string IsNotNullOrWhitespace(string? value, : value; } - /// ------------------------------------------------------------------------------------------------- - /// Ensures that a given file exists. - /// - /// Thrown when the file - /// is not present. - /// - /// Name of the file to check. - /// Name of the parameter. - /// ------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified file exists. + /// + /// The path of the file to check. + /// The name of the parameter being validated. + /// does not exist. public static void FileExists(string? fileName, [CallerArgumentExpression("fileName")] string paramName = UnknownParamName) { @@ -153,16 +141,12 @@ public static void FileExists(string? fileName, } } - ///------------------------------------------------------------------------------------------------- - /// Ensures that a given directory exists. - /// - /// Thrown when the requested directory - /// is not - /// present. - /// - /// Name of the directory to check. - /// Name of the parameter. - ///------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified directory exists. + /// + /// The path of the directory to check. + /// The name of the parameter being validated. + /// does not exist. public static void DirectoryExists(string? directoryName, [CallerArgumentExpression("directoryName")] string paramName = UnknownParamName) @@ -174,14 +158,12 @@ public static void DirectoryExists(string? directoryName, } } - ///------------------------------------------------------------------------------------------------- - /// Ensures that the unique identifier is not empty. - /// - /// Thrown when is empty. - /// - /// The guid to check. - /// Name of the parameter. - ///------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified is not . + /// + /// The value to validate. + /// The name of the parameter being validated. + /// is . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GuidIsNotEmpty(Guid guid, [CallerArgumentExpression("guid")] string paramName = UnknownParamName) @@ -192,12 +174,13 @@ public static void GuidIsNotEmpty(Guid guid, } } - /// ------------------------------------------------------------------------------------------------- - /// Ensures that is true. - /// The condition that gets checked. - /// The message for the exception. - /// Name of the parameter that gets checked. - /// ------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified is . + /// + /// The condition to evaluate. + /// The exception message. + /// The name of the parameter being validated. + /// is . [ContractAnnotation("halt <= condition: false")] public static void That(bool condition, string message = "Assertion failed", @@ -210,15 +193,13 @@ public static void That(bool condition, } } - /// ------------------------------------------------------------------------------------------------- - /// Ensures that a index meets a condition. - /// - /// Thrown when is - /// false, cause index not meets requirements. - /// The condition that gets checked. - /// The message for the exception. - /// Name of the parameter that gets checked. - /// ------------------------------------------------------------------------------------------------- + /// + /// Ensures that the specified range is . + /// + /// The range condition to evaluate. + /// The exception message. + /// The name of the parameter being validated. + /// is . [ContractAnnotation("halt <= condition: false")] public static void ThatRange(bool condition, string message = "Assertion failed", @@ -231,19 +212,15 @@ public static void ThatRange(bool condition, } } - ///------------------------------------------------------------------------------------------------- /// - /// Ensures that the is in range between - /// and . + /// Ensures that the specified is in range between + /// and . /// - /// - /// Thrown when index is out of range. - /// - /// Zero-based index of the. - /// The start index. - /// The end index. - /// Name of the parameter. - ///------------------------------------------------------------------------------------------------- + /// The zero-based index to validate. + /// The inclusive lower bound of the valid range. + /// The inclusive upper bound of the valid range. + /// The name of the parameter. + /// is outside the range of to . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IndexIsInRange(int index, int startIndex, int endIndex, [CallerArgumentExpression("index")] string paramName = UnknownParamName) @@ -255,18 +232,14 @@ public static void IndexIsInRange(int index, int startIndex, int endIndex, } } - ///------------------------------------------------------------------------------------------------- /// - /// Ensures that the is in range of a collection with length - /// . + /// Ensures that the specified is in range for a collection with the given + /// . /// - /// - /// Thrown when the index is out of range. - /// - /// Zero-based index of the. - /// Length of the collection. - /// Name of the parameter. - ///------------------------------------------------------------------------------------------------- + /// The zero-based index to validate. + /// The length of the collection. + /// The name of the parameter. + /// is outside the valid range for the collection. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IndexIsInRange(int index, int collectionLength, [CallerArgumentExpression("index")] string paramName = UnknownParamName) @@ -278,15 +251,13 @@ public static void IndexIsInRange(int index, int collectionLength, } } - ///------------------------------------------------------------------------------------------------- - /// Creates an Argument{T} for fluent argument validation. - /// - /// Generic type parameter of the arguments value. - /// The value to check. - /// Name of the parameter. - /// - /// An Argument<T> - ///------------------------------------------------------------------------------------------------- + /// + /// Creates an for fluent argument validation. + /// + /// The type of the argument value. + /// The value to validate. + /// The name of the parameter. + /// An wrapping the specified value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Argument Argument(T? value, [CallerArgumentExpression("value")] string paramName = UnknownParamName) diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/Argument.cs b/source/Core/CreativeCoders.Core/EnsureArguments/Argument.cs index f2a5335d..08836e96 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/Argument.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/Argument.cs @@ -6,14 +6,14 @@ // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; -///------------------------------------------------------------------------------------------------- -/// Represents an argument, which can be validated. -/// -/// Generic type parameter. -///------------------------------------------------------------------------------------------------- +/// Represents an argument that can be validated. +/// The type of the argument value. [PublicAPI] public readonly struct Argument { + /// Initializes a new instance of the struct. + /// The argument value. + /// The name of the argument, automatically captured from the caller expression. public Argument(T? value, [CallerArgumentExpression("value")] string name = "[unknown]") { @@ -21,21 +21,14 @@ public Argument(T? value, Name = name ?? throw new ArgumentNullException(nameof(name)); } - ///------------------------------------------------------------------------------------------------- - /// Query if this object has value. - /// - /// True if value is not null, false if not. - ///------------------------------------------------------------------------------------------------- + /// Determines whether the argument has a non-null value. + /// if the value is not ; otherwise, . [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasValue() => Value is not null; - ///------------------------------------------------------------------------------------------------- - /// Casts the arguments value to TValue?. - /// - /// Type of the value. - /// - /// The casted argument value - ///------------------------------------------------------------------------------------------------- + /// Casts the argument value to . + /// The target type to cast the value to. + /// The argument value cast to , or if the value is . public TValue? Cast() { object? objectValue = Value; @@ -43,27 +36,17 @@ public Argument(T? value, return (TValue?) objectValue; } - ///------------------------------------------------------------------------------------------------- - /// Gets the value of the argument. - /// - /// The value. - ///------------------------------------------------------------------------------------------------- + /// Gets the value of the argument. + /// The argument value. public T? Value { get; } - ///------------------------------------------------------------------------------------------------- - /// Gets the name of the argument. - /// - /// The name. - ///------------------------------------------------------------------------------------------------- + /// Gets the name of the argument. + /// The argument name. public string Name { get; } - ///------------------------------------------------------------------------------------------------- - /// Implicit cast that converts the given Argument<T> to its value T? - /// - /// The argument. - /// - /// The value of the argument. - ///------------------------------------------------------------------------------------------------- + /// Implicitly converts an to its underlying value. + /// The argument to convert. + /// The value of the argument. public static implicit operator T?(Argument argument) { return argument.Value; diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/ArgumentNotNull.cs b/source/Core/CreativeCoders.Core/EnsureArguments/ArgumentNotNull.cs index 911cb90f..ec050094 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/ArgumentNotNull.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/ArgumentNotNull.cs @@ -6,16 +6,15 @@ // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; -///------------------------------------------------------------------------------------------------- -/// -/// Represents an argument that is known to be not null, which can be validated. -/// -/// -/// Generic type parameter. -///------------------------------------------------------------------------------------------------- +/// Represents an argument that is guaranteed to be not and can be validated. +/// The type of the argument value. [PublicAPI] public readonly struct ArgumentNotNull { + /// Initializes a new instance of the struct. + /// The argument value. + /// The name of the argument, automatically captured from the caller expression. + /// is . public ArgumentNotNull(T value, [CallerArgumentExpression("value")] string name = "[unknown]") { @@ -28,21 +27,14 @@ public ArgumentNotNull(T value, Name = name ?? throw new ArgumentNullException(nameof(name)); } - ///------------------------------------------------------------------------------------------------- - /// Query if this object has value. - /// - /// True if value is not null, false if not. - ///------------------------------------------------------------------------------------------------- + /// Determines whether the argument has a non-null value. + /// if the value is not ; otherwise, . [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasValue() => true; - ///------------------------------------------------------------------------------------------------- - /// Casts the arguments value to TValue. - /// - /// Type of the value. - /// - /// The casted argument value - ///------------------------------------------------------------------------------------------------- + /// Casts the argument value to . + /// The target type to cast the value to. + /// The argument value cast to . public TValue Cast() { object objectValue = Value; @@ -50,28 +42,18 @@ public TValue Cast() return (TValue) objectValue; } - ///------------------------------------------------------------------------------------------------- - /// Gets the value of the argument. - /// - /// The value. - ///------------------------------------------------------------------------------------------------- + /// Gets the value of the argument. + /// The argument value, guaranteed to be not . [System.Diagnostics.CodeAnalysis.NotNull] public T Value { get; } - ///------------------------------------------------------------------------------------------------- - /// Gets the name of the argument. - /// - /// The name. - ///------------------------------------------------------------------------------------------------- + /// Gets the name of the argument. + /// The argument name. public string Name { get; } - ///------------------------------------------------------------------------------------------------- - /// Implicit cast that converts the given Argument<T> to its value T - /// - /// The argument. - /// - /// The value of the argument. - ///------------------------------------------------------------------------------------------------- + /// Implicitly converts an to its underlying value. + /// The argument to convert. + /// The value of the argument. public static implicit operator T(ArgumentNotNull argument) { return argument.Value; diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/ExceptionThrower.cs b/source/Core/CreativeCoders.Core/EnsureArguments/ExceptionThrower.cs index ca37cc6c..fdb558af 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/ExceptionThrower.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/ExceptionThrower.cs @@ -6,14 +6,13 @@ // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; +/// Provides methods for throwing standard argument exceptions. public static class ExceptionThrower { - /// - /// - /// - /// - /// - /// + /// Throws an with the specified parameter name. + /// The name of the parameter that caused the exception. + /// The optional error message. + /// is always thrown as this method never returns normally. [DoesNotReturn] [ContractAnnotation("=> halt")] public static void ThrowArgumentNullException(string paramName, string? message = null) diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/EnumerableArgumentExtensions.cs b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/EnumerableArgumentExtensions.cs index c956dbdb..3b671078 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/EnumerableArgumentExtensions.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/EnumerableArgumentExtensions.cs @@ -6,8 +6,20 @@ // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; +/// +/// Provides collection validation extension methods for . +/// public static class EnumerableArgumentExtensions { + /// + /// Ensures that the enumerable argument is not and contains at least one element. + /// + /// The enumerable type of the argument value. + /// The argument to validate. + /// Optional custom error message. + /// The validated argument as for chaining. + /// The argument value is . + /// The enumerable contains no elements. public static ArgumentNotNull NotNullOrEmpty(in this Argument argument, string? message = null) where T : IEnumerable? { @@ -27,6 +39,14 @@ public static ArgumentNotNull NotNullOrEmpty(in this Argument argument, return new ArgumentNotNull(argument.Value, argument.Name); } + /// + /// Ensures that the enumerable argument contains at least one element. + /// + /// The enumerable type of the argument value. + /// The argument to validate. + /// Optional custom error message. + /// The validated argument for chaining. + /// The enumerable contains no elements. public static ref readonly ArgumentNotNull NotEmpty(in this ArgumentNotNull argument, string? message = null) where T : IEnumerable @@ -39,6 +59,15 @@ public static ref readonly ArgumentNotNull NotEmpty(in this ArgumentNotNul return ref argument; } + /// + /// Ensures that the enumerable argument contains at least the specified number of elements. + /// + /// The enumerable type of the argument value. + /// The argument to validate. + /// The minimum required number of elements. + /// Optional custom error message. + /// The validated argument for chaining. + /// The element count is less than . public static ref readonly ArgumentNotNull MinCount(in this ArgumentNotNull argument, int minCount, string? message = null) @@ -53,6 +82,15 @@ public static ref readonly ArgumentNotNull MinCount(in this ArgumentNotNul return ref argument; } + /// + /// Ensures that the enumerable argument contains at most the specified number of elements. + /// + /// The enumerable type of the argument value. + /// The argument to validate. + /// The maximum allowed number of elements. + /// Optional custom error message. + /// The validated argument for chaining. + /// The element count exceeds . public static ref readonly ArgumentNotNull MaxCount(in this ArgumentNotNull argument, int maxCount, string? message = null) @@ -67,6 +105,16 @@ public static ref readonly ArgumentNotNull MaxCount(in this ArgumentNotNul return ref argument; } + /// + /// Ensures that the enumerable argument element count is within the specified range. + /// + /// The enumerable type of the argument value. + /// The argument to validate. + /// The minimum required number of elements. + /// The maximum allowed number of elements. + /// Optional custom error message. + /// The validated argument for chaining. + /// The element count is outside the range of to . public static ref readonly ArgumentNotNull InRange(in this ArgumentNotNull argument, int minCount, int maxCount, string? message = null) diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/GuidArgumentExtensions.cs b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/GuidArgumentExtensions.cs index 5c84367b..bea219bf 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/GuidArgumentExtensions.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/GuidArgumentExtensions.cs @@ -4,8 +4,18 @@ // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; +/// +/// Provides validation extension methods for . +/// public static class GuidArgumentExtensions { + /// + /// Ensures that the argument is not . + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value is . public static ref readonly Argument NotEmpty(in this Argument argument, string? message = null) { @@ -17,6 +27,14 @@ public static ref readonly Argument NotEmpty(in this Argument argume return ref argument; } + /// + /// Ensures that the nullable argument has a value and is not . + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value is . + /// The argument value is . public static ref readonly Argument NotEmpty(in this Argument argument, string? message = null) { @@ -33,6 +51,13 @@ public static ref readonly Argument NotEmpty(in this Argument argume return ref argument; } + /// + /// Ensures that the argument is not . + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value is . public static ref readonly ArgumentNotNull NotEmpty(in this ArgumentNotNull argument, string? message = null) { @@ -44,6 +69,13 @@ public static ref readonly ArgumentNotNull NotEmpty(in this ArgumentNotNul return ref argument; } + /// + /// Ensures that the nullable argument is not . + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value is . public static ref readonly ArgumentNotNull NotEmpty(in this ArgumentNotNull argument, string? message = null) { diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/IoArgumentExtensions.cs b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/IoArgumentExtensions.cs index 52a41f4a..5c307dcb 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/IoArgumentExtensions.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/IoArgumentExtensions.cs @@ -1,11 +1,23 @@ -using System.IO; +using System; +using System.IO; using CreativeCoders.Core.IO; // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; +/// +/// Provides file system validation extension methods for . +/// public static class IoArgumentExtensions { + /// + /// Ensures that the argument is not and refers to an existing file. + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument as for chaining. + /// The argument value is . + /// The file specified by the argument does not exist. public static ArgumentNotNull FileExists(in this Argument argument, string message = null) { @@ -23,6 +35,13 @@ public static ArgumentNotNull FileExists(in this Argument argume return new ArgumentNotNull(argument.Value, argument.Name); } + /// + /// Ensures that the argument refers to an existing file. + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument for chaining. + /// The file specified by the argument does not exist. public static ref readonly ArgumentNotNull FileExists( in this ArgumentNotNull argument, string message = null) { @@ -35,6 +54,14 @@ public static ref readonly ArgumentNotNull FileExists( return ref argument; } + /// + /// Ensures that the argument is not and refers to an existing directory. + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument as for chaining. + /// The argument value is . + /// The directory specified by the argument does not exist. public static ArgumentNotNull DirectoryExists(in this Argument argument, string message = null) { @@ -52,6 +79,13 @@ public static ArgumentNotNull DirectoryExists(in this Argument a return new ArgumentNotNull(argument.Value, argument.Name); } + /// + /// Ensures that the argument refers to an existing directory. + /// + /// The argument to validate. + /// Optional custom error message. + /// The validated argument for chaining. + /// The directory specified by the argument does not exist. public static ref readonly ArgumentNotNull DirectoryExists( in this ArgumentNotNull argument, string message = null) { diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/MustArgumentExtensions.cs b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/MustArgumentExtensions.cs index 41cbca0e..1e9083c2 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/MustArgumentExtensions.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/MustArgumentExtensions.cs @@ -4,8 +4,21 @@ // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; +/// +/// Provides generic predicate-based validation extension methods for +/// and . +/// public static class MustArgumentExtensions { + /// + /// Ensures that the argument satisfies the specified predicate. + /// + /// The type of the argument value. + /// The argument to validate. + /// The condition the argument value must satisfy. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value does not satisfy . public static ref readonly Argument Must(in this Argument argument, Func predicate, string? message = null) { @@ -17,6 +30,15 @@ public static ref readonly Argument Must(in this Argument argument, Fun return ref argument; } + /// + /// Ensures that the argument does not satisfy the specified predicate. + /// + /// The type of the argument value. + /// The argument to validate. + /// The condition the argument value must not satisfy. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value satisfies . public static ref readonly Argument MustNot(in this Argument argument, Func predicate, string? message = null) { @@ -28,6 +50,15 @@ public static ref readonly Argument MustNot(in this Argument argument, return ref argument; } + /// + /// Ensures that the non-null argument satisfies the specified predicate. + /// + /// The type of the argument value. + /// The argument to validate. + /// The condition the argument value must satisfy. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value does not satisfy . public static ref readonly ArgumentNotNull Must(in this ArgumentNotNull argument, Func predicate, string? message = null) @@ -40,6 +71,15 @@ public static ref readonly ArgumentNotNull Must(in this ArgumentNotNull return ref argument; } + /// + /// Ensures that the non-null argument does not satisfy the specified predicate. + /// + /// The type of the argument value. + /// The argument to validate. + /// The condition the argument value must not satisfy. + /// Optional custom error message. + /// The validated argument for chaining. + /// The argument value satisfies . public static ref readonly ArgumentNotNull MustNot(in this ArgumentNotNull argument, Func predicate, string? message = null) diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/NullArgumentExtensions.cs b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/NullArgumentExtensions.cs index c61d1708..a7aa5976 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/NullArgumentExtensions.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/NullArgumentExtensions.cs @@ -4,8 +4,19 @@ // ReSharper disable once CheckNamespace namespace CreativeCoders.Core; +/// +/// Provides null validation extension methods for . +/// public static class NullArgumentExtensions { + /// + /// Ensures that the argument is not . + /// + /// The type of the argument value. + /// The argument to validate. + /// Optional custom error message. + /// The validated argument as for chaining. + /// The argument value is . public static ArgumentNotNull NotNull(in this Argument argument, string? message = null) { if (argument.Value is null) @@ -16,6 +27,14 @@ public static ArgumentNotNull NotNull(in this Argument argument, strin return new ArgumentNotNull(argument.Value, argument.Name); } + /// + /// Ensures that the argument is . + /// + /// The type of the argument value. + /// The argument to validate. + /// Optional custom error message. + /// The validated value. + /// The argument value is not . public static T? Null(in this Argument argument, string? message = null) { if (argument.Value is not null) diff --git a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/StringArgumentExtensions.cs b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/StringArgumentExtensions.cs index a043d870..93348be7 100644 --- a/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/StringArgumentExtensions.cs +++ b/source/Core/CreativeCoders.Core/EnsureArguments/Extensions/StringArgumentExtensions.cs @@ -5,8 +5,20 @@ namespace CreativeCoders.Core; #nullable enable +/// +/// Provides string validation extension methods for . +/// public static class StringArgumentExtensions { + /// + /// Ensures that the string argument is not and has at least the specified minimum length. + /// + /// The argument to validate. + /// The minimum allowed length. + /// Optional custom error message. + /// The validated argument as for chaining. + /// The argument value is . + /// The string length is less than . public static ArgumentNotNull HasMinLength(this Argument argument, uint minLength, string? message = null) { if (argument.Value is null) @@ -22,6 +34,14 @@ public static ArgumentNotNull HasMinLength(this Argument argumen return new ArgumentNotNull(argument.Value, argument.Name); } + /// + /// Ensures that the string argument has at least the specified minimum length. + /// + /// The argument to validate. + /// The minimum allowed length. + /// Optional custom error message. + /// The validated argument for chaining. + /// The string length is less than . public static ref readonly ArgumentNotNull HasMinLength(this in ArgumentNotNull argument, uint minLength, string? message = null) { if (argument.Value.Length < minLength) @@ -32,6 +52,15 @@ public static ref readonly ArgumentNotNull HasMinLength(this in Argument return ref argument; } + /// + /// Ensures that the string argument is not and does not exceed the specified maximum length. + /// + /// The argument to validate. + /// The maximum allowed length. + /// Optional custom error message. + /// The validated argument as for chaining. + /// The argument value is . + /// The string length exceeds . public static ArgumentNotNull HasMaxLength(this Argument argument, uint maxLength, string? message = null) { @@ -48,6 +77,14 @@ public static ArgumentNotNull HasMaxLength(this Argument argumen return new ArgumentNotNull(argument.Value, argument.Name); } + /// + /// Ensures that the string argument does not exceed the specified maximum length. + /// + /// The argument to validate. + /// The maximum allowed length. + /// Optional custom error message. + /// The validated argument for chaining. + /// The string length exceeds . public static ref readonly ArgumentNotNull HasMaxLength(this in ArgumentNotNull argument, uint maxLength, string? message = null) { if (argument.Value.Length > maxLength) diff --git a/source/Core/CreativeCoders.Core/Enums/EnumExtensions.cs b/source/Core/CreativeCoders.Core/Enums/EnumExtensions.cs index df821cdb..c67b7966 100644 --- a/source/Core/CreativeCoders.Core/Enums/EnumExtensions.cs +++ b/source/Core/CreativeCoders.Core/Enums/EnumExtensions.cs @@ -4,15 +4,29 @@ namespace CreativeCoders.Core.Enums; +/// +/// Provides extension methods for types. +/// public static class EnumExtensions { private static readonly EnumStringConverter __enumToStringConverter = new EnumStringConverter(); + /// + /// Converts the enum value to its string representation using metadata. + /// + /// The enum value to convert. + /// The string representation of the enum value. public static string ToText(this Enum enumValue) { return __enumToStringConverter.Convert(enumValue); } + /// + /// Enumerates all individual flag values that are set on the specified flags enum value. + /// + /// The type of the flags enum. + /// The flags enum value to decompose. + /// A sequence of individual flag values that are set in . public static IEnumerable EnumerateFlags(this T flags) where T : struct, Enum { diff --git a/source/Core/CreativeCoders.Core/Enums/EnumStringConverter.cs b/source/Core/CreativeCoders.Core/Enums/EnumStringConverter.cs index ad8b838a..010d10a3 100644 --- a/source/Core/CreativeCoders.Core/Enums/EnumStringConverter.cs +++ b/source/Core/CreativeCoders.Core/Enums/EnumStringConverter.cs @@ -8,6 +8,9 @@ namespace CreativeCoders.Core.Enums; +/// +/// Converts between enum values and their string representations using metadata. +/// public class EnumStringConverter : IEnumToStringConverter { private static readonly ICache> __textToEnumMappingCache = @@ -38,12 +41,16 @@ private static IEnumStringAttribute GetEnumStringAttribute(ICustomAttributeProvi return null; } + /// + /// Clears the internal enum-to-string and string-to-enum mapping caches. + /// public static void ClearCaches() { __enumToTextCache.Clear(); __textToEnumMappingCache.Clear(); } + /// public string Convert(Enum enumValue) { if (enumValue == null) @@ -56,6 +63,7 @@ public string Convert(Enum enumValue) return GetTextForField(fieldInfo, enumValue); } + /// public T Convert(string text) where T : Enum { diff --git a/source/Core/CreativeCoders.Core/Enums/EnumStringValueAttribute.cs b/source/Core/CreativeCoders.Core/Enums/EnumStringValueAttribute.cs index e3459c96..f19e6fb4 100644 --- a/source/Core/CreativeCoders.Core/Enums/EnumStringValueAttribute.cs +++ b/source/Core/CreativeCoders.Core/Enums/EnumStringValueAttribute.cs @@ -2,13 +2,21 @@ namespace CreativeCoders.Core.Enums; +/// +/// Specifies a custom string representation for an enum field or enum type. +/// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Enum)] public class EnumStringValueAttribute : Attribute, IEnumStringAttribute { + /// + /// Initializes a new instance of the class. + /// + /// The string representation for the enum value. public EnumStringValueAttribute(string text) { Text = text; } + /// public string Text { get; } } diff --git a/source/Core/CreativeCoders.Core/Enums/EnumUtils.cs b/source/Core/CreativeCoders.Core/Enums/EnumUtils.cs index 7322d297..e589548f 100644 --- a/source/Core/CreativeCoders.Core/Enums/EnumUtils.cs +++ b/source/Core/CreativeCoders.Core/Enums/EnumUtils.cs @@ -5,9 +5,17 @@ namespace CreativeCoders.Core.Enums; +/// +/// Provides static utility methods for working with enum types and their reflection metadata. +/// public static class EnumUtils { // ReSharper disable once ReturnTypeCanBeEnumerable.Global + /// + /// Gets the for each value defined in the enum type . + /// + /// The enum type. + /// An array of instances for the enum values. public static FieldInfo[] GetValuesFieldInfos() where T : Enum { @@ -21,6 +29,11 @@ public static FieldInfo[] GetValuesFieldInfos() .ToArray(); } + /// + /// Gets all values defined in the enum type . + /// + /// The enum type. + /// An array of values. public static Enum[] GetEnumValues() where T : Enum { @@ -28,6 +41,14 @@ public static Enum[] GetEnumValues() return values.Cast().ToArray(); } + /// + /// Gets the enum value of type that corresponds to the specified integer value. + /// + /// The enum type. + /// The integer value to match. + /// + /// The matching enum value, or the first defined value if no match is found. + /// public static T GetEnum(int enumIntValue) where T : Enum { @@ -41,14 +62,27 @@ public static T GetEnum(int enumIntValue) return (T) enumValues.First(); } + /// + /// Gets the for the specified enum value. + /// + /// The enum value to look up. + /// The representing the enum field. public static FieldInfo GetFieldInfoForEnum(Enum enumValue) { - Ensure.IsNotNull(enumValue, nameof(enumValue)); + Ensure.IsNotNull(enumValue); var enumType = enumValue.GetType(); return enumType.GetField(enumValue.ToString()); } + /// + /// Gets a dictionary mapping each enum value to its for the specified enum type. + /// + /// The enum type to inspect. + /// + /// A dictionary mapping enum values to their , or an empty dictionary + /// if is not an enum. + /// public static IDictionary GetEnumFieldInfos(Type enumType) { if (!enumType.IsEnum) @@ -73,6 +107,11 @@ public static IDictionary GetEnumFieldInfos(Type enumType) return enumFieldInfos; } + /// + /// Gets a dictionary mapping each enum value to its for the enum type . + /// + /// The enum type. + /// A dictionary mapping enum values to their . public static IDictionary GetEnumFieldInfos() where T : Enum { diff --git a/source/Core/CreativeCoders.Core/Enums/IEnumStringAttribute.cs b/source/Core/CreativeCoders.Core/Enums/IEnumStringAttribute.cs index 83025817..9487aa4e 100644 --- a/source/Core/CreativeCoders.Core/Enums/IEnumStringAttribute.cs +++ b/source/Core/CreativeCoders.Core/Enums/IEnumStringAttribute.cs @@ -1,6 +1,12 @@ namespace CreativeCoders.Core.Enums; +/// +/// Defines a contract for attributes that provide a custom string representation for enum values. +/// public interface IEnumStringAttribute { + /// + /// Gets the custom string representation for the enum value. + /// string Text { get; } } diff --git a/source/Core/CreativeCoders.Core/Enums/IEnumToStringConverter.cs b/source/Core/CreativeCoders.Core/Enums/IEnumToStringConverter.cs index d98533dc..f1c19236 100644 --- a/source/Core/CreativeCoders.Core/Enums/IEnumToStringConverter.cs +++ b/source/Core/CreativeCoders.Core/Enums/IEnumToStringConverter.cs @@ -3,11 +3,25 @@ namespace CreativeCoders.Core.Enums; +/// +/// Defines a contract for converting between enum values and their string representations. +/// [PublicAPI] public interface IEnumToStringConverter { + /// + /// Converts the specified enum value to its string representation. + /// + /// The enum value to convert. + /// The string representation of the enum value. string Convert(Enum enumValue); + /// + /// Converts the specified string to the corresponding enum value of type . + /// + /// The enum type to convert to. + /// The string to convert. + /// The enum value matching , or the default value if no match is found. T Convert(string text) where T : Enum; } diff --git a/source/Core/CreativeCoders.Core/Error/DelegateErrorHandler.cs b/source/Core/CreativeCoders.Core/Error/DelegateErrorHandler.cs index d5196c02..cf971bd8 100644 --- a/source/Core/CreativeCoders.Core/Error/DelegateErrorHandler.cs +++ b/source/Core/CreativeCoders.Core/Error/DelegateErrorHandler.cs @@ -3,18 +3,26 @@ namespace CreativeCoders.Core.Error; +/// +/// Implements by delegating exception handling to a provided . +/// [PublicAPI] public class DelegateErrorHandler : IErrorHandler { private readonly Action _handleException; + /// + /// Initializes a new instance of the class. + /// + /// The delegate invoked to handle exceptions. public DelegateErrorHandler(Action handleException) { - Ensure.IsNotNull(handleException, nameof(handleException)); + Ensure.IsNotNull(handleException); _handleException = handleException; } + /// public void HandleException(Exception exception) { _handleException(exception); diff --git a/source/Core/CreativeCoders.Core/Error/IErrorHandler.cs b/source/Core/CreativeCoders.Core/Error/IErrorHandler.cs index 3cbb5d26..a9f2d822 100644 --- a/source/Core/CreativeCoders.Core/Error/IErrorHandler.cs +++ b/source/Core/CreativeCoders.Core/Error/IErrorHandler.cs @@ -2,13 +2,14 @@ namespace CreativeCoders.Core.Error; -/// Interface for an error handler. +/// +/// Defines a contract for handling exceptions. +/// public interface IErrorHandler { - ///------------------------------------------------------------------------------------------------- - /// Handles the exception . - /// - /// The exception that gets handled. - ///------------------------------------------------------------------------------------------------- + /// + /// Handles the specified exception. + /// + /// The exception to handle. void HandleException(Exception exception); } diff --git a/source/Core/CreativeCoders.Core/Error/NullErrorHandler.cs b/source/Core/CreativeCoders.Core/Error/NullErrorHandler.cs index 1c051b2c..c62f40e1 100644 --- a/source/Core/CreativeCoders.Core/Error/NullErrorHandler.cs +++ b/source/Core/CreativeCoders.Core/Error/NullErrorHandler.cs @@ -4,20 +4,16 @@ namespace CreativeCoders.Core.Error; -///------------------------------------------------------------------------------------------------- -/// An error handler which simply does nothing. -/// -/// -///------------------------------------------------------------------------------------------------- +/// +/// Implements as a no-op that silently ignores all exceptions. +/// [PublicAPI] [ExcludeFromCodeCoverage] public class NullErrorHandler : IErrorHandler { - ///------------------------------------------------------------------------------------------------- - /// Static instance for use, when a is needed. - /// - /// The instance. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets a shared instance of . + /// public static IErrorHandler Instance { get; } = new NullErrorHandler(); /// diff --git a/source/Core/CreativeCoders.Core/EventHandlerEx.cs b/source/Core/CreativeCoders.Core/EventHandlerEx.cs index e65f1aba..f35b4441 100644 --- a/source/Core/CreativeCoders.Core/EventHandlerEx.cs +++ b/source/Core/CreativeCoders.Core/EventHandlerEx.cs @@ -4,8 +4,20 @@ namespace CreativeCoders.Core; +/// +/// Represents a method that handles an event with a strongly typed sender and event argument. +/// +/// The type of the event sender. +/// The type of the event argument. +/// The source of the event. +/// The event data. [PublicAPI] public delegate void EventHandlerEx(TSender sender, TEventArg e); +/// +/// Represents a method that handles an event with a strongly typed sender. +/// +/// The type of the event sender. +/// The source of the event. [PublicAPI] public delegate void EventHandlerEx(TSender sender); diff --git a/source/Core/CreativeCoders.Core/Executing/IExecutable.cs b/source/Core/CreativeCoders.Core/Executing/IExecutable.cs index 1f204812..36a41c3b 100644 --- a/source/Core/CreativeCoders.Core/Executing/IExecutable.cs +++ b/source/Core/CreativeCoders.Core/Executing/IExecutable.cs @@ -2,8 +2,14 @@ namespace CreativeCoders.Core.Executing; +/// +/// Defines an executable operation with no parameters and no return value. +/// [PublicAPI] public interface IExecutable { + /// + /// Executes the operation. + /// void Execute(); } diff --git a/source/Core/CreativeCoders.Core/Executing/IExecutableGeneric.cs b/source/Core/CreativeCoders.Core/Executing/IExecutableGeneric.cs index c32d1054..585d81e2 100644 --- a/source/Core/CreativeCoders.Core/Executing/IExecutableGeneric.cs +++ b/source/Core/CreativeCoders.Core/Executing/IExecutableGeneric.cs @@ -2,8 +2,16 @@ namespace CreativeCoders.Core.Executing; +/// +/// Defines an executable operation that accepts a strongly-typed parameter and has no return value. +/// +/// The type of the parameter. [PublicAPI] public interface IExecutable { + /// + /// Executes the operation with the specified parameter. + /// + /// The parameter for the operation. void Execute(T parameter); } diff --git a/source/Core/CreativeCoders.Core/Executing/IExecutableWithParameter.cs b/source/Core/CreativeCoders.Core/Executing/IExecutableWithParameter.cs index be6c99e7..a32e5411 100644 --- a/source/Core/CreativeCoders.Core/Executing/IExecutableWithParameter.cs +++ b/source/Core/CreativeCoders.Core/Executing/IExecutableWithParameter.cs @@ -2,8 +2,15 @@ namespace CreativeCoders.Core.Executing; +/// +/// Defines an executable operation that accepts an untyped parameter and has no return value. +/// [PublicAPI] public interface IExecutableWithParameter { + /// + /// Executes the operation with the specified parameter. + /// + /// The parameter for the operation. void Execute(object parameter); } diff --git a/source/Core/CreativeCoders.Core/Executing/IExecutableWithResult.cs b/source/Core/CreativeCoders.Core/Executing/IExecutableWithResult.cs index 0434f3de..c23c93bb 100644 --- a/source/Core/CreativeCoders.Core/Executing/IExecutableWithResult.cs +++ b/source/Core/CreativeCoders.Core/Executing/IExecutableWithResult.cs @@ -2,8 +2,16 @@ namespace CreativeCoders.Core.Executing; +/// +/// Defines an executable operation with no parameters that returns a strongly-typed result. +/// +/// The type of the result. [PublicAPI] public interface IExecutableWithResult { + /// + /// Executes the operation and returns the result. + /// + /// The result of the operation. T Execute(); } diff --git a/source/Core/CreativeCoders.Core/Executing/IExecutableWithResultGeneric.cs b/source/Core/CreativeCoders.Core/Executing/IExecutableWithResultGeneric.cs index b207bbc6..40696885 100644 --- a/source/Core/CreativeCoders.Core/Executing/IExecutableWithResultGeneric.cs +++ b/source/Core/CreativeCoders.Core/Executing/IExecutableWithResultGeneric.cs @@ -2,8 +2,18 @@ namespace CreativeCoders.Core.Executing; +/// +/// Defines an executable operation that accepts a strongly-typed parameter and returns a strongly-typed result. +/// +/// The type of the parameter. +/// The type of the result. [PublicAPI] public interface IExecutableWithResult { + /// + /// Executes the operation with the specified parameter and returns the result. + /// + /// The parameter for the operation. + /// The result of the operation. TResult Execute(TParameter parameter); } diff --git a/source/Core/CreativeCoders.Core/FluentInterfaceExtensions.cs b/source/Core/CreativeCoders.Core/FluentInterfaceExtensions.cs index 421b5fe1..805df612 100644 --- a/source/Core/CreativeCoders.Core/FluentInterfaceExtensions.cs +++ b/source/Core/CreativeCoders.Core/FluentInterfaceExtensions.cs @@ -6,33 +6,74 @@ namespace CreativeCoders.Core; +/// +/// Provides extension methods for building fluent interfaces. +/// [ExcludeFromCodeCoverage] [PublicAPI] public static class FluentInterfaceExtensions { + /// + /// Executes the specified action and returns the fluent interface instance for chaining. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// The action to execute. + /// The fluent interface instance. public static TFluent Fluent(this TFluent fluentInterface, Action fluentAction) { fluentAction(); return fluentInterface; } + /// + /// Executes the specified action and returns the fluent interface instance for chaining. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// The action to execute. + /// The fluent interface instance. public static TFluent Fluent(this TFluent fluentInterface, Action fluentAction) { fluentAction(fluentInterface); return fluentInterface; } + /// + /// Executes the specified function and returns the fluent interface instance for chaining. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// The function to execute. + /// The fluent interface instance. // ReSharper disable once UnusedParameter.Global public static TFluent Fluent(this TFluent fluentInterface, Func fluentFunction) { return fluentFunction(); } + /// + /// Executes the specified function and returns the fluent interface instance for chaining. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// The function to execute. + /// The fluent interface instance. public static TFluent Fluent(this TFluent fluentInterface, Func fluentFunction) { return fluentFunction(fluentInterface); } + /// + /// Executes the specified action only if the condition is met and returns the fluent interface instance. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// + /// to execute the action; otherwise, . + /// + /// The action to execute. + /// The fluent interface instance. public static TFluent FluentIf(this TFluent fluentInterface, bool condition, Action fluentAction) { if (condition) @@ -43,6 +84,16 @@ public static TFluent FluentIf(this TFluent fluentInterface, bool condi return fluentInterface; } + /// + /// Executes the specified action only if the condition is met and returns the fluent interface instance. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// + /// to execute the action; otherwise, . + /// + /// The action to execute. + /// The fluent interface instance. public static TFluent FluentIf(this TFluent fluentInterface, bool condition, Action fluentAction) { @@ -54,6 +105,16 @@ public static TFluent FluentIf(this TFluent fluentInterface, bool condi return fluentInterface; } + /// + /// Executes the specified function only if the condition is met and returns the fluent interface instance. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// + /// to execute the function; otherwise, . + /// + /// The function to execute. + /// The fluent interface instance. public static TFluent FluentIf(this TFluent fluentInterface, bool condition, Func fluentFunction) { @@ -62,6 +123,16 @@ public static TFluent FluentIf(this TFluent fluentInterface, bool condi : fluentInterface; } + /// + /// Executes the specified function only if the condition is met and returns the fluent interface instance. + /// + /// The type of the fluent interface. + /// The fluent interface instance. + /// + /// to execute the function; otherwise, . + /// + /// The function to execute. + /// The fluent interface instance. public static TFluent FluentIf(this TFluent fluentInterface, bool condition, Func fluentFunction) { diff --git a/source/Core/CreativeCoders.Core/IO/DirectoryCleanUp.cs b/source/Core/CreativeCoders.Core/IO/DirectoryCleanUp.cs index d807238c..f53efd7f 100644 --- a/source/Core/CreativeCoders.Core/IO/DirectoryCleanUp.cs +++ b/source/Core/CreativeCoders.Core/IO/DirectoryCleanUp.cs @@ -2,9 +2,20 @@ namespace CreativeCoders.Core.IO; +/// +/// Deletes a directory when disposed, providing automatic cleanup of temporary directories. +/// +/// The path of the directory to delete on disposal. +/// +/// to delete the directory, its subdirectories, and all files; otherwise, . +/// +/// +/// to rethrow exceptions that occur during deletion; to suppress them. +/// public sealed class DirectoryCleanUp(string directoryName, bool recursive = true, bool throwException = false) : IDisposable { + /// public void Dispose() { try @@ -23,5 +34,8 @@ public void Dispose() } } + /// + /// Gets the path of the directory that will be deleted on disposal. + /// public string DirectoryName { get; } = Ensure.IsNotNullOrWhitespace(directoryName); } diff --git a/source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs b/source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs index 1345705e..fc0a9c23 100644 --- a/source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs +++ b/source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs @@ -4,8 +4,17 @@ namespace CreativeCoders.Core.IO; +/// +/// Provides extension methods for to ensure directories exist. +/// public static class DirectoryExtensions { + /// + /// Ensures that the specified directory exists, creating it if necessary. + /// Does nothing if the path is or white-space. + /// + /// The directory service. + /// The directory path to ensure exists. public static void EnsureDirectoryExists(this IDirectory directory, string? directoryPath) { if (string.IsNullOrWhiteSpace(directoryPath)) @@ -16,6 +25,12 @@ public static void EnsureDirectoryExists(this IDirectory directory, string? dire directory.CreateDirectory(directoryPath); } + /// + /// Ensures that the parent directory for the specified file path exists, creating it if necessary. + /// Does nothing if the file path is or white-space, or if no parent directory can be determined. + /// + /// The directory service. + /// The file path whose parent directory to ensure exists. public static void EnsureDirectoryForFileNameExists(this IDirectory directory, string? filePath) { if (string.IsNullOrWhiteSpace(filePath)) diff --git a/source/Core/CreativeCoders.Core/IO/FileCleanUp.cs b/source/Core/CreativeCoders.Core/IO/FileCleanUp.cs index 5858de7e..6756cdd9 100644 --- a/source/Core/CreativeCoders.Core/IO/FileCleanUp.cs +++ b/source/Core/CreativeCoders.Core/IO/FileCleanUp.cs @@ -1,9 +1,17 @@ -using System; +using System; namespace CreativeCoders.Core.IO; +/// +/// Deletes a file when disposed, providing automatic cleanup of temporary files. +/// +/// The path of the file to delete on disposal. +/// +/// to rethrow exceptions that occur during deletion; to suppress them. +/// public sealed class FileCleanUp(string fileName, bool throwException = false) : IDisposable { + /// public void Dispose() { try @@ -22,5 +30,8 @@ public void Dispose() } } + /// + /// Gets the path of the file that will be deleted on disposal. + /// public string FileName { get; } = Ensure.IsNotNullOrWhitespace(fileName); } diff --git a/source/Core/CreativeCoders.Core/IO/FileSys.cs b/source/Core/CreativeCoders.Core/IO/FileSys.cs index 0cce0bff..258f3922 100644 --- a/source/Core/CreativeCoders.Core/IO/FileSys.cs +++ b/source/Core/CreativeCoders.Core/IO/FileSys.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; using JetBrains.Annotations; @@ -6,10 +6,17 @@ namespace CreativeCoders.Core.IO; +/// +/// Provides static access to file system operations through an instance. +/// [ExcludeFromCodeCoverage] [PublicAPI] public static class FileSys { + /// + /// Installs a custom implementation as the static file system provider. + /// + /// The file system implementation to use. public static void InstallFileSystemSupport(IFileSystemEx fileSystem) { Ensure.IsNotNull(fileSystem); @@ -17,25 +24,58 @@ public static void InstallFileSystemSupport(IFileSystemEx fileSystem) Instance = fileSystem; } + /// + /// Creates a new for monitoring file system changes. + /// + /// A new file system watcher instance. public static FileSystemWatcherBase CreateFileSystemWatcher() => Instance.CreateFileSystemWatcher(); + /// + /// Creates a new that monitors the specified directory. + /// + /// The directory path to monitor. + /// A new file system watcher instance for the specified path. public static FileSystemWatcherBase CreateFileSystemWatcher(string path) => Instance.CreateFileSystemWatcher(path); + /// + /// Creates a new that monitors the specified directory with a filter. + /// + /// The directory path to monitor. + /// The file name filter (e.g., "*.txt"). + /// A new file system watcher instance for the specified path and filter. public static FileSystemWatcherBase CreateFileSystemWatcher(string path, string filter) => Instance.CreateFileSystemWatcher(path, filter); private static IFileSystemEx Instance { get; set; } = new FileSystemEx(); + /// + /// Gets the service for file operations. + /// public static IFile File => Instance.File; + /// + /// Gets the service for directory operations. + /// public static IDirectory Directory => Instance.Directory; + /// + /// Gets the for creating file info instances. + /// public static IFileInfoFactory FileInfo => Instance.FileInfo; + /// + /// Gets the service for path operations. + /// public static IPath Path => Instance.Path; + /// + /// Gets the for creating directory info instances. + /// public static IDirectoryInfoFactory DirectoryInfo => Instance.DirectoryInfo; + /// + /// Gets the for creating drive info instances. + /// public static IDriveInfoFactory DriveInfo => Instance.DriveInfo; } diff --git a/source/Core/CreativeCoders.Core/IO/FileSystemEx.cs b/source/Core/CreativeCoders.Core/IO/FileSystemEx.cs index fe9242ac..13185fac 100644 --- a/source/Core/CreativeCoders.Core/IO/FileSystemEx.cs +++ b/source/Core/CreativeCoders.Core/IO/FileSystemEx.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; using JetBrains.Annotations; @@ -6,25 +6,33 @@ namespace CreativeCoders.Core.IO; +/// +/// Provides an extended file system implementation that adds file system watcher support +/// and self-installation to . +/// [PublicAPI] [ExcludeFromCodeCoverage] public class FileSystemEx : FileSystem, IFileSystemEx { + /// public void Install() { FileSys.InstallFileSystemSupport(this); } + /// public FileSystemWatcherBase CreateFileSystemWatcher() { return new FileSystemWatcherWrapper(this); } + /// public FileSystemWatcherBase CreateFileSystemWatcher(string path) { return new FileSystemWatcherWrapper(this, path); } + /// public FileSystemWatcherBase CreateFileSystemWatcher(string path, string filter) { return new FileSystemWatcherWrapper(this, path, filter); diff --git a/source/Core/CreativeCoders.Core/IO/IFileSystemEx.cs b/source/Core/CreativeCoders.Core/IO/IFileSystemEx.cs index b72dffa8..55df6019 100644 --- a/source/Core/CreativeCoders.Core/IO/IFileSystemEx.cs +++ b/source/Core/CreativeCoders.Core/IO/IFileSystemEx.cs @@ -1,18 +1,40 @@ -using System.IO.Abstractions; +using System.IO.Abstractions; using JetBrains.Annotations; #nullable enable namespace CreativeCoders.Core.IO; +/// +/// Extends with additional file system operations including +/// self-installation and file system watcher creation. +/// [PublicAPI] public interface IFileSystemEx : IFileSystem { + /// + /// Installs this file system instance as the global provider. + /// void Install(); + /// + /// Creates a new for monitoring file system changes. + /// + /// A new file system watcher instance. FileSystemWatcherBase CreateFileSystemWatcher(); + /// + /// Creates a new that monitors the specified directory. + /// + /// The directory path to monitor. + /// A new file system watcher instance for the specified path. FileSystemWatcherBase CreateFileSystemWatcher(string path); + /// + /// Creates a new that monitors the specified directory with a filter. + /// + /// The directory path to monitor. + /// The file name filter (e.g., "*.txt"). + /// A new file system watcher instance for the specified path and filter. FileSystemWatcherBase CreateFileSystemWatcher(string path, string filter); } diff --git a/source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs b/source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs index 5a3bd61d..4caa36f3 100644 --- a/source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs +++ b/source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs @@ -5,9 +5,18 @@ namespace CreativeCoders.Core.IO; +/// +/// Provides extension methods for registering IO services with the dependency injection container. +/// [ExcludeFromCodeCoverage] public static class IOServiceCollectionExtensions { + /// + /// Registers and as singleton services + /// in the dependency injection container. + /// + /// The service collection to add the registrations to. + /// The same instance for chaining. public static IServiceCollection AddFileSystem(this IServiceCollection services) { services.TryAddSingleton(); diff --git a/source/Core/CreativeCoders.Core/IO/PathExtensions.cs b/source/Core/CreativeCoders.Core/IO/PathExtensions.cs index f622dfaf..eab66f10 100644 --- a/source/Core/CreativeCoders.Core/IO/PathExtensions.cs +++ b/source/Core/CreativeCoders.Core/IO/PathExtensions.cs @@ -1,21 +1,47 @@ -using System.IO.Abstractions; +using System.IO.Abstractions; using System.Linq; namespace CreativeCoders.Core.IO; +/// +/// Provides extension methods for operations. +/// public static class PathExtensions { + /// + /// Replaces all invalid file name characters in the specified file name with the given replacement string. + /// + /// The path service. + /// The file name to sanitize. + /// The string to substitute for each invalid character. + /// A sanitized file name with invalid characters replaced. public static string ReplaceInvalidFileNameChars(this IPath path, string fileName, string replacement) { return path.GetInvalidFileNameChars() .Aggregate(fileName, (current, c) => current.Replace(c.ToString(), replacement)); } + /// + /// Determines whether the specified path is safe to use within the given base path, + /// preventing path traversal attacks. + /// + /// The path service. + /// The path to validate. + /// The base path that the checked path must remain within. + /// if the path is safe; otherwise, . public static bool IsSafe(this IPath path, string pathToCheck, string basePath) { return PathSafety.IsSafe(path, pathToCheck, basePath); } + /// + /// Ensures that the specified path is safe to use within the given base path, + /// throwing an exception if a path traversal is detected. + /// + /// The path service. + /// The path to validate. + /// The base path that the checked path must remain within. + /// The path attempts to traverse outside the base path. public static void EnsureSafe(this IPath path, string pathToCheck, string basePath) { PathSafety.EnsureSafe(path, pathToCheck, basePath); diff --git a/source/Core/CreativeCoders.Core/IO/PathSafety.cs b/source/Core/CreativeCoders.Core/IO/PathSafety.cs index 2d89d218..c4ccd605 100644 --- a/source/Core/CreativeCoders.Core/IO/PathSafety.cs +++ b/source/Core/CreativeCoders.Core/IO/PathSafety.cs @@ -8,16 +8,16 @@ namespace CreativeCoders.Core.IO; /// -/// Provides methods for path safety checks, especially to prevent path traversal attacks. +/// Provides methods for path safety checks to prevent path traversal attacks. /// public static class PathSafety { /// - /// Checks if the specified is safe to use within the given . + /// Determines whether the specified path is safe to use within the given base path. /// /// The path to check. /// The base path that the must be contained in. - /// true if the path is safe; otherwise, false. + /// if the path is safe; otherwise, . /// /// /// var isSafe = PathSafety.IsSafe("data/config.json", "/var/www"); @@ -29,12 +29,12 @@ public static bool IsSafe(string path, string basePath) } /// - /// Checks if the specified is safe to use within the given . + /// Determines whether the specified path is safe to use within the given base path. /// - /// The helper to use for path operations. + /// The service for path operations. /// The path to check. /// The base path that the must be contained in. - /// true if the path is safe; otherwise, false. + /// if the path is safe; otherwise, . public static bool IsSafe(IPath pathHelper, string path, string basePath) { Ensure.NotNull(pathHelper); @@ -55,25 +55,23 @@ public static bool IsSafe(IPath pathHelper, string path, string basePath) } /// - /// Ensures that the specified is safe to use within the given . - /// Throws an if the path is not safe. + /// Ensures that the specified path is safe to use within the given base path. /// /// The path to check. /// The base path that the must be contained in. - /// Thrown when the path is not safe. + /// The path attempts to traverse outside the base path. public static void EnsureSafe(string path, string basePath) { EnsureSafe(FileSys.Path, path, basePath); } /// - /// Ensures that the specified is safe to use within the given . - /// Throws an if the path is not safe. + /// Ensures that the specified path is safe to use within the given base path. /// - /// The helper to use for path operations. + /// The service for path operations. /// The path to check. /// The base path that the must be contained in. - /// Thrown when the path is not safe. + /// The path attempts to traverse outside the base path. public static void EnsureSafe(IPath pathHelper, string path, string basePath) { if (!IsSafe(pathHelper, path, basePath)) diff --git a/source/Core/CreativeCoders.Core/IO/StreamExtensions.cs b/source/Core/CreativeCoders.Core/IO/StreamExtensions.cs index 96ac75f7..193feb92 100644 --- a/source/Core/CreativeCoders.Core/IO/StreamExtensions.cs +++ b/source/Core/CreativeCoders.Core/IO/StreamExtensions.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Text; using System.Threading.Tasks; @@ -6,20 +6,43 @@ namespace CreativeCoders.Core.IO; +/// +/// Provides extension methods for reading content as strings. +/// public static class StreamExtensions { + /// + /// Reads the entire stream content as a string using the default encoding. + /// + /// The stream to read. + /// The string content of the stream. public static string ReadAsString(this Stream stream) { using var streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } + /// + /// Reads the entire stream content as a string using the specified encoding. + /// + /// The stream to read. + /// The character encoding to use. + /// The string content of the stream. public static string ReadAsString(this Stream stream, Encoding encoding) { using var streamReader = new StreamReader(stream, encoding); return streamReader.ReadToEnd(); } + /// + /// Reads the entire stream content as a string using the specified encoding and byte order mark detection. + /// + /// The stream to read. + /// The character encoding to use. + /// + /// to detect encoding from the byte order marks; otherwise, . + /// + /// The string content of the stream. public static string ReadAsString(this Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks) { @@ -27,18 +50,38 @@ public static string ReadAsString(this Stream stream, Encoding encoding, return streamReader.ReadToEnd(); } + /// + /// Asynchronously reads the entire stream content as a string using the default encoding. + /// + /// The stream to read. + /// A task containing the string content of the stream. public static async Task ReadAsStringAsync(this Stream stream) { using var streamReader = new StreamReader(stream); return await streamReader.ReadToEndAsync().ConfigureAwait(false); } + /// + /// Asynchronously reads the entire stream content as a string using the specified encoding. + /// + /// The stream to read. + /// The character encoding to use. + /// A task containing the string content of the stream. public static async Task ReadAsStringAsync(this Stream stream, Encoding encoding) { using var streamReader = new StreamReader(stream, encoding); return await streamReader.ReadToEndAsync().ConfigureAwait(false); } + /// + /// Asynchronously reads the entire stream content as a string using the specified encoding and byte order mark detection. + /// + /// The stream to read. + /// The character encoding to use. + /// + /// to detect encoding from the byte order marks; otherwise, . + /// + /// A task containing the string content of the stream. public static async Task ReadAsStringAsync(this Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks) { diff --git a/source/Core/CreativeCoders.Core/IO/StreamWrapper.cs b/source/Core/CreativeCoders.Core/IO/StreamWrapper.cs index f718de76..c900baa3 100644 --- a/source/Core/CreativeCoders.Core/IO/StreamWrapper.cs +++ b/source/Core/CreativeCoders.Core/IO/StreamWrapper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; @@ -9,50 +9,69 @@ namespace CreativeCoders.Core.IO; +/// +/// Wraps a and delegates all operations to the underlying stream, +/// allowing custom actions to be executed before and after disposal. +/// [PublicAPI] [ExcludeFromCodeCoverage] -public class StreamWrapper : Stream +public sealed class StreamWrapper : Stream { private readonly Stream _dataStream; private readonly Action _disposeAfterStreamAction; private readonly Action _disposeBeforeStreamAction; + /// + /// Initializes a new instance of the class with no disposal actions. + /// + /// The underlying stream to wrap. public StreamWrapper(Stream dataStream) : this(dataStream, NullAction.Instance, NullAction.Instance) { } + /// + /// Initializes a new instance of the class with custom disposal actions. + /// + /// The underlying stream to wrap. + /// The action to invoke before disposing the underlying stream. + /// The action to invoke after disposing the underlying stream. public StreamWrapper(Stream dataStream, Action disposeBeforeStreamAction, Action disposeAfterStreamAction) { - _dataStream = Ensure.NotNull(dataStream, nameof(dataStream)); + _dataStream = Ensure.NotNull(dataStream); _disposeBeforeStreamAction = disposeBeforeStreamAction; _disposeAfterStreamAction = disposeAfterStreamAction; } + /// public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { return _dataStream.BeginRead(buffer, offset, count, callback, state); } + /// public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { return _dataStream.BeginWrite(buffer, offset, count, callback, state); } + /// public override void Close() { _dataStream.Close(); } + /// public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { return _dataStream.CopyToAsync(destination, bufferSize, cancellationToken); } + /// protected override void Dispose(bool disposing) { _disposeBeforeStreamAction.Invoke(disposing); @@ -63,121 +82,147 @@ protected override void Dispose(bool disposing) _disposeAfterStreamAction.Invoke(disposing); } + /// public override int EndRead(IAsyncResult asyncResult) { return _dataStream.EndRead(asyncResult); } + /// public override void EndWrite(IAsyncResult asyncResult) { _dataStream.EndWrite(asyncResult); } + /// public override Task FlushAsync(CancellationToken cancellationToken) { return _dataStream.FlushAsync(cancellationToken); } + /// public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return _dataStream.ReadAsync(buffer, offset, count, cancellationToken); } + /// public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = new CancellationToken()) { return _dataStream.ReadAsync(buffer, cancellationToken); } + /// public override int ReadByte() { return _dataStream.ReadByte(); } + /// public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return _dataStream.WriteAsync(buffer, offset, count, cancellationToken); } + /// public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new CancellationToken()) { return _dataStream.WriteAsync(buffer, cancellationToken); } + /// public override void WriteByte(byte value) { _dataStream.WriteByte(value); } + /// public override bool Equals(object? obj) { return _dataStream.Equals(obj); } + /// public override int GetHashCode() { return _dataStream.GetHashCode(); } + /// public override string? ToString() { return _dataStream.ToString(); } + /// public override void Flush() { _dataStream.Flush(); } + /// public override int Read(byte[] buffer, int offset, int count) { return _dataStream.Read(buffer, offset, count); } + /// public override int Read(Span buffer) { return _dataStream.Read(buffer); } + /// public override long Seek(long offset, SeekOrigin origin) { return _dataStream.Seek(offset, origin); } + /// public override void SetLength(long value) { _dataStream.SetLength(value); } + /// public override void Write(byte[] buffer, int offset, int count) { _dataStream.Write(buffer, offset, count); } + /// public override bool CanRead => _dataStream.CanRead; + /// public override bool CanSeek => _dataStream.CanSeek; + /// public override bool CanWrite => _dataStream.CanWrite; + /// public override long Length => _dataStream.Length; + /// public override long Position { get => _dataStream.Position; set => _dataStream.Position = value; } + /// public override bool CanTimeout => _dataStream.CanTimeout; + /// public override int ReadTimeout { get => _dataStream.ReadTimeout; set => _dataStream.ReadTimeout = value; } + /// public override int WriteTimeout { get => _dataStream.WriteTimeout; diff --git a/source/Core/CreativeCoders.Core/Messaging/IMessenger.cs b/source/Core/CreativeCoders.Core/Messaging/IMessenger.cs index dc5f6b54..83bedaf1 100644 --- a/source/Core/CreativeCoders.Core/Messaging/IMessenger.cs +++ b/source/Core/CreativeCoders.Core/Messaging/IMessenger.cs @@ -4,17 +4,51 @@ namespace CreativeCoders.Core.Messaging; +/// +/// Provides a publish-subscribe messaging pattern that enables loosely-coupled communication +/// between components without requiring direct references to each other. +/// [PublicAPI] public interface IMessenger { + /// + /// Registers a receiver to handle messages of the specified type. + /// + /// The type of message to receive. + /// The object that receives the message. + /// The action to invoke when a message of the specified type is sent. + /// A disposable registration that unregisters the receiver when disposed. IDisposable Register(object receiver, Action action); + /// + /// Registers a receiver to handle messages of the specified type, with control over + /// whether the registration keeps the owner alive. + /// + /// The type of message to receive. + /// The object that receives the message. + /// The action to invoke when a message of the specified type is sent. + /// The mode that controls whether the registration prevents garbage collection of the owner. + /// A disposable registration that unregisters the receiver when disposed. IDisposable Register(object receiver, Action action, KeepOwnerAliveMode keepOwnerAliveMode); + /// + /// Unregisters a receiver from all message types. + /// + /// The object to unregister. void Unregister(object receiver); + /// + /// Unregisters a receiver from messages of the specified type. + /// + /// The type of message to unregister from. + /// The object to unregister. void Unregister(object receiver); + /// + /// Sends a message to all registered receivers of the specified type. + /// + /// The type of message to send. + /// The message instance to send. void Send(TMessage message); } diff --git a/source/Core/CreativeCoders.Core/Messaging/IMessengerRegistration.cs b/source/Core/CreativeCoders.Core/Messaging/IMessengerRegistration.cs index 461a3709..b4511ec9 100644 --- a/source/Core/CreativeCoders.Core/Messaging/IMessengerRegistration.cs +++ b/source/Core/CreativeCoders.Core/Messaging/IMessengerRegistration.cs @@ -2,13 +2,31 @@ namespace CreativeCoders.Core.Messaging; +/// +/// Represents a single message subscription within an , tracking +/// the receiver and providing methods to deliver messages or manage the registration lifecycle. +/// public interface IMessengerRegistration : IDisposable { + /// + /// Delivers a message to the registered receiver. + /// + /// The type of message to deliver. + /// The message instance to deliver. void Execute(TMessage message); + /// + /// Gets the object that registered to receive messages. + /// object Target { get; } + /// + /// Gets a value indicating whether the registration target is still alive and can receive messages. + /// bool IsAlive { get; } + /// + /// Notifies the registration that it has been removed from the messenger. + /// void RemovedFromMessenger(); } diff --git a/source/Core/CreativeCoders.Core/Messaging/Messages/CallbackMessage.cs b/source/Core/CreativeCoders.Core/Messaging/Messages/CallbackMessage.cs index c48f8cef..b05b0668 100644 --- a/source/Core/CreativeCoders.Core/Messaging/Messages/CallbackMessage.cs +++ b/source/Core/CreativeCoders.Core/Messaging/Messages/CallbackMessage.cs @@ -2,15 +2,25 @@ namespace CreativeCoders.Core.Messaging.Messages; +/// +/// Represents a message that carries a callback action to be executed by the receiver. +/// public class CallbackMessage : MessageBase { private readonly Action _callback; + /// + /// Initializes a new instance of the class. + /// + /// The action to invoke when the message is executed. public CallbackMessage(Action callback) { - _callback = Ensure.NotNull(callback, nameof(callback)); + _callback = Ensure.NotNull(callback); } + /// + /// Invokes the callback action associated with this message. + /// public void Execute() { _callback(); diff --git a/source/Core/CreativeCoders.Core/Messaging/Messages/DialogMessage.cs b/source/Core/CreativeCoders.Core/Messaging/Messages/DialogMessage.cs index 9fc7ab85..54549d00 100644 --- a/source/Core/CreativeCoders.Core/Messaging/Messages/DialogMessage.cs +++ b/source/Core/CreativeCoders.Core/Messaging/Messages/DialogMessage.cs @@ -1,11 +1,21 @@ namespace CreativeCoders.Core.Messaging.Messages; +/// +/// Represents a message intended to display a dialog to the user. +/// public class DialogMessage : MessageBase { + /// + /// Initializes a new instance of the class. + /// + /// The text to display in the dialog. public DialogMessage(string messageText) { MessageText = messageText; } + /// + /// Gets the text to display in the dialog. + /// public string MessageText { get; } } diff --git a/source/Core/CreativeCoders.Core/Messaging/Messages/MessageBase.cs b/source/Core/CreativeCoders.Core/Messaging/Messages/MessageBase.cs index 3cdc6ee1..abc7ea3a 100644 --- a/source/Core/CreativeCoders.Core/Messaging/Messages/MessageBase.cs +++ b/source/Core/CreativeCoders.Core/Messaging/Messages/MessageBase.cs @@ -1,6 +1,12 @@ namespace CreativeCoders.Core.Messaging.Messages; +/// +/// Serves as the base class for all messages sent through the system. +/// public class MessageBase { + /// + /// Gets or sets a value indicating whether the message has been handled by a receiver. + /// public bool IsHandled { get; set; } } diff --git a/source/Core/CreativeCoders.Core/Messaging/Messenger.cs b/source/Core/CreativeCoders.Core/Messaging/Messenger.cs index 95bdb1d4..8a0d8514 100644 --- a/source/Core/CreativeCoders.Core/Messaging/Messenger.cs +++ b/source/Core/CreativeCoders.Core/Messaging/Messenger.cs @@ -1,9 +1,20 @@ namespace CreativeCoders.Core.Messaging; +/// +/// Provides access to a default instance and a factory method for +/// creating new messenger instances. +/// public static class Messenger { + /// + /// Gets the default shared instance. + /// public static IMessenger Default { get; } = new MessengerImpl(); + /// + /// Creates a new independent instance. + /// + /// A new instance. public static IMessenger CreateInstance() { return new MessengerImpl(); diff --git a/source/Core/CreativeCoders.Core/Messaging/MessengerImpl.cs b/source/Core/CreativeCoders.Core/Messaging/MessengerImpl.cs index 0d5c3caf..37eda760 100644 --- a/source/Core/CreativeCoders.Core/Messaging/MessengerImpl.cs +++ b/source/Core/CreativeCoders.Core/Messaging/MessengerImpl.cs @@ -84,7 +84,7 @@ public void Unregister(object receiver) public void Send(TMessage message) { - Ensure.IsNotNull(message, nameof(message)); + Ensure.IsNotNull(message); var actions = GetRegistrationsForMessageType(); diff --git a/source/Core/CreativeCoders.Core/NullAction.cs b/source/Core/CreativeCoders.Core/NullAction.cs index ed1958f8..63e456fa 100644 --- a/source/Core/CreativeCoders.Core/NullAction.cs +++ b/source/Core/CreativeCoders.Core/NullAction.cs @@ -6,16 +6,29 @@ namespace CreativeCoders.Core; +/// +/// Provides a singleton no-op delegate. +/// [ExcludeFromCodeCoverage] [PublicAPI] public static class NullAction { + /// + /// Gets a no-op instance. + /// public static Action Instance { get; } = () => { }; } +/// +/// Provides a singleton no-op delegate. +/// +/// The type of the action parameter. [ExcludeFromCodeCoverage] [PublicAPI] public static class NullAction { + /// + /// Gets a no-op instance. + /// public static Action Instance { get; } = _ => { }; } diff --git a/source/Core/CreativeCoders.Core/NullObject.cs b/source/Core/CreativeCoders.Core/NullObject.cs index f055f292..1faa5f78 100644 --- a/source/Core/CreativeCoders.Core/NullObject.cs +++ b/source/Core/CreativeCoders.Core/NullObject.cs @@ -5,9 +5,15 @@ namespace CreativeCoders.Core; +/// +/// Provides a singleton null object instance. +/// [PublicAPI] [ExcludeFromCodeCoverage] public static class NullObject { + /// + /// Gets a shared instance. + /// public static object Instance { get; } = new object(); } diff --git a/source/Core/CreativeCoders.Core/ObjectExtensions.cs b/source/Core/CreativeCoders.Core/ObjectExtensions.cs index 778ba00b..3d3eb290 100644 --- a/source/Core/CreativeCoders.Core/ObjectExtensions.cs +++ b/source/Core/CreativeCoders.Core/ObjectExtensions.cs @@ -10,19 +10,42 @@ namespace CreativeCoders.Core; +/// +/// Provides extension methods for . +/// [PublicAPI] public static class ObjectExtensions { + /// + /// Converts the object to its string representation, returning the specified default value + /// if the object is . + /// + /// The object to convert. + /// The default value to return if the object is . + /// The string representation of the object, or if . public static string ToStringSafe(this object? obj, string defaultValue) { return obj?.ToString() ?? defaultValue; } + /// + /// Converts the object to its string representation, returning an empty string + /// if the object is . + /// + /// The object to convert. + /// The string representation of the object, or an empty string if . public static string ToStringSafe(this object? obj) { return obj.ToStringSafe(string.Empty); } + /// + /// Attempts to cast the object to the specified type, returning a default value on failure. + /// + /// The target type. + /// The object to cast. + /// The value to return if the cast fails. + /// The cast instance, or if the cast fails. public static T? As(this object instance, T? defaultValue) { if (instance is T value) @@ -33,8 +56,21 @@ public static string ToStringSafe(this object? obj) return defaultValue; } + /// + /// Attempts to cast the object to the specified type, returning the default value on failure. + /// + /// The target type. + /// The object to cast. + /// The cast instance, or the default value of if the cast fails. public static T? As(this object instance) => As(instance, default); + /// + /// Attempts to cast the object to the specified type. + /// + /// The target type. + /// The object to cast. + /// When this method returns, contains the cast instance if successful. This parameter is treated as uninitialized. + /// if the cast succeeds; otherwise, . public static bool TryAs(this object instance, [MaybeNullWhen(false)] out T asInstance) { if (instance is T value) @@ -47,8 +83,20 @@ public static bool TryAs(this object instance, [MaybeNullWhen(false)] out T a return false; } + /// + /// Casts the object to the specified type. + /// + /// The target type. + /// The object to cast. + /// The cast instance. + /// The object cannot be cast to type . public static T CastAs(this object instance) => (T)Ensure.NotNull(instance); + /// + /// Attempts to dispose the object asynchronously if it implements or . + /// + /// The object to dispose. + /// A representing the asynchronous operation. public static async ValueTask TryDisposeAsync(this object instance) { switch (instance) @@ -62,6 +110,14 @@ public static async ValueTask TryDisposeAsync(this object instance) } } + /// + /// Gets the value of the specified property using reflection. + /// + /// The type of the property value. + /// The object to read the property from. + /// The name of the property. + /// The property value. + /// The property does not exist on the object's type. public static T? GetPropertyValue(this object instance, string propertyName) { var propInfo = instance.GetType().GetProperty(propertyName); @@ -74,6 +130,14 @@ public static async ValueTask TryDisposeAsync(this object instance) return (T?)propInfo.GetValue(instance); } + /// + /// Sets the value of the specified property using reflection. + /// + /// The type of the property value. + /// The object to set the property on. + /// The name of the property. + /// The value to set. + /// The property does not exist on the object's type. public static void SetPropertyValue(this object instance, string propertyName, T? value) { var propInfo = instance.GetType().GetProperty(propertyName); @@ -86,6 +150,11 @@ public static void SetPropertyValue(this object instance, string propertyName propInfo.SetValue(instance, value); } + /// + /// Converts the object's public instance properties to a dictionary. + /// + /// The object to convert. + /// A dictionary containing the property names and values. public static Dictionary ToDictionary(this object obj) { Ensure.NotNull(obj); diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableSourcePropertyConverter.cs b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableSourcePropertyConverter.cs index 04d460f0..8399e0ae 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableSourcePropertyConverter.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableSourcePropertyConverter.cs @@ -1,16 +1,24 @@ namespace CreativeCoders.Core.ObjectLinking.Converters; +/// +/// Converts property values when the source property is a and the +/// target property is a non-nullable value type . Acts as the inverse of +/// . +/// +/// The underlying value type of the nullable source property. public class NullableSourcePropertyConverter : IPropertyValueConverter where T : struct { private readonly NullableTargetPropertyConverter _nullableTargetPropertyConverter = new NullableTargetPropertyConverter(); + /// public object Convert(object value, object parameter) { return _nullableTargetPropertyConverter.ConvertBack(value, parameter); } + /// public object ConvertBack(object value, object parameter) { return _nullableTargetPropertyConverter.Convert(value, parameter); diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableTargetPropertyConverter.cs b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableTargetPropertyConverter.cs index 384f190e..faec6237 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableTargetPropertyConverter.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/NullableTargetPropertyConverter.cs @@ -1,8 +1,14 @@ namespace CreativeCoders.Core.ObjectLinking.Converters; +/// +/// Converts property values when the target property is a and the +/// source property is a non-nullable value type . +/// +/// The underlying value type of the nullable target property. public class NullableTargetPropertyConverter : IPropertyValueConverter where T : struct { + /// public object Convert(object value, object parameter) { if (value is not T structValue) @@ -15,6 +21,7 @@ public object Convert(object value, object parameter) return nullableValue; } + /// public object ConvertBack(object value, object parameter) { if (value is T nullableValue) diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringSourcePropertyConverter.cs b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringSourcePropertyConverter.cs index 12191bdb..2a982bf1 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringSourcePropertyConverter.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringSourcePropertyConverter.cs @@ -1,15 +1,23 @@ namespace CreativeCoders.Core.ObjectLinking.Converters; +/// +/// Converts property values when the source property is a and the target +/// property is of type . Acts as the inverse of +/// . +/// +/// The type of the target property value. public class StringSourcePropertyConverter : IPropertyValueConverter { private readonly StringTargetPropertyConverter _stringTargetPropertyConverter = new StringTargetPropertyConverter(); + /// public object Convert(object value, object parameter) { return _stringTargetPropertyConverter.ConvertBack(value, parameter); } + /// public object ConvertBack(object value, object parameter) { return _stringTargetPropertyConverter.Convert(value, parameter); diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringTargetPropertyConverter.cs b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringTargetPropertyConverter.cs index 022f56e6..64d50641 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringTargetPropertyConverter.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/Converters/StringTargetPropertyConverter.cs @@ -2,13 +2,21 @@ namespace CreativeCoders.Core.ObjectLinking.Converters; +/// +/// Converts property values when the target property is a and the source +/// property is of type . Conversion to string uses +/// ; conversion back uses . +/// +/// The type of the source property value. public class StringTargetPropertyConverter : IPropertyValueConverter { + /// public object Convert(object value, object parameter) { return value?.ToString(); } + /// public object ConvertBack(object value, object parameter) { if (value is not string text) diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/IPropertyValueConverter.cs b/source/Core/CreativeCoders.Core/ObjectLinking/IPropertyValueConverter.cs index a55fe6bf..f40eaa13 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/IPropertyValueConverter.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/IPropertyValueConverter.cs @@ -2,10 +2,25 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Defines methods for converting property values between source and target objects in a property link. +/// [PublicAPI] public interface IPropertyValueConverter { + /// + /// Converts a source property value to the target property type. + /// + /// The source property value to convert. + /// An optional conversion parameter. + /// The converted value for the target property. object Convert(object value, object parameter); + /// + /// Converts a target property value back to the source property type. + /// + /// The target property value to convert back. + /// An optional conversion parameter. + /// The converted value for the source property. object ConvertBack(object value, object parameter); } diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/LinkDirection.cs b/source/Core/CreativeCoders.Core/ObjectLinking/LinkDirection.cs index 3cc65b77..d18ed3df 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/LinkDirection.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/LinkDirection.cs @@ -1,8 +1,22 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Specifies the direction in which property values are synchronized between linked objects. +/// public enum LinkDirection { + /// + /// Synchronizes property values from the source object to the target object only. + /// OneWayToTarget, + + /// + /// Synchronizes property values from the target object to the source object only. + /// OneWayFromTarget, + + /// + /// Synchronizes property values in both directions between source and target objects. + /// TwoWay } diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLink.cs b/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLink.cs index 19a6d5a4..8bc7188c 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLink.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLink.cs @@ -5,6 +5,11 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Manages a live link between two object instances, automatically synchronizing property values +/// when either object raises . Disposing the +/// link disconnects the event handlers. +/// public sealed class ObjectLink : IDisposable { private readonly object _instance0; @@ -13,6 +18,13 @@ public sealed class ObjectLink : IDisposable private readonly IEnumerable _linkItems; + /// + /// Initializes a new instance of the class, connects property change + /// event handlers, and performs the initial property value synchronization. + /// + /// The first linked object instance. + /// The second linked object instance. + /// The property link items defining the individual property links. public ObjectLink(object instance0, object instance1, IEnumerable linkItems) { _instance0 = instance0; @@ -60,6 +72,9 @@ private void NotifyPropertyChangedOnPropertyChanged(object sender, PropertyChang _linkItems.ForEach(linkItem => linkItem.HandleChange(sender, e.PropertyName)); } + /// + /// Disconnects the property change event handlers from both linked object instances. + /// public void Dispose() { DisconnectChangedEvents(); diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLinkBuilder.cs b/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLinkBuilder.cs index d4285297..3b0904c2 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLinkBuilder.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/ObjectLinkBuilder.cs @@ -4,6 +4,10 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Builds an between two object instances by discovering and resolving +/// declarations on their properties. +/// public class ObjectLinkBuilder { private readonly object _instance0; @@ -14,10 +18,18 @@ public class ObjectLinkBuilder private readonly Type _instance1Type; + /// + /// Initializes a new instance of the class. + /// + /// The first object instance to link. + /// The second object instance to link. + /// + /// or is . + /// public ObjectLinkBuilder(object instance0, object instance1) { - Ensure.IsNotNull(instance0, nameof(instance0)); - Ensure.IsNotNull(instance1, nameof(instance1)); + Ensure.IsNotNull(instance0); + Ensure.IsNotNull(instance1); _instance0 = instance0; _instance1 = instance1; @@ -25,6 +37,10 @@ public ObjectLinkBuilder(object instance0, object instance1) _instance1Type = _instance1.GetType(); } + /// + /// Builds and returns an that synchronizes properties between the two instances. + /// + /// A new instance managing the property links. public ObjectLink Build() { var linkItems = GetLinkItems(); diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLink.cs b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLink.cs index f25cce9a..bf644a97 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLink.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLink.cs @@ -1,8 +1,18 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Provides sentinel values used by implementations to +/// signal special conversion outcomes. +/// public static class PropertyLink { + /// + /// Gets a sentinel value indicating that the converter determined no property update should occur. + /// public static object DoNothing { get; } = new object(); + /// + /// Gets a sentinel value indicating that the conversion failed and the property should not be updated. + /// public static object Error { get; } = new object(); } diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkAttribute.cs b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkAttribute.cs index b9585ea2..c5afce07 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkAttribute.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkAttribute.cs @@ -3,25 +3,52 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Marks a property as linked to a property on a target object, enabling automatic value synchronization. +/// [PublicAPI] [AttributeUsage(AttributeTargets.Property)] public class PropertyLinkAttribute : Attribute { + /// + /// Initializes a new instance of the class. + /// + /// The type of the target object containing the linked property. + /// The name of the property on the target object to link to. public PropertyLinkAttribute(Type targetType, string targetPropertyName) { TargetType = targetType; TargetPropertyName = targetPropertyName; } + /// + /// Gets the type of the target object containing the linked property. + /// public Type TargetType { get; } + /// + /// Gets the name of the property on the target object to link to. + /// public string TargetPropertyName { get; } + /// + /// Gets or sets the direction in which property values are synchronized. + /// public LinkDirection Direction { get; set; } + /// + /// Gets or sets the type of the used to convert values between linked properties. + /// public Type Converter { get; set; } + /// + /// Gets or sets an optional parameter passed to the . + /// public object ConverterParameter { get; set; } + /// + /// Gets or sets a value indicating whether the source property is initialized with the target property value + /// when the link is established. + /// public bool InitWithTargetValue { get; set; } } diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkDefinition.cs b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkDefinition.cs index 50c86174..92576f70 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkDefinition.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkDefinition.cs @@ -3,23 +3,55 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Represents the definition of a property link between a source and target object, including +/// direction, converter, and initialization settings. +/// public class PropertyLinkDefinition { + /// + /// Gets or initializes the source object of the property link. + /// public object Source { get; init; } + /// + /// Gets or initializes the target object of the property link. + /// public object Target { get; init; } + /// + /// Gets or initializes the type of the target object. + /// public Type TargetType { get; init; } + /// + /// Gets or initializes the of the source property. + /// public PropertyInfo SourceProperty { get; init; } + /// + /// Gets or initializes the name of the target property. + /// public string TargetPropertyName { get; init; } + /// + /// Gets or initializes the direction in which values are synchronized. + /// public LinkDirection LinkDirection { get; init; } + /// + /// Gets or initializes the type of the used for value conversion. + /// public Type Converter { get; init; } + /// + /// Gets or initializes an optional parameter passed to the . + /// public object ConverterParameter { get; init; } + /// + /// Gets or initializes a value indicating whether the source property is initialized with the + /// target property value when the link is established. + /// public bool InitWithTargetValue { get; init; } } diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkInfo.cs b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkInfo.cs index 26b41300..a9f1f607 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkInfo.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkInfo.cs @@ -2,21 +2,50 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Contains resolved information about a property link, including source and target objects, +/// their properties, direction, and converter details. +/// public class PropertyLinkInfo { + /// + /// Gets or initializes the source object of the property link. + /// public object Source { get; init; } + /// + /// Gets or initializes the target object of the property link. + /// public object Target { get; init; } + /// + /// Gets or initializes the of the source property. + /// public PropertyInfo SourceProperty { get; init; } + /// + /// Gets or initializes the of the target property. + /// public PropertyInfo TargetProperty { get; init; } + /// + /// Gets or sets the direction in which values are synchronized. + /// public LinkDirection Direction { get; set; } + /// + /// Gets or initializes the converter used to transform values between linked properties. + /// public IPropertyValueConverter Converter { get; init; } + /// + /// Gets or initializes an optional parameter passed to the . + /// public object ConverterParameter { get; init; } + /// + /// Gets or initializes a value indicating whether the source property is initialized with the + /// target property value when the link is established. + /// public bool InitWithTargetValue { get; init; } } diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItem.cs b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItem.cs index 5ba5f25c..c94629a1 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItem.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItem.cs @@ -1,15 +1,29 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Represents an individual property link between two objects and handles property change +/// propagation based on the configured . +/// public class PropertyLinkItem { private readonly PropertyValueCopier _propertyValueCopier; + /// + /// Initializes a new instance of the class. + /// + /// The property link information describing the link configuration. public PropertyLinkItem(PropertyLinkInfo propertyLinkInfo) { Info = propertyLinkInfo; _propertyValueCopier = new PropertyValueCopier(); } + /// + /// Handles a property change notification and copies the value in the appropriate direction + /// if the changed property matches the link configuration. + /// + /// The object instance that raised the property change. + /// The name of the property that changed. public void HandleChange(object changedInstance, string changedPropertyName) { // ReSharper disable once SwitchStatementMissingSomeCases @@ -41,6 +55,10 @@ public void HandleChange(object changedInstance, string changedPropertyName) } } + /// + /// Initializes the link by copying the current property value in the appropriate direction + /// based on the link configuration. + /// public void Init() { if (Info.Direction == LinkDirection.OneWayFromTarget || Info.InitWithTargetValue) @@ -67,5 +85,8 @@ private void CopyToSource() true, Info); } + /// + /// Gets the property link information describing the link configuration. + /// public PropertyLinkInfo Info { get; } } diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItemsBuilder.cs b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItemsBuilder.cs index dee47e2c..9bfbd2a8 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItemsBuilder.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyLinkItemsBuilder.cs @@ -5,17 +5,31 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Builds a collection of instances from a set of +/// entries, merging duplicate or opposite links +/// into bidirectional links where appropriate. +/// public class PropertyLinkItemsBuilder { private readonly HandlerChain<(PropertyLinkInfo, PropertyLinkInfo), bool> _handlerChain; private readonly IEnumerable _propertyLinkDefinitions; + /// + /// Initializes a new instance of the class. + /// + /// The property link definitions to build link items from. public PropertyLinkItemsBuilder(IEnumerable propertyLinkDefinitions) { _propertyLinkDefinitions = propertyLinkDefinitions; _handlerChain = CreateHandlerChain(); } + /// + /// Builds and returns the collection of instances, + /// merging duplicate and opposite link definitions. + /// + /// A collection of resolved property link items. public IEnumerable Build() { var linkItems = new List(); diff --git a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyValueCopier.cs b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyValueCopier.cs index 6bd57e50..e534d9ad 100644 --- a/source/Core/CreativeCoders.Core/ObjectLinking/PropertyValueCopier.cs +++ b/source/Core/CreativeCoders.Core/ObjectLinking/PropertyValueCopier.cs @@ -3,10 +3,27 @@ namespace CreativeCoders.Core.ObjectLinking; +/// +/// Copies property values between source and target objects, applying an optional +/// and preventing recursive copy cycles. +/// public class PropertyValueCopier { private readonly SynchronizedValue _isInCopyProperty = new SynchronizedValue(false); + /// + /// Copies the value of a source property to a target property, optionally applying a converter. + /// Recursive calls are suppressed to prevent infinite loops in bidirectional links. + /// + /// The source object to read the property value from. + /// The of the source property. + /// The target object to write the property value to. + /// The of the target property. + /// + /// to use the converter's method; + /// to use . + /// + /// The property link information containing the converter and converter parameter. public void CopyPropertyValue(object source, PropertyInfo sourceProperty, object target, PropertyInfo targetProperty, bool backDirection, PropertyLinkInfo info) { diff --git a/source/Core/CreativeCoders.Core/ObservableObject.cs b/source/Core/CreativeCoders.Core/ObservableObject.cs index 03d40abf..4ebe1a5b 100644 --- a/source/Core/CreativeCoders.Core/ObservableObject.cs +++ b/source/Core/CreativeCoders.Core/ObservableObject.cs @@ -10,22 +10,44 @@ namespace CreativeCoders.Core; +/// +/// Provides a base class for objects that support property change notification. +/// [PublicAPI] public class ObservableObject : INotifyPropertyChanged, INotifyPropertyChanging { + /// + /// Occurs when a property value changes. + /// public event PropertyChangedEventHandler? PropertyChanged; + /// + /// Raises the event. + /// + /// The name of the property that changed. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + /// + /// Raises the event for the specified property expression. + /// + /// The type of the property. + /// The expression identifying the property. protected void OnPropertyChanged(Expression> propertyExpression) { var propertyName = propertyExpression.GetMemberName(); OnPropertyChanged(propertyName); } + /// + /// Sets the property value and raises change notification events if the value has changed. + /// + /// The type of the property. + /// A reference to the backing field. + /// The new value. + /// The name of the property that changed. protected void Set(ref T? value, T? newValue, [CallerMemberName] string? propertyName = null) { if (EqualityComparer.Default.Equals(value, newValue)) @@ -38,14 +60,28 @@ protected void Set(ref T? value, T? newValue, [CallerMemberName] string? prop OnPropertyChanged(propertyName); } + /// + /// Sets the property value and raises change notification events if the value has changed. + /// + /// The type of the property. + /// A reference to the backing field. + /// The new value. + /// The expression identifying the property. protected void Set(ref T? value, T? newValue, Expression> propertyExpression) { var propertyName = propertyExpression.GetMemberName(); Set(ref value, newValue, propertyName); } + /// + /// Occurs when a property value is changing. + /// public event PropertyChangingEventHandler? PropertyChanging; + /// + /// Raises the event. + /// + /// The name of the property that is changing. protected virtual void OnPropertyChanging([CallerMemberName] string? propertyName = null) { PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName)); diff --git a/source/Core/CreativeCoders.Core/Placeholders/PlaceholderReplacer.cs b/source/Core/CreativeCoders.Core/Placeholders/PlaceholderReplacer.cs index fa8be59f..2e3b9f17 100644 --- a/source/Core/CreativeCoders.Core/Placeholders/PlaceholderReplacer.cs +++ b/source/Core/CreativeCoders.Core/Placeholders/PlaceholderReplacer.cs @@ -6,6 +6,12 @@ namespace CreativeCoders.Core.Placeholders; #nullable enable +/// +/// Replaces placeholder tokens in text with their corresponding values from a dictionary. +/// +/// The prefix that marks the beginning of a placeholder token. +/// The suffix that marks the end of a placeholder token. +/// The dictionary mapping placeholder names to their replacement values. public class PlaceholderReplacer( string placeholderPrefix, string placeholderSuffix, @@ -17,6 +23,12 @@ public class PlaceholderReplacer( private readonly IDictionary _placeholders = Ensure.NotNull(placeholders); + /// + /// Replaces all placeholder tokens in the specified text with their corresponding values. + /// + /// The text containing placeholder tokens to replace. + /// to replace values with the literal string "null"; to replace them with an empty string. + /// The text with all recognized placeholder tokens replaced by their values. public string Replace(string text, bool allowNull = false) { if (_placeholders.Count == 0) @@ -31,6 +43,12 @@ public string Replace(string text, bool allowNull = false) placeholder.Value.ToStringSafe(allowNull ? "null" : string.Empty))); } + /// + /// Replaces all placeholder tokens in each line of the specified sequence with their corresponding values. + /// + /// The sequence of text lines containing placeholder tokens to replace. + /// to replace values with the literal string "null"; to replace them with an empty string. + /// A sequence of lines with all recognized placeholder tokens replaced by their values. public IEnumerable Replace(IEnumerable lines, bool allowNull = false) { return _placeholders.Count == 0 diff --git a/source/Core/CreativeCoders.Core/Reflection/AssemblyExtensions.cs b/source/Core/CreativeCoders.Core/Reflection/AssemblyExtensions.cs index c0fec15e..4986606b 100644 --- a/source/Core/CreativeCoders.Core/Reflection/AssemblyExtensions.cs +++ b/source/Core/CreativeCoders.Core/Reflection/AssemblyExtensions.cs @@ -4,9 +4,21 @@ namespace CreativeCoders.Core.Reflection; +/// +/// Provides extension methods for the class. +/// [ExcludeFromCodeCoverage] public static class AssemblyExtensions { + /// + /// Gets all types defined in the assembly, returning an empty array instead of throwing + /// a when types cannot be loaded. + /// + /// The assembly from which to retrieve types. + /// + /// An array of all objects defined in the assembly, or an empty array + /// if a occurs. + /// // ReSharper disable once ReturnTypeCanBeEnumerable.Global public static Type[] GetTypesSafe(this Assembly assembly) { diff --git a/source/Core/CreativeCoders.Core/Reflection/ExpressionExtensions.cs b/source/Core/CreativeCoders.Core/Reflection/ExpressionExtensions.cs index e8bf9372..56791742 100644 --- a/source/Core/CreativeCoders.Core/Reflection/ExpressionExtensions.cs +++ b/source/Core/CreativeCoders.Core/Reflection/ExpressionExtensions.cs @@ -4,18 +4,44 @@ namespace CreativeCoders.Core.Reflection; +/// +/// Provides extension methods for working with trees. +/// public static class ExpressionExtensions { + /// + /// Gets the member name from a member access expression. + /// + /// The type of the member. + /// The expression that accesses a member. + /// The name of the member referenced by the expression. public static string GetMemberName(this Expression> memberExpression) { return InternalGetMemberName(memberExpression); } + /// + /// Gets the member name from a member access expression on a specific type. + /// + /// The type that contains the member. + /// The type of the member. + /// The expression that accesses a member on . + /// The name of the member referenced by the expression. public static string GetMemberName(this Expression> memberExpression) { return InternalGetMemberName(memberExpression); } + /// + /// Determines whether the expression refers to a property declared directly on type . + /// + /// The type expected to declare the property. + /// The type of the property. + /// The expression that accesses a property. + /// + /// if the expression refers to a property declared on ; + /// otherwise, . + /// public static bool IsPropertyOf(this Expression> propertyExpression) where T : class { @@ -31,7 +57,7 @@ private static MemberInfo GetMemberInfo(Expression memberExpression) private static string InternalGetMemberName(Expression memberExpression) { - Ensure.IsNotNull(memberExpression, nameof(memberExpression)); + Ensure.IsNotNull(memberExpression); var body = memberExpression.Body as MemberExpression; Ensure.IsNotNull(body); diff --git a/source/Core/CreativeCoders.Core/Reflection/ExpressionUtils.cs b/source/Core/CreativeCoders.Core/Reflection/ExpressionUtils.cs index 61bbbbed..d2cde3a1 100644 --- a/source/Core/CreativeCoders.Core/Reflection/ExpressionUtils.cs +++ b/source/Core/CreativeCoders.Core/Reflection/ExpressionUtils.cs @@ -5,13 +5,29 @@ namespace CreativeCoders.Core.Reflection; +/// +/// Provides utility methods for creating compiled delegates from expression trees for method invocations. +/// public static class ExpressionUtils { + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Action CreateCallAction(object instance, MethodInfo methodInfo) { return CreateCallExpression(instance, methodInfo); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the method parameter. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Action CreateCallAction(object instance, MethodInfo methodInfo) { var parameterExpression = Expression.Parameter(typeof(T)); @@ -19,6 +35,14 @@ public static Action CreateCallAction(object instance, MethodInfo methodIn return CreateCallExpression>(instance, methodInfo, parameterExpression); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Action CreateCallAction(object instance, MethodInfo methodInfo) { var parameterExpression1 = Expression.Parameter(typeof(T1)); @@ -28,6 +52,15 @@ public static Action CreateCallAction(object instance, MethodInf parameterExpression1, parameterExpression2); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the third method parameter. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Action CreateCallAction(object instance, MethodInfo methodInfo) { var parameterExpression1 = Expression.Parameter(typeof(T1)); @@ -38,6 +71,16 @@ public static Action CreateCallAction(object instance, M parameterExpression1, parameterExpression2, parameterExpression3); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the third method parameter. + /// The type of the fourth method parameter. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Action CreateCallAction(object instance, MethodInfo methodInfo) { @@ -50,11 +93,26 @@ public static Action CreateCallAction(object ins parameterExpression1, parameterExpression2, parameterExpression3, parameterExpression4); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the return value. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Func CreateCallFunc(object instance, MethodInfo methodInfo) { return CreateCallExpression>(instance, methodInfo); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the method parameter. + /// The type of the return value. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Func CreateCallFunc(object instance, MethodInfo methodInfo) { var parameterExpression = Expression.Parameter(typeof(T)); @@ -62,6 +120,15 @@ public static Func CreateCallFunc(object instance, Metho return CreateCallExpression>(instance, methodInfo, parameterExpression); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the return value. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Func CreateCallFunc(object instance, MethodInfo methodInfo) { @@ -72,6 +139,16 @@ public static Func CreateCallFunc(object insta parameterExpression1, parameterExpression2); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the third method parameter. + /// The type of the return value. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Func CreateCallFunc(object instance, MethodInfo methodInfo) { @@ -83,6 +160,17 @@ public static Func CreateCallFunc(obje parameterExpression1, parameterExpression2, parameterExpression3); } + /// + /// Creates a compiled delegate that calls the specified method on the given instance. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the third method parameter. + /// The type of the fourth method parameter. + /// The type of the return value. + /// The object instance on which the method is called. + /// The method to invoke. + /// A compiled delegate for the method call. public static Func CreateCallFunc(object instance, MethodInfo methodInfo) { diff --git a/source/Core/CreativeCoders.Core/Reflection/GenericArgument.cs b/source/Core/CreativeCoders.Core/Reflection/GenericArgument.cs index 2505100b..e380f6cc 100644 --- a/source/Core/CreativeCoders.Core/Reflection/GenericArgument.cs +++ b/source/Core/CreativeCoders.Core/Reflection/GenericArgument.cs @@ -2,18 +2,32 @@ namespace CreativeCoders.Core.Reflection; +/// +/// Represents a named generic type argument used when invoking generic methods dynamically. +/// public class GenericArgument { + /// + /// Initializes a new instance of the class. + /// + /// The name of the generic type parameter. + /// The concrete type to substitute for the generic parameter. public GenericArgument(string name, Type type) { - Ensure.IsNotNullOrWhitespace(name, nameof(name)); - Ensure.IsNotNull(type, nameof(type)); + Ensure.IsNotNullOrWhitespace(name); + Ensure.IsNotNull(type); Name = name; Type = type; } + /// + /// Gets the name of the generic type parameter. + /// public string Name { get; } + /// + /// Gets the concrete type to substitute for the generic parameter. + /// public Type Type { get; } } diff --git a/source/Core/CreativeCoders.Core/Reflection/MethodExtensions.cs b/source/Core/CreativeCoders.Core/Reflection/MethodExtensions.cs index c39bad0b..0d3f18dd 100644 --- a/source/Core/CreativeCoders.Core/Reflection/MethodExtensions.cs +++ b/source/Core/CreativeCoders.Core/Reflection/MethodExtensions.cs @@ -7,8 +7,18 @@ namespace CreativeCoders.Core.Reflection; +/// +/// Provides extension methods for dynamically invoking methods on objects using reflection. +/// public static class MethodExtensions { + /// + /// Invokes a method by name on the specified object instance. + /// + /// The object on which to invoke the method. + /// The name of the method to invoke. + /// The arguments to pass to the method. + /// The method is not found on the instance type. public static void ExecuteMethod(this object instance, string methodName, params object[] arguments) { Ensure.IsNotNull(instance); @@ -25,6 +35,15 @@ public static void ExecuteMethod(this object instance, string methodName, params method.Invoke(instance, arguments); } + /// + /// Invokes a method by name on the specified object instance and returns the result. + /// + /// The type of the return value. + /// The object on which to invoke the method. + /// The name of the method to invoke. + /// The arguments to pass to the method. + /// The result of the method invocation cast to . + /// The method is not found on the instance type. public static TResult ExecuteMethod(this object instance, string methodName, params object[] arguments) { @@ -42,6 +61,16 @@ public static TResult ExecuteMethod(this object instance, string method return (TResult)method.Invoke(instance, arguments); } + /// + /// Invokes a generic method by name using named generic arguments and returns the result. + /// + /// The type of the return value. + /// The object on which to invoke the method. + /// The name of the generic method to invoke. + /// The named generic type arguments used to construct the generic method. + /// The parameters to pass to the method. + /// The result of the method invocation cast to , or the default value if the result cannot be cast. + /// The generic method is not found on the instance type. public static TResult ExecuteGenericMethod(this object instance, string methodName, IEnumerable genericArguments, params object[] methodParams) { @@ -68,6 +97,16 @@ public static TResult ExecuteGenericMethod(this object instance, string return default; } + /// + /// Invokes a generic method by name using type arguments and returns the result. + /// + /// The type of the return value. + /// The object on which to invoke the method. + /// The name of the generic method to invoke. + /// The type arguments used to construct the generic method. + /// The parameters to pass to the method. + /// The result of the method invocation cast to , or the default value if the result cannot be cast. + /// The generic method is not found on the instance type. public static TResult ExecuteGenericMethod(this object instance, string methodName, Type[] genericTypeArguments, params object[] methodParams) { @@ -197,18 +236,44 @@ private static bool GenericParamsAreEqual(Type[] genericArguments, return genericArguments.Select(genericArg => genericArg.Name).SequenceEqual(genericArgumentNames); } + /// + /// Invokes a generic method by name using named generic arguments, discarding the return value. + /// + /// The object on which to invoke the method. + /// The name of the generic method to invoke. + /// The named generic type arguments used to construct the generic method. + /// The parameters to pass to the method. + /// The generic method is not found on the instance type. public static void ExecuteGenericMethod(this object instance, string methodName, IEnumerable genericArguments, params object[] methodParams) { ExecuteGenericMethod(instance, methodName, genericArguments, methodParams); } + /// + /// Invokes a generic method by name using type arguments, discarding the return value. + /// + /// The object on which to invoke the method. + /// The name of the generic method to invoke. + /// The type arguments used to construct the generic method. + /// The parameters to pass to the method. + /// The generic method is not found on the instance type. public static void ExecuteGenericMethod(this object instance, string methodName, Type[] genericTypeArguments, params object[] methodParams) { ExecuteGenericMethod(instance, methodName, genericTypeArguments, methodParams); } + /// + /// Creates a compiled delegate that invokes a generic method accepting an object array of parameters and returning a result. + /// + /// The type of the return value. + /// The object on which the method is called. + /// The name of the generic method. + /// The types of the method parameters used for overload resolution. + /// The type arguments used to construct the generic method. + /// A compiled delegate that invokes the method and returns the result. + /// The generic method is not found on the instance type. public static Func CreateGenericMethodFunc(this object instance, string methodName, Type[] parameterTypes, params Type[] genericTypeArguments) @@ -219,6 +284,17 @@ public static Func CreateGenericMethodFunc(this obje return methodParameters => ExecuteMethod(instance, methodParameters, noneGenericMethod); } + /// + /// Creates a compiled strongly-typed delegate with one parameter that invokes a generic method. + /// + /// The type of the method parameter. + /// The type of the return value. + /// The object on which the method is called. + /// The name of the generic method. + /// The types of the method parameters used for overload resolution. + /// The type arguments used to construct the generic method. + /// A compiled delegate that invokes the method and returns the result. + /// The generic method is not found on the instance type. public static Func CreateGenericMethodFunc(this object instance, string methodName, Type[] parameterTypes, params Type[] genericTypeArguments) @@ -228,6 +304,18 @@ public static Func CreateGenericMethodFunc(this object i return ExpressionUtils.CreateCallFunc(instance, noneGenericMethod); } + /// + /// Creates a compiled strongly-typed delegate with two parameters that invokes a generic method. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the return value. + /// The object on which the method is called. + /// The name of the generic method. + /// The types of the method parameters used for overload resolution. + /// The type arguments used to construct the generic method. + /// A compiled delegate that invokes the method and returns the result. + /// The generic method is not found on the instance type. public static Func CreateGenericMethodFunc(this object instance, string methodName, Type[] parameterTypes, params Type[] genericTypeArguments) @@ -238,6 +326,19 @@ public static Func CreateGenericMethodFunc(thi return ExpressionUtils.CreateCallFunc(instance, noneGenericMethod); } + /// + /// Creates a compiled strongly-typed delegate with three parameters that invokes a generic method. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the third method parameter. + /// The type of the return value. + /// The object on which the method is called. + /// The name of the generic method. + /// The types of the method parameters used for overload resolution. + /// The type arguments used to construct the generic method. + /// A compiled delegate that invokes the method and returns the result. + /// The generic method is not found on the instance type. public static Func CreateGenericMethodFunc(this object instance, string methodName, Type[] parameterTypes, params Type[] genericTypeArguments) @@ -248,6 +349,20 @@ public static Func CreateGenericMethodFunc(instance, noneGenericMethod); } + /// + /// Creates a compiled strongly-typed delegate with four parameters that invokes a generic method. + /// + /// The type of the first method parameter. + /// The type of the second method parameter. + /// The type of the third method parameter. + /// The type of the fourth method parameter. + /// The type of the return value. + /// The object on which the method is called. + /// The name of the generic method. + /// The types of the method parameters used for overload resolution. + /// The type arguments used to construct the generic method. + /// A compiled delegate that invokes the method and returns the result. + /// The generic method is not found on the instance type. public static Func CreateGenericMethodFunc( this object instance, string methodName, diff --git a/source/Core/CreativeCoders.Core/Reflection/MethodInfoExtensions.cs b/source/Core/CreativeCoders.Core/Reflection/MethodInfoExtensions.cs index 31d8d840..418072a7 100644 --- a/source/Core/CreativeCoders.Core/Reflection/MethodInfoExtensions.cs +++ b/source/Core/CreativeCoders.Core/Reflection/MethodInfoExtensions.cs @@ -8,30 +8,57 @@ namespace CreativeCoders.Core.Reflection; #nullable enable +/// +/// Provides extension methods for to support invocation and comparison. +/// public static class MethodInfoExtensions { + /// + /// Invokes the method on the specified instance, resolving parameters from a service provider, and returns the result. + /// + /// The type of the return value. + /// The method to invoke. + /// The object instance on which to invoke the method. + /// The service provider used to resolve method parameters. + /// Additional arguments to pass to the method. + /// The result of the method invocation cast to . public static T? Execute(this MethodInfo methodInfo, object instance, IServiceProvider serviceProvider, params object[] args) { - Ensure.NotNull(methodInfo, nameof(methodInfo)); - Ensure.NotNull(serviceProvider, nameof(serviceProvider)); + Ensure.NotNull(methodInfo); + Ensure.NotNull(serviceProvider); var arguments = methodInfo.GetParameters().CreateArguments(serviceProvider, out _, args); return (T?) methodInfo.Invoke(instance, arguments); } + /// + /// Invokes the method on the specified instance, resolving parameters from a service provider, discarding the return value. + /// + /// The method to invoke. + /// The object instance on which to invoke the method. + /// The service provider used to resolve method parameters. + /// Additional arguments to pass to the method. public static void Execute(this MethodInfo methodInfo, object instance, IServiceProvider serviceProvider, params object[] args) { - Ensure.NotNull(methodInfo, nameof(methodInfo)); - Ensure.NotNull(serviceProvider, nameof(serviceProvider)); + Ensure.NotNull(methodInfo); + Ensure.NotNull(serviceProvider); var arguments = methodInfo.GetParameters().CreateArguments(serviceProvider, out _, args); methodInfo.Invoke(instance, arguments); } + /// + /// Determines whether two methods have the same parameter names and types. + /// + /// The first method to compare. + /// The second method to compare against. + /// + /// if both methods have identical parameter names and types; otherwise, . + /// public static bool ParametersAreEqual(this MethodInfo methodInfo, MethodInfo methodInfoForCompare) { var parameters1 = methodInfo.GetParameters(); @@ -48,6 +75,14 @@ public static bool ParametersAreEqual(this MethodInfo methodInfo, MethodInfo met return parametersEqual; } + /// + /// Determines whether the specified method matches another method by name, parameters, return type, and generic arguments. + /// + /// The method to check. + /// The method to compare against. + /// + /// if the methods match; otherwise, . + /// public static bool MatchesMethod(this MethodInfo methodInfo, MethodInfo methodInfoForCompare) { var match = methodInfo == methodInfoForCompare; @@ -71,19 +106,16 @@ public static bool MatchesMethod(this MethodInfo methodInfo, MethodInfo methodIn var returnTypeEqual = methodInfoForCompare.ReturnType.IsAssignableFrom(methodInfo.ReturnType); - if (!returnTypeEqual) + if (!returnTypeEqual && (methodInfo.ReturnType.IsGenericType || methodInfoForCompare.ReturnType.IsGenericType)) { - if (methodInfo.ReturnType.IsGenericType || methodInfoForCompare.ReturnType.IsGenericType) - { - var genericReturnTypeArguments = methodInfo.ReturnType.GenericTypeArguments; - var genericReturnTypeArgumentsForCompare = - methodInfoForCompare.ReturnType.GenericTypeArguments; - - returnTypeEqual = genericReturnTypeArguments.SequenceEqual( - genericReturnTypeArgumentsForCompare, - new DelegateEqualityComparer( - (x, y) => x == y || x.IsGenericParameter || y.IsGenericParameter)); - } + var genericReturnTypeArguments = methodInfo.ReturnType.GenericTypeArguments; + var genericReturnTypeArgumentsForCompare = + methodInfoForCompare.ReturnType.GenericTypeArguments; + + returnTypeEqual = genericReturnTypeArguments.SequenceEqual( + genericReturnTypeArgumentsForCompare, + new DelegateEqualityComparer( + (x, y) => x == y || x.IsGenericParameter || y.IsGenericParameter)); } if (!returnTypeEqual) diff --git a/source/Core/CreativeCoders.Core/Reflection/ParameterInfoExtensions.cs b/source/Core/CreativeCoders.Core/Reflection/ParameterInfoExtensions.cs index c00ba49d..b89a3230 100644 --- a/source/Core/CreativeCoders.Core/Reflection/ParameterInfoExtensions.cs +++ b/source/Core/CreativeCoders.Core/Reflection/ParameterInfoExtensions.cs @@ -8,13 +8,35 @@ namespace CreativeCoders.Core.Reflection; #nullable enable +/// +/// Provides extension methods for to support parameter inspection and argument resolution. +/// public static class ParameterInfoExtensions { + /// + /// Determines whether the parameter is decorated with the keyword. + /// + /// The parameter to inspect. + /// + /// if the parameter is a params array; otherwise, . + /// public static bool IsParams(this ParameterInfo parameterInfo) { return parameterInfo.IsDefined(typeof(ParamArrayAttribute), false); } + /// + /// Creates an argument array for the specified parameters by matching provided values by type + /// and resolving remaining parameters from a service provider. + /// + /// The method parameters to create arguments for. + /// The service provider used to resolve unmatched parameters. + /// + /// When this method returns, if all provided argument values were consumed; + /// otherwise, . + /// + /// The explicit argument values to match against the parameters. + /// An array of resolved argument values in parameter order. public static object[] CreateArguments(this IEnumerable parameters, IServiceProvider serviceProvider, out bool allArgumentsMatched, params object[] argumentValues) diff --git a/source/Core/CreativeCoders.Core/Reflection/ReflectionUtils.cs b/source/Core/CreativeCoders.Core/Reflection/ReflectionUtils.cs index c3c5ec8a..5544e645 100644 --- a/source/Core/CreativeCoders.Core/Reflection/ReflectionUtils.cs +++ b/source/Core/CreativeCoders.Core/Reflection/ReflectionUtils.cs @@ -5,8 +5,15 @@ namespace CreativeCoders.Core.Reflection; +/// +/// Provides static utility methods for retrieving assemblies and types from the current application domain. +/// public static class ReflectionUtils { + /// + /// Gets all assemblies loaded in the current application domain. + /// + /// A sequence of all loaded instances. public static IEnumerable GetAllAssemblies() { var assemblies = AppDomain @@ -16,6 +23,13 @@ public static IEnumerable GetAllAssemblies() return assemblies; } + /// + /// Gets all assemblies loaded in the current application domain, optionally including reflection-only assemblies. + /// + /// + /// to include reflection-only assemblies; otherwise, . + /// + /// A sequence of loaded instances. public static IEnumerable GetAllAssemblies(bool withReflectionOnlyAssemblies) { var assemblies = AppDomain @@ -33,6 +47,10 @@ public static IEnumerable GetAllAssemblies(bool withReflectionOnlyAsse return assemblies; } + /// + /// Gets all types from all assemblies loaded in the current application domain. + /// + /// A sequence of all objects from all loaded assemblies. public static IEnumerable GetAllTypes() { return @@ -40,6 +58,14 @@ public static IEnumerable GetAllTypes() .SelectMany(assembly => assembly.GetTypesSafe()); } + /// + /// Gets all types from all assemblies loaded in the current application domain, + /// optionally including reflection-only assemblies. + /// + /// + /// to include reflection-only assemblies; otherwise, . + /// + /// A sequence of all objects from the matching assemblies. public static IEnumerable GetAllTypes(bool withReflectionOnlyAssemblies) { return @@ -47,6 +73,11 @@ public static IEnumerable GetAllTypes(bool withReflectionOnlyAssemblies) .SelectMany(assembly => assembly.GetTypesSafe()); } + /// + /// Gets all types from assemblies in the current application domain that satisfy the specified predicate. + /// + /// The predicate used to filter assemblies. + /// A sequence of all objects from the matching assemblies. public static IEnumerable GetAllTypes(Func checkAssembly) { return @@ -55,6 +86,15 @@ public static IEnumerable GetAllTypes(Func checkAssembly) .SelectMany(assembly => assembly.GetTypesSafe()); } + /// + /// Gets all types from assemblies in the current application domain that satisfy the specified predicate, + /// optionally including reflection-only assemblies. + /// + /// The predicate used to filter assemblies. + /// + /// to include reflection-only assemblies; otherwise, . + /// + /// A sequence of all objects from the matching assemblies. public static IEnumerable GetAllTypes(Func checkAssembly, bool withReflectionOnlyAssemblies) { diff --git a/source/Core/CreativeCoders.Core/Reflection/TypeExtensions.cs b/source/Core/CreativeCoders.Core/Reflection/TypeExtensions.cs index 4e8d2953..bdfa80a4 100644 --- a/source/Core/CreativeCoders.Core/Reflection/TypeExtensions.cs +++ b/source/Core/CreativeCoders.Core/Reflection/TypeExtensions.cs @@ -7,9 +7,19 @@ #nullable enable namespace CreativeCoders.Core.Reflection; +/// +/// Provides extension methods for the class to simplify common reflection tasks. +/// [PublicAPI] public static class TypeExtensions { + /// + /// Creates an instance of a generic type constructed with the specified type argument. + /// + /// The open generic type definition. + /// The type argument used to construct the generic type. + /// The parameters passed to the constructor. + /// The newly created instance, or if creation fails. public static object? CreateGenericInstance(this Type type, Type typeArgument, params object[] constructorParameters) { @@ -22,6 +32,11 @@ public static class TypeExtensions return instance; } + /// + /// Gets the default value for the specified . + /// + /// The type for which to get the default value. + /// The default value for value types, or for reference types. public static object? GetDefault(this Type type) { return type.IsValueType @@ -29,6 +44,11 @@ public static class TypeExtensions : null; } + /// + /// Gets all non-abstract types in the current application domain that implement or inherit from the specified type. + /// + /// The base type or interface to find implementations for. + /// A sequence of non-abstract types assignable to the specified type. public static IEnumerable GetImplementations(this Type type) { return ReflectionUtils @@ -36,6 +56,15 @@ public static IEnumerable GetImplementations(this Type type) .Where(x => !x.IsAbstract && type.IsAssignableFrom(x)); } + /// + /// Gets all non-abstract types in the current application domain that implement or inherit from the specified type, + /// optionally including reflection-only assemblies. + /// + /// The base type or interface to find implementations for. + /// + /// to include reflection-only assemblies; otherwise, . + /// + /// A sequence of non-abstract types assignable to the specified type. public static IEnumerable GetImplementations(this Type type, bool withReflectionOnlyAssemblies) { return ReflectionUtils @@ -43,6 +72,12 @@ public static IEnumerable GetImplementations(this Type type, bool withRefl .Where(x => !x.IsAbstract && type.IsAssignableFrom(x)); } + /// + /// Gets all non-abstract types from the specified assemblies that implement or inherit from the specified type. + /// + /// The base type or interface to find implementations for. + /// The assemblies to search for implementations. + /// A sequence of non-abstract types assignable to the specified type. public static IEnumerable GetImplementations(this Type type, params Assembly[] assemblies) { return assemblies @@ -50,6 +85,14 @@ public static IEnumerable GetImplementations(this Type type, params Assemb .Where(x => !x.IsAbstract && type.IsAssignableFrom(x)); } + /// + /// Creates an instance of the specified type by resolving constructor parameters from a service provider. + /// + /// The type of instance to create. + /// The concrete type to instantiate. + /// The service provider used to resolve constructor dependencies. + /// Additional arguments passed to the constructor. + /// The created instance, or if no suitable constructor is found. public static T? CreateInstance(this Type type, IServiceProvider serviceProvider, params object[] args) where T : class { @@ -58,6 +101,15 @@ public static IEnumerable GetImplementations(this Type type, params Assemb .FirstOrDefault(instance => instance != null); } + /// + /// Creates an instance of the specified type using a specific constructor, resolving parameters from a service provider. + /// + /// The type of instance to create. + /// The concrete type to instantiate. + /// The constructor to invoke. + /// The service provider used to resolve constructor dependencies. + /// Additional arguments passed to the constructor. + /// The created instance, or if not all constructor arguments can be resolved. public static T? CreateInstance(this Type type, ConstructorInfo ctorInfo, IServiceProvider serviceProvider, params object[] args) where T : class @@ -74,10 +126,27 @@ public static IEnumerable GetImplementations(this Type type, params Assemb return Activator.CreateInstance(type, arguments) as T; } + /// + /// Determines whether the type implements a specific open generic interface. + /// + /// The type to check. + /// The open generic interface definition (e.g., typeof(IEnumerable<>)). + /// + /// if the type implements the generic interface; otherwise, . + /// public static bool ImplementsGenericInterface(this Type type, Type genericInterfaceDefinition) => type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == genericInterfaceDefinition); + /// + /// Gets the generic type arguments of a specific generic interface implemented by the type. + /// + /// The type to inspect. + /// The open generic interface definition to match. + /// + /// An array of objects representing the generic arguments of the matched interface, + /// or an empty array if the interface is not implemented. + /// public static Type[] GetGenericInterfaceArguments(this Type type, Type genericInterfaceDefinition) { return type.GetInterfaces() diff --git a/source/Core/CreativeCoders.Core/ServiceProviderExtensions.cs b/source/Core/CreativeCoders.Core/ServiceProviderExtensions.cs index f7198a17..f3861bea 100644 --- a/source/Core/CreativeCoders.Core/ServiceProviderExtensions.cs +++ b/source/Core/CreativeCoders.Core/ServiceProviderExtensions.cs @@ -4,14 +4,30 @@ namespace CreativeCoders.Core; +/// +/// Provides extension methods for . +/// [ExcludeFromCodeCoverage] public static class ServiceProviderExtensions { + /// + /// Gets an existing service or creates a new instance of the specified type. + /// + /// The type of service to get or create. + /// The service provider. + /// The resolved or created service instance. public static T GetServiceOrCreateInstance(this IServiceProvider serviceProvider) { return ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); } + /// + /// Creates a new instance of the specified type using the service provider for dependency injection. + /// + /// The type of service to create. + /// The service provider. + /// The additional constructor parameters. + /// The created instance. public static T CreateInstance(this IServiceProvider serviceProvider, params object[] parameters) { return ActivatorUtilities.CreateInstance(serviceProvider, parameters); diff --git a/source/Core/CreativeCoders.Core/SysEnvironment/Env.cs b/source/Core/CreativeCoders.Core/SysEnvironment/Env.cs index 016727e5..ad523afb 100644 --- a/source/Core/CreativeCoders.Core/SysEnvironment/Env.cs +++ b/source/Core/CreativeCoders.Core/SysEnvironment/Env.cs @@ -7,12 +7,21 @@ namespace CreativeCoders.Core.SysEnvironment; +/// +/// Provides static access to environment information by delegating to an implementation. +/// Serves as a testable wrapper for . +/// [PublicAPI] [ExcludeFromCodeCoverage] public static class Env { private static IEnvironment __environmentInstance = new EnvironmentWrapper(); + /// + /// Replaces the underlying implementation used by all static members. + /// + /// The replacement environment implementation. + /// An that restores the previous implementation when disposed. public static IDisposable SetEnvironmentImpl(IEnvironment environment) { Ensure.IsNotNull(environment); @@ -26,133 +35,270 @@ public static IDisposable SetEnvironmentImpl(IEnvironment environment) return resetEnvImpl; } + /// + /// Terminates the current process and returns the specified exit code to the operating system. + /// + /// The exit code to return to the operating system. public static void Exit(int exitCode) { __environmentInstance.Exit(exitCode); } + /// + /// Replaces the name of each environment variable embedded in the specified string + /// with the string equivalent of the value of the variable. + /// + /// The string containing the names of zero or more environment variables. + /// The string with each environment variable replaced by its value. public static string ExpandEnvironmentVariables(string name) { return __environmentInstance.ExpandEnvironmentVariables(name); } + /// + /// Immediately terminates the process with the specified message. + /// + /// The message that explains why the process was terminated, or if no explanation is provided. public static void FailFast(string? message) { __environmentInstance.FailFast(message); } + /// + /// Immediately terminates the process with the specified message and exception. + /// + /// The message that explains why the process was terminated, or if no explanation is provided. + /// The exception associated with the failure, or if there is none. public static void FailFast(string? message, Exception? exception) { __environmentInstance.FailFast(message, exception); } + /// + /// Returns the command-line arguments for the current process. + /// + /// An array of strings containing the command-line arguments. public static string[] GetCommandLineArgs() { return __environmentInstance.GetCommandLineArgs(); } + /// + /// Retrieves the value of an environment variable from the current process. + /// + /// The name of the environment variable. + /// The value of the environment variable, or if the variable is not found. public static string? GetEnvironmentVariable(string variable) { return __environmentInstance.GetEnvironmentVariable(variable); } + /// + /// Retrieves the value of an environment variable from the specified location. + /// + /// The name of the environment variable. + /// The location of the environment variable. + /// The value of the environment variable, or if the variable is not found. public static string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) { return __environmentInstance.GetEnvironmentVariable(variable, target); } + /// + /// Retrieves all environment variable names and their values from the current process. + /// + /// A dictionary containing all environment variable names and their values. public static IDictionary GetEnvironmentVariables() { return __environmentInstance.GetEnvironmentVariables(); } + /// + /// Retrieves all environment variable names and their values from the specified location. + /// + /// The location of the environment variables. + /// A dictionary containing all environment variable names and their values. public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target) { return __environmentInstance.GetEnvironmentVariables(target); } + /// + /// Returns the path of the specified system special folder. + /// + /// The special folder to retrieve the path for. + /// The path to the specified system special folder. public static string GetFolderPath(Environment.SpecialFolder folder) { return __environmentInstance.GetFolderPath(folder); } + /// + /// Returns the path of the specified system special folder using the specified option. + /// + /// The special folder to retrieve the path for. + /// The option for accessing the special folder. + /// The path to the specified system special folder. public static string GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option) { return __environmentInstance.GetFolderPath(folder, option); } + /// + /// Returns the names of the logical drives on the current computer. + /// + /// An array of strings containing the names of the logical drives. public static string[] GetLogicalDrives() { return __environmentInstance.GetLogicalDrives(); } + /// + /// Creates, modifies, or deletes an environment variable stored in the current process. + /// + /// The name of the environment variable. + /// The value to assign to the environment variable, or to delete the variable. public static void SetEnvironmentVariable(string variable, string? value) { __environmentInstance.SetEnvironmentVariable(variable, value); } + /// + /// Creates, modifies, or deletes an environment variable stored in the specified location. + /// + /// The name of the environment variable. + /// The value to assign to the environment variable, or to delete the variable. + /// The location of the environment variable. public static void SetEnvironmentVariable(string variable, string? value, EnvironmentVariableTarget target) { __environmentInstance.SetEnvironmentVariable(variable, value, target); } + /// + /// Returns the directory path of the current application. + /// + /// The application directory path, or if it cannot be determined. public static string? GetAppDirectory() { return __environmentInstance.GetAppDirectory(); } + /// + /// Returns the file name of the current application executable. + /// + /// The application executable file name. public static string GetAppFileName() { return __environmentInstance.GetAppFileName(); } + /// + /// Gets the command line for the current process. + /// public static string CommandLine => __environmentInstance.CommandLine; + /// + /// Gets or sets the fully qualified path of the current working directory. + /// public static string CurrentDirectory { get => __environmentInstance.CurrentDirectory; set => __environmentInstance.CurrentDirectory = value; } + /// + /// Gets the unique identifier for the current managed thread. + /// public static int CurrentManagedThreadId => __environmentInstance.CurrentManagedThreadId; + /// + /// Gets or sets the exit code of the process. + /// public static int ExitCode { get => __environmentInstance.ExitCode; set => __environmentInstance.ExitCode = value; } + /// + /// Gets a value indicating whether the common language runtime is shutting down. + /// public static bool HasShutdownStarted => __environmentInstance.HasShutdownStarted; + /// + /// Gets a value indicating whether the current operating system is a 64-bit operating system. + /// public static bool Is64BitOperatingSystem => __environmentInstance.Is64BitOperatingSystem; + /// + /// Gets a value indicating whether the current process is a 64-bit process. + /// public static bool Is64BitProcess => __environmentInstance.Is64BitProcess; + /// + /// Gets the NetBIOS name of the local computer. + /// public static string MachineName => __environmentInstance.MachineName; + /// + /// Gets the newline string defined for the current environment. + /// public static string NewLine => __environmentInstance.NewLine; + /// + /// Gets an object that contains the current platform identifier and version number. + /// public static OperatingSystem OSVersion => __environmentInstance.OSVersion; + /// + /// Gets the number of processors available to the current process. + /// public static int ProcessorCount => __environmentInstance.ProcessorCount; + /// + /// Gets the current stack trace information. + /// public static string StackTrace => __environmentInstance.StackTrace; + /// + /// Gets the fully qualified path of the system directory. + /// public static string SystemDirectory => __environmentInstance.SystemDirectory; + /// + /// Gets the amount of memory for an operating system memory page, in bytes. + /// public static int SystemPageSize => __environmentInstance.SystemPageSize; + /// + /// Gets the number of milliseconds elapsed since the system started. + /// public static int TickCount => __environmentInstance.TickCount; + /// + /// Gets the network domain name associated with the current user. + /// public static string UserDomainName => __environmentInstance.UserDomainName; + /// + /// Gets a value indicating whether the current process is running in user interactive mode. + /// public static bool UserInteractive => __environmentInstance.UserInteractive; + /// + /// Gets the user name of the person who is associated with the current thread. + /// public static string UserName => __environmentInstance.UserName; + /// + /// Gets a object that describes the major, minor, build, and revision numbers of the common language runtime. + /// public static Version Version => __environmentInstance.Version; + /// + /// Gets the amount of physical memory mapped to the process context, in bytes. + /// public static long WorkingSet => __environmentInstance.WorkingSet; } diff --git a/source/Core/CreativeCoders.Core/SysEnvironment/EnvServiceCollectionExtensions.cs b/source/Core/CreativeCoders.Core/SysEnvironment/EnvServiceCollectionExtensions.cs index dc319a99..79afdebd 100644 --- a/source/Core/CreativeCoders.Core/SysEnvironment/EnvServiceCollectionExtensions.cs +++ b/source/Core/CreativeCoders.Core/SysEnvironment/EnvServiceCollectionExtensions.cs @@ -5,10 +5,18 @@ namespace CreativeCoders.Core.SysEnvironment; +/// +/// Provides extension methods for registering environment services with the dependency injection container. +/// [ExcludeFromCodeCoverage] [PublicAPI] public static class EnvServiceCollectionExtensions { + /// + /// Registers with the dependency injection container + /// using as the default implementation. + /// + /// The service collection to register the environment service with. public static void AddEnvironment(this IServiceCollection services) { services.TryAddSingleton(); diff --git a/source/Core/CreativeCoders.Core/SysEnvironment/EnvironmentWrapper.cs b/source/Core/CreativeCoders.Core/SysEnvironment/EnvironmentWrapper.cs index dfa77f1d..4e8d00be 100644 --- a/source/Core/CreativeCoders.Core/SysEnvironment/EnvironmentWrapper.cs +++ b/source/Core/CreativeCoders.Core/SysEnvironment/EnvironmentWrapper.cs @@ -10,44 +10,55 @@ namespace CreativeCoders.Core.SysEnvironment; +/// +/// Default implementation of that delegates all calls to . +/// [ExcludeFromCodeCoverage] public class EnvironmentWrapper : IEnvironment { + /// public void Exit(int exitCode) { Environment.Exit(exitCode); } + /// public string ExpandEnvironmentVariables(string name) { return Environment.ExpandEnvironmentVariables(name); } + /// public void FailFast(string? message) { Environment.FailFast(message); } + /// public void FailFast(string? message, Exception? exception) { Environment.FailFast(message, exception); } + /// public string[] GetCommandLineArgs() { return Environment.GetCommandLineArgs(); } + /// public string? GetEnvironmentVariable(string variable) { return Environment.GetEnvironmentVariable(variable); } + /// public string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) { return Environment.GetEnvironmentVariable(variable, target); } + /// public IDictionary GetEnvironmentVariables() { var variables = new Dictionary(); @@ -68,6 +79,7 @@ public string[] GetCommandLineArgs() return variables; } + /// public IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target) { var variables = new Dictionary(); @@ -88,87 +100,114 @@ public string[] GetCommandLineArgs() return variables; } + /// public string GetFolderPath(Environment.SpecialFolder folder) { return Environment.GetFolderPath(folder); } + /// public string GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option) { return Environment.GetFolderPath(folder, option); } + /// public string[] GetLogicalDrives() { return Environment.GetLogicalDrives(); } + /// public void SetEnvironmentVariable(string variable, string? value) { Environment.SetEnvironmentVariable(variable, value); } + /// public void SetEnvironmentVariable(string variable, string? value, EnvironmentVariableTarget target) { Environment.SetEnvironmentVariable(variable, value, target); } + /// public string? GetAppDirectory() { return FileSys.Path.GetDirectoryName(GetAppFileName()); } + /// public string GetAppFileName() { return Process.GetCurrentProcess().MainModule?.FileName ?? Environment.GetCommandLineArgs().FirstOrDefault(string.Empty); } + /// public string CommandLine => Environment.CommandLine; + /// public string CurrentDirectory { get => Environment.CurrentDirectory; set => Environment.CurrentDirectory = value; } + /// public int CurrentManagedThreadId => Environment.CurrentManagedThreadId; + /// public int ExitCode { get => Environment.ExitCode; set => Environment.ExitCode = value; } + /// public bool HasShutdownStarted => Environment.HasShutdownStarted; + /// public bool Is64BitOperatingSystem => Environment.Is64BitOperatingSystem; + /// public bool Is64BitProcess => Environment.Is64BitProcess; + /// public string MachineName => Environment.MachineName; + /// public string NewLine => Environment.NewLine; + /// public OperatingSystem OSVersion => Environment.OSVersion; + /// public int ProcessorCount => Environment.ProcessorCount; + /// public string StackTrace => Environment.StackTrace; + /// public string SystemDirectory => Environment.SystemDirectory; + /// public int SystemPageSize => Environment.SystemPageSize; + /// public int TickCount => Environment.TickCount; + /// public string UserDomainName => Environment.UserDomainName; + /// public bool UserInteractive => Environment.UserInteractive; + /// public string UserName => Environment.UserName; + /// public Version Version => Environment.Version; + /// public long WorkingSet => Environment.WorkingSet; } diff --git a/source/Core/CreativeCoders.Core/SysEnvironment/IEnvironment.cs b/source/Core/CreativeCoders.Core/SysEnvironment/IEnvironment.cs index 1f15e6c3..a3730bc3 100644 --- a/source/Core/CreativeCoders.Core/SysEnvironment/IEnvironment.cs +++ b/source/Core/CreativeCoders.Core/SysEnvironment/IEnvironment.cs @@ -5,77 +5,217 @@ namespace CreativeCoders.Core.SysEnvironment; +/// +/// Provides an abstraction over for testability and substitution. +/// public interface IEnvironment { + /// + /// Terminates the current process and returns the specified exit code to the operating system. + /// + /// The exit code to return to the operating system. void Exit(int exitCode); + /// + /// Replaces the name of each environment variable embedded in the specified string + /// with the string equivalent of the value of the variable. + /// + /// The string containing the names of zero or more environment variables. + /// The string with each environment variable replaced by its value. string ExpandEnvironmentVariables(string name); + /// + /// Immediately terminates the process with the specified message. + /// + /// The message that explains why the process was terminated, or if no explanation is provided. void FailFast(string? message); + /// + /// Immediately terminates the process with the specified message and exception. + /// + /// The message that explains why the process was terminated, or if no explanation is provided. + /// The exception associated with the failure, or if there is none. void FailFast(string? message, Exception? exception); + /// + /// Returns the command-line arguments for the current process. + /// + /// An array of strings containing the command-line arguments. string[] GetCommandLineArgs(); + /// + /// Retrieves the value of an environment variable from the current process. + /// + /// The name of the environment variable. + /// The value of the environment variable, or if the variable is not found. string? GetEnvironmentVariable(string variable); + /// + /// Retrieves the value of an environment variable from the specified location. + /// + /// The name of the environment variable. + /// The location of the environment variable. + /// The value of the environment variable, or if the variable is not found. string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target); + /// + /// Retrieves all environment variable names and their values from the current process. + /// + /// A dictionary containing all environment variable names and their values. IDictionary GetEnvironmentVariables(); + /// + /// Retrieves all environment variable names and their values from the specified location. + /// + /// The location of the environment variables. + /// A dictionary containing all environment variable names and their values. IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target); + /// + /// Returns the path of the specified system special folder. + /// + /// The special folder to retrieve the path for. + /// The path to the specified system special folder. string GetFolderPath(Environment.SpecialFolder folder); + /// + /// Returns the path of the specified system special folder using the specified option. + /// + /// The special folder to retrieve the path for. + /// The option for accessing the special folder. + /// The path to the specified system special folder. string GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option); + /// + /// Returns the names of the logical drives on the current computer. + /// + /// An array of strings containing the names of the logical drives. string[] GetLogicalDrives(); + /// + /// Creates, modifies, or deletes an environment variable stored in the current process. + /// + /// The name of the environment variable. + /// The value to assign to the environment variable, or to delete the variable. void SetEnvironmentVariable(string variable, string? value); + /// + /// Creates, modifies, or deletes an environment variable stored in the specified location. + /// + /// The name of the environment variable. + /// The value to assign to the environment variable, or to delete the variable. + /// The location of the environment variable. void SetEnvironmentVariable(string variable, string? value, EnvironmentVariableTarget target); + /// + /// Returns the directory path of the current application. + /// + /// The application directory path, or if it cannot be determined. string? GetAppDirectory(); + /// + /// Returns the file name of the current application executable. + /// + /// The application executable file name. string GetAppFileName(); + /// + /// Gets the command line for the current process. + /// string CommandLine { get; } + /// + /// Gets or sets the fully qualified path of the current working directory. + /// string CurrentDirectory { get; set; } + /// + /// Gets the unique identifier for the current managed thread. + /// int CurrentManagedThreadId { get; } + /// + /// Gets or sets the exit code of the process. + /// int ExitCode { get; set; } + /// + /// Gets a value indicating whether the common language runtime is shutting down. + /// bool HasShutdownStarted { get; } + /// + /// Gets a value indicating whether the current operating system is a 64-bit operating system. + /// bool Is64BitOperatingSystem { get; } + /// + /// Gets a value indicating whether the current process is a 64-bit process. + /// bool Is64BitProcess { get; } + /// + /// Gets the NetBIOS name of the local computer. + /// string MachineName { get; } + /// + /// Gets the newline string defined for the current environment. + /// string NewLine { get; } + /// + /// Gets an object that contains the current platform identifier and version number. + /// OperatingSystem OSVersion { get; } + /// + /// Gets the number of processors available to the current process. + /// int ProcessorCount { get; } + /// + /// Gets the current stack trace information. + /// string StackTrace { get; } + /// + /// Gets the fully qualified path of the system directory. + /// string SystemDirectory { get; } + /// + /// Gets the amount of memory for an operating system memory page, in bytes. + /// int SystemPageSize { get; } + /// + /// Gets the number of milliseconds elapsed since the system started. + /// int TickCount { get; } + /// + /// Gets the network domain name associated with the current user. + /// string UserDomainName { get; } + /// + /// Gets a value indicating whether the current process is running in user interactive mode. + /// bool UserInteractive { get; } + /// + /// Gets the user name of the person who is associated with the current thread. + /// string UserName { get; } + /// + /// Gets a object that describes the major, minor, build, and revision numbers of the common language runtime. + /// Version Version { get; } + /// + /// Gets the amount of physical memory mapped to the process context, in bytes. + /// long WorkingSet { get; } } diff --git a/source/Core/CreativeCoders.Core/Tasking/TaskEx.cs b/source/Core/CreativeCoders.Core/Tasking/TaskEx.cs index eb330593..d0a0e7e0 100644 --- a/source/Core/CreativeCoders.Core/Tasking/TaskEx.cs +++ b/source/Core/CreativeCoders.Core/Tasking/TaskEx.cs @@ -4,14 +4,17 @@ #nullable enable namespace CreativeCoders.Core.Tasking; +/// +/// Provides extension methods for converting synchronous actions into completed and instances. +/// public static class TaskEx { /// - /// Executes the provided action and returns a completed task. + /// Executes the provided action and returns a completed task. /// - /// The action to be executed. This must not be null. - /// The that represents the completed async operation. - /// Thrown when the provided action is null. + /// The action to execute. + /// A representing the completed operation. + /// is . public static Task AsCompletedTask(this Action action) { Ensure.NotNull(action).Invoke(); @@ -20,11 +23,11 @@ public static Task AsCompletedTask(this Action action) } /// - /// Executes the provided action and returns a completed task. + /// Executes the provided action and returns a completed value task. /// - /// The action to be executed. This must not be null. - /// A that represents the completed async operation. - /// Thrown when the is null. + /// The action to execute. + /// A representing the completed operation. + /// is . public static ValueTask AsCompletedValueTask(this Action action) { Ensure.NotNull(action).Invoke(); diff --git a/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs b/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs index 695924c8..b8b55958 100644 --- a/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs +++ b/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs @@ -8,8 +8,24 @@ namespace CreativeCoders.Core.Text; #nullable enable +/// +/// Provides extension methods for of . +/// public static class EnumerableStringExtensions { + /// + /// Converts a sequence of strings containing key-value pairs into a . + /// + /// The sequence of strings to convert. + /// The separator that delimits keys from values within each string. + /// + /// to silently skip entries that cannot be split into a key-value pair; + /// to throw an for invalid entries. + /// + /// A dictionary containing the parsed key-value pairs. + /// + /// is and an entry does not contain the separator. + /// [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] public static Dictionary ToDictionary(this IEnumerable items, string separator, bool ignoreInvalidEntries = true) @@ -31,6 +47,14 @@ public static Dictionary ToDictionary(this IEnumerable i .ToDictionary(x => x!.Key, x => x!.Value); } + /// + /// Replaces placeholders in each string of the sequence using the specified placeholder delimiters and values. + /// + /// The sequence of strings containing placeholders. + /// The prefix that marks the start of a placeholder. + /// The suffix that marks the end of a placeholder. + /// The dictionary mapping placeholder names to their replacement values. + /// A sequence of strings with placeholders replaced by their corresponding values. [ExcludeFromCodeCoverage] public static IEnumerable ReplacePlaceholders(this IEnumerable items, string placeholderPrefix, string placeholderSuffix, diff --git a/source/Core/CreativeCoders.Core/Text/Json/DefaultJsonSerializer.cs b/source/Core/CreativeCoders.Core/Text/Json/DefaultJsonSerializer.cs index 70372795..dd52b4e5 100644 --- a/source/Core/CreativeCoders.Core/Text/Json/DefaultJsonSerializer.cs +++ b/source/Core/CreativeCoders.Core/Text/Json/DefaultJsonSerializer.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.Json; using System.Threading.Tasks; @@ -8,11 +8,15 @@ namespace CreativeCoders.Core.Text.Json; #nullable enable +/// +/// Provides a default implementation of using . +/// [ExcludeFromCodeCoverage] [PublicAPI] [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")] public class DefaultJsonSerializer : IJsonSerializer { + /// public string Serialize(T data, JsonSerializerOptions? jsonSerializerOptions = null) { Ensure.NotNull(data); @@ -20,6 +24,7 @@ public string Serialize(T data, JsonSerializerOptions? jsonSerializerOptions return JsonSerializer.Serialize(data, jsonSerializerOptions); } + /// public void Serialize(Stream utf8Json, T data, JsonSerializerOptions? jsonSerializerOptions = null) { Ensure.NotNull(utf8Json); @@ -28,6 +33,7 @@ public void Serialize(Stream utf8Json, T data, JsonSerializerOptions? jsonSer JsonSerializer.Serialize(utf8Json, data, jsonSerializerOptions); } + /// public Task SerializeAsync(Stream utf8Json, T data, JsonSerializerOptions? jsonSerializerOptions = null) { @@ -37,6 +43,7 @@ public Task SerializeAsync(Stream utf8Json, T data, return JsonSerializer.SerializeAsync(utf8Json, data, jsonSerializerOptions); } + /// public T? Deserialize(string json, JsonSerializerOptions? jsonSerializerOptions = null) { Ensure.NotNull(json); @@ -44,6 +51,7 @@ public Task SerializeAsync(Stream utf8Json, T data, return JsonSerializer.Deserialize(json, jsonSerializerOptions); } + /// public T? Deserialize(Stream utf8Json, JsonSerializerOptions? jsonSerializerOptions = null) { Ensure.NotNull(utf8Json); @@ -51,6 +59,7 @@ public Task SerializeAsync(Stream utf8Json, T data, return JsonSerializer.Deserialize(utf8Json, jsonSerializerOptions); } + /// public ValueTask DeserializeAsync(Stream utf8Json, JsonSerializerOptions? jsonSerializerOptions = null) { @@ -59,16 +68,19 @@ public Task SerializeAsync(Stream utf8Json, T data, return JsonSerializer.DeserializeAsync(utf8Json, jsonSerializerOptions); } + /// public void Populate(string json, T obj, JsonSerializerOptions? jsonSerializerOptions = null) { JsonExtensions.PopulateJson(json, obj, jsonSerializerOptions); } + /// public void Populate(Stream utf8Json, T obj, JsonSerializerOptions? jsonSerializerOptions = null) { JsonExtensions.PopulateJson(utf8Json, obj, jsonSerializerOptions); } + /// public Task PopulateAsync(Stream utf8Json, T obj, JsonSerializerOptions? jsonSerializerOptions = null) { diff --git a/source/Core/CreativeCoders.Core/Text/Json/IJsonSerializer.cs b/source/Core/CreativeCoders.Core/Text/Json/IJsonSerializer.cs index 764da8fa..f147b66f 100644 --- a/source/Core/CreativeCoders.Core/Text/Json/IJsonSerializer.cs +++ b/source/Core/CreativeCoders.Core/Text/Json/IJsonSerializer.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Text.Json; using System.Threading.Tasks; using JetBrains.Annotations; @@ -7,16 +7,92 @@ namespace CreativeCoders.Core.Text.Json; #nullable enable +/// +/// Defines methods for serializing, deserializing, and populating objects using JSON. +/// [PublicAPI] public interface IJsonSerializer { + /// + /// Serializes the specified object to a JSON string. + /// + /// The type of the object to serialize. + /// The object to serialize. + /// Optional serializer options. + /// A JSON string representation of the object. string Serialize(T data, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Serializes the specified object as JSON into a UTF-8 stream. + /// + /// The type of the object to serialize. + /// The UTF-8 stream to write JSON to. + /// The object to serialize. + /// Optional serializer options. void Serialize(Stream utf8Json, T data, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Asynchronously serializes the specified object as JSON into a UTF-8 stream. + /// + /// The type of the object to serialize. + /// The UTF-8 stream to write JSON to. + /// The object to serialize. + /// Optional serializer options. + /// A task representing the asynchronous serialize operation. Task SerializeAsync(Stream utf8Json, T data, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Deserializes a JSON string into an object of the specified type. + /// + /// The type to deserialize to. + /// The JSON string to deserialize. + /// Optional serializer options. + /// The deserialized object, or if the JSON value is null. T? Deserialize(string json, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Deserializes JSON from a UTF-8 stream into an object of the specified type. + /// + /// The type to deserialize to. + /// The UTF-8 stream containing JSON data. + /// Optional serializer options. + /// The deserialized object, or if the JSON value is null. T? Deserialize(Stream utf8Json, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Asynchronously deserializes JSON from a UTF-8 stream into an object of the specified type. + /// + /// The type to deserialize to. + /// The UTF-8 stream containing JSON data. + /// Optional serializer options. + /// A value task containing the deserialized object, or if the JSON value is null. ValueTask DeserializeAsync(Stream utf8Json, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Populates an existing object's properties from a JSON string. + /// + /// The type of the object to populate. + /// The JSON string containing property values. + /// The existing object to populate. + /// Optional serializer options. void Populate(string json, T obj, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Populates an existing object's properties from a UTF-8 JSON stream. + /// + /// The type of the object to populate. + /// The UTF-8 stream containing JSON data. + /// The existing object to populate. + /// Optional serializer options. void Populate(Stream utf8Json, T obj, JsonSerializerOptions? jsonSerializerOptions = null); + + /// + /// Asynchronously populates an existing object's properties from a UTF-8 JSON stream. + /// + /// The type of the object to populate. + /// The UTF-8 stream containing JSON data. + /// The existing object to populate. + /// Optional serializer options. + /// A task representing the asynchronous populate operation. Task PopulateAsync(Stream utf8Json, T obj, JsonSerializerOptions? jsonSerializerOptions = null); } diff --git a/source/Core/CreativeCoders.Core/Text/JsonExtensions.cs b/source/Core/CreativeCoders.Core/Text/JsonExtensions.cs index 84a346b9..a7d1e710 100644 --- a/source/Core/CreativeCoders.Core/Text/JsonExtensions.cs +++ b/source/Core/CreativeCoders.Core/Text/JsonExtensions.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Text.Json; @@ -10,15 +10,32 @@ namespace CreativeCoders.Core.Text; #nullable enable +/// +/// Provides extension methods for JSON serialization and deserialization using . +/// [PublicAPI] public static class JsonExtensions { + /// + /// Serializes the object to a JSON string. + /// + /// The type of the object to serialize. + /// The object to serialize. + /// Optional serializer options. + /// A JSON string representation of the object. [ExcludeFromCodeCoverage] public static string ToJson(this T data, JsonSerializerOptions? jsonSerializerOptions = null) { return JsonSerializer.Serialize(data, jsonSerializerOptions); } + /// + /// Serializes the object as JSON into the specified UTF-8 stream. + /// + /// The type of the object to serialize. + /// The object to serialize. + /// The UTF-8 stream to write JSON to. + /// Optional serializer options. [ExcludeFromCodeCoverage] public static void ToJson(this T data, Stream utf8Json, JsonSerializerOptions? jsonSerializerOptions = null) @@ -26,18 +43,39 @@ public static void ToJson(this T data, Stream utf8Json, JsonSerializer.Serialize(utf8Json, data, jsonSerializerOptions); } + /// + /// Deserializes JSON from a UTF-8 stream into an object of the specified type. + /// + /// The type to deserialize to. + /// The UTF-8 stream containing JSON data. + /// Optional serializer options. + /// The deserialized object, or if the JSON value is null. [ExcludeFromCodeCoverage] public static T? ReadAsJson(this Stream utf8Json, JsonSerializerOptions? jsonSerializerOptions = null) { return JsonSerializer.Deserialize(utf8Json, jsonSerializerOptions); } + /// + /// Deserializes a JSON string into an object of the specified type. + /// + /// The type to deserialize to. + /// The JSON string to deserialize. + /// Optional serializer options. + /// The deserialized object, or if the JSON value is null. [ExcludeFromCodeCoverage] public static T? ReadAsJson(this string json, JsonSerializerOptions? jsonSerializerOptions = null) { return JsonSerializer.Deserialize(json, jsonSerializerOptions); } + /// + /// Populates an existing object's properties from a JSON string. + /// + /// The type of the object to populate. + /// The JSON string containing property values. + /// The existing object to populate. + /// Optional serializer options. public static void PopulateJson(this string json, T obj, JsonSerializerOptions? jsonSerializerOptions = null) { @@ -50,6 +88,13 @@ public static void PopulateJson(this string json, T obj, PopulateObjectFromJsonDocument(obj, jsonDocument, jsonSerializerOptions); } + /// + /// Populates an existing object's properties from a UTF-8 JSON stream. + /// + /// The type of the object to populate. + /// The UTF-8 stream containing JSON data. + /// The existing object to populate. + /// Optional serializer options. public static void PopulateJson(this Stream utf8Json, T obj, JsonSerializerOptions? jsonSerializerOptions = null) { @@ -61,6 +106,14 @@ public static void PopulateJson(this Stream utf8Json, T obj, PopulateObjectFromJsonDocument(obj, jsonDocument, jsonSerializerOptions); } + /// + /// Asynchronously populates an existing object's properties from a UTF-8 JSON stream. + /// + /// The type of the object to populate. + /// The UTF-8 stream containing JSON data. + /// The existing object to populate. + /// Optional serializer options. + /// A task representing the asynchronous populate operation. public static async Task PopulateJsonAsync(this Stream utf8Json, T obj, JsonSerializerOptions? jsonSerializerOptions = null) { diff --git a/source/Core/CreativeCoders.Core/Text/KeyAndValue.cs b/source/Core/CreativeCoders.Core/Text/KeyAndValue.cs index 7227088f..e9a79942 100644 --- a/source/Core/CreativeCoders.Core/Text/KeyAndValue.cs +++ b/source/Core/CreativeCoders.Core/Text/KeyAndValue.cs @@ -1,9 +1,20 @@ #nullable enable namespace CreativeCoders.Core.Text; +/// +/// Represents an immutable key-value pair of strings. +/// +/// The key. +/// The value. public class KeyAndValue(string key, string value) { + /// + /// Gets the key. + /// public string Key { get; } = Ensure.NotNull(key); + /// + /// Gets the value. + /// public string Value { get; } = Ensure.NotNull(value); } diff --git a/source/Core/CreativeCoders.Core/Text/PatternMatcher.cs b/source/Core/CreativeCoders.Core/Text/PatternMatcher.cs index 9a5e8ca1..97f77906 100644 --- a/source/Core/CreativeCoders.Core/Text/PatternMatcher.cs +++ b/source/Core/CreativeCoders.Core/Text/PatternMatcher.cs @@ -1,15 +1,26 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; #nullable enable namespace CreativeCoders.Core.Text; +/// +/// Provides wildcard pattern matching for strings using * and ? placeholders. +/// public static class PatternMatcher { + /// + /// Determines whether the specified name matches the wildcard mask pattern. + /// The mask supports * (zero or more characters) and ? (exactly one character) wildcards. + /// The comparison is case-insensitive. + /// + /// The name to match against the pattern. + /// The wildcard mask pattern. + /// if the name matches the mask; otherwise, . public static bool MatchesPattern(string name, string mask) { - Ensure.IsNotNullOrEmpty(name, nameof(name)); - Ensure.IsNotNullOrEmpty(mask, nameof(mask)); + Ensure.IsNotNullOrEmpty(name); + Ensure.IsNotNullOrEmpty(mask); var pattern = '^' + Regex.Escape(mask diff --git a/source/Core/CreativeCoders.Core/Text/RandomString.cs b/source/Core/CreativeCoders.Core/Text/RandomString.cs index 1d7d2dba..a2062cbc 100644 --- a/source/Core/CreativeCoders.Core/Text/RandomString.cs +++ b/source/Core/CreativeCoders.Core/Text/RandomString.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Security.Cryptography; using JetBrains.Annotations; @@ -6,17 +6,17 @@ namespace CreativeCoders.Core.Text; -/// A static class for creating a random string. +/// +/// Provides methods for generating cryptographically random Base64-encoded strings. +/// [PublicAPI] public static class RandomString { - ///------------------------------------------------------------------------------------------------- - /// Creates a random base64 string. - /// - /// Size of the buffer used for the random number generator. - /// - /// The random base64 string. - ///------------------------------------------------------------------------------------------------- + /// + /// Creates a random Base64-encoded string using the specified buffer size for the random number generator. + /// + /// The number of random bytes to generate before Base64 encoding. + /// A random Base64-encoded string. public static string Create(int bufferSize) { var buffer = new byte[bufferSize]; @@ -26,11 +26,11 @@ public static string Create(int bufferSize) return Convert.ToBase64String(buffer); } - ///------------------------------------------------------------------------------------------------- - /// Creates a random base64 string with a bufferSize of 128. - /// - /// The random base64 string. - ///------------------------------------------------------------------------------------------------- + /// + /// Creates a random Base64-encoded string using a default buffer size of 128 bytes. + /// + /// A random Base64-encoded string. + /// public static string Create() { return Create(128); diff --git a/source/Core/CreativeCoders.Core/Text/StringExtension.cs b/source/Core/CreativeCoders.Core/Text/StringExtension.cs index 2e600c2d..25a67fc0 100644 --- a/source/Core/CreativeCoders.Core/Text/StringExtension.cs +++ b/source/Core/CreativeCoders.Core/Text/StringExtension.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -12,25 +12,30 @@ namespace CreativeCoders.Core.Text; +/// +/// Provides extension methods for and operations. +/// [PublicAPI] public static class StringExtension { - /// ------------------------------------------------------------------------------------------------- - /// A string extension method that converts a text to a secure string. - /// The text to convert. - /// Text as a SecureString. - /// ------------------------------------------------------------------------------------------------- + /// + /// Converts the string to a . + /// + /// The string to convert. + /// A new containing the characters of the string. public static SecureString ToSecureString(this string? text) { return ToSecureString(text, false); } - /// ------------------------------------------------------------------------------------------------- - /// A string extension method that converts a text to a secure string. - /// The text to convert. - /// True to make the SecureString read only. - /// Text as a readonly SecureString. - /// ------------------------------------------------------------------------------------------------- + /// + /// Converts the string to a , optionally making it read-only. + /// + /// The string to convert. + /// + /// to make the resulting read-only; otherwise, . + /// + /// A new containing the characters of the string. public static SecureString ToSecureString(this string? text, bool makeReadOnly) { var result = new SecureString(); @@ -51,13 +56,11 @@ public static SecureString ToSecureString(this string? text, bool makeReadOnly) return result; } - /// ------------------------------------------------------------------------------------------------- /// - /// A SecureString extension method that converts a SecureString to a normal string. + /// Converts the to a plain . /// - /// The secureString to convert. - /// SecureString as a string. - /// ------------------------------------------------------------------------------------------------- + /// The secure string to convert. + /// The plain-text representation of the secure string, or if the secure string is empty. public static string? ToNormalString(this SecureString secureString) { Ensure.NotNull(secureString); @@ -86,6 +89,11 @@ public static SecureString ToSecureString(this string? text, bool makeReadOnly) return result; } + /// + /// Determines whether the string is or . + /// + /// The string to test. + /// if the string is or empty; otherwise, . [ContractAnnotation("text: null => true")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrEmpty([NotNullWhen(false)] this string? text) @@ -93,6 +101,11 @@ public static bool IsNullOrEmpty([NotNullWhen(false)] this string? text) return string.IsNullOrEmpty(text); } + /// + /// Determines whether the string is neither nor . + /// + /// The string to test. + /// if the string is not and not empty; otherwise, . [ContractAnnotation("text: notnull => true")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNotNullOrEmpty([NotNullWhen(true)] this string? text) @@ -100,6 +113,11 @@ public static bool IsNotNullOrEmpty([NotNullWhen(true)] this string? text) return !string.IsNullOrEmpty(text); } + /// + /// Determines whether the string is , empty, or consists only of white-space characters. + /// + /// The string to test. + /// if the string is , empty, or white-space only; otherwise, . [ContractAnnotation("text: null => true")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? text) @@ -107,6 +125,11 @@ public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? text) return string.IsNullOrWhiteSpace(text); } + /// + /// Determines whether the string is neither , empty, nor consists only of white-space characters. + /// + /// The string to test. + /// if the string contains non-white-space content; otherwise, . [ContractAnnotation("text: notnull => true")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNotNullOrWhiteSpace([NotNullWhen(true)] this string? text) @@ -114,6 +137,15 @@ public static bool IsNotNullOrWhiteSpace([NotNullWhen(true)] this string? text) return !string.IsNullOrWhiteSpace(text); } + /// + /// Appends a line to the , unless the append is suppressed. + /// + /// The string builder to append to. + /// The line to append. + /// + /// to suppress the append operation; otherwise, . + /// + /// The same instance for chaining. public static StringBuilder AppendLine(this StringBuilder stringBuilder, string line, bool suppressAppend) { if (!suppressAppend) @@ -124,6 +156,15 @@ public static StringBuilder AppendLine(this StringBuilder stringBuilder, string return stringBuilder; } + /// + /// Appends the specified text to the if the condition is met. + /// + /// The string builder to append to. + /// + /// to perform the append; otherwise, . + /// + /// The text to append. + /// The same instance for chaining. public static StringBuilder AppendIf(this StringBuilder stringBuilder, bool doAppend, string text) { if (doAppend) @@ -134,6 +175,15 @@ public static StringBuilder AppendIf(this StringBuilder stringBuilder, bool doAp return stringBuilder; } + /// + /// Appends the specified line followed by a line terminator to the if the condition is met. + /// + /// The string builder to append to. + /// + /// to perform the append; otherwise, . + /// + /// The line to append. + /// The same instance for chaining. public static StringBuilder AppendLineIf(this StringBuilder stringBuilder, bool doAppend, string line) { if (doAppend) @@ -144,16 +194,33 @@ public static StringBuilder AppendLineIf(this StringBuilder stringBuilder, bool return stringBuilder; } + /// + /// Filters the string by retaining only characters that satisfy the predicate. + /// + /// The string to filter. + /// A function that returns for characters to keep. + /// A new string containing only the allowed characters, or if the input is or empty. public static string Filter(this string? text, Func isAllowedChar) { return text.IsNullOrEmpty() ? string.Empty : string.Join("", text.Where(isAllowedChar)); } + /// + /// Filters the string by removing the specified characters. + /// + /// The string to filter. + /// The characters to remove. + /// A new string with the specified characters removed. public static string Filter(this string? text, params char[] filteredChars) { return text.Filter(c => !filteredChars.Contains(c)); } + /// + /// Converts a camelCase string to PascalCase by capitalizing the first character. + /// + /// The camelCase string to convert. + /// The PascalCase representation, or if the input is or empty. public static string CamelCaseToPascalCase(this string? text) { if (string.IsNullOrEmpty(text)) @@ -164,11 +231,21 @@ public static string CamelCaseToPascalCase(this string? text) return char.ToUpperInvariant(text[0]) + text[1..]; } + /// + /// Converts a kebab-case string to PascalCase. + /// + /// The kebab-case string to convert. + /// The PascalCase representation, or if the input is or empty. public static string KebabCaseToPascalCase(this string? text) { return SeparatedToPascalCase(text, '-'); } + /// + /// Converts a snake_case string to PascalCase. + /// + /// The snake_case string to convert. + /// The PascalCase representation, or if the input is or empty. public static string SnakeCaseToPascalCase(this string? text) { return SeparatedToPascalCase(text, '_'); @@ -192,6 +269,15 @@ private static string SeparatedToPascalCase(this string? text, char separator) (current, part) => current + char.ToUpperInvariant(part[0]) + part[1..].ToLowerInvariant()); } + /// + /// Splits the string into a key-value pair at the first occurrence of the specified separator. + /// + /// The string to split. + /// The separator string to split on. + /// + /// A containing the key and value parts, or + /// if the input is , empty, or does not contain the separator. + /// public static KeyAndValue? SplitIntoKeyValue(this string? text, string separator) { Ensure.NotNull(separator); diff --git a/source/Core/CreativeCoders.Core/Text/TextSpan.cs b/source/Core/CreativeCoders.Core/Text/TextSpan.cs index 5845c81c..013a1a8a 100644 --- a/source/Core/CreativeCoders.Core/Text/TextSpan.cs +++ b/source/Core/CreativeCoders.Core/Text/TextSpan.cs @@ -1,17 +1,18 @@ -using JetBrains.Annotations; +using JetBrains.Annotations; namespace CreativeCoders.Core.Text; -/// Representing a text span. +/// +/// Represents an immutable span of text defined by a start position and length. +/// [PublicAPI] public class TextSpan { - ///------------------------------------------------------------------------------------------------- - /// Initializes a new instance of the CreativeCoders.Core.TextSpan class. - /// - /// The start of the text span. Must not be less 0. - /// The length of the text span. Must not be less 0. - ///------------------------------------------------------------------------------------------------- + /// + /// Initializes a new instance of the class. + /// + /// The zero-based start position. Must not be negative. + /// The length of the span. Must not be negative. public TextSpan(int start, int length) { Ensure.That(start >= 0, "Start must not be less 0"); @@ -22,31 +23,23 @@ public TextSpan(int start, int length) End = start + length; } - ///------------------------------------------------------------------------------------------------- - /// Gets the start position of the text span. - /// - /// The start position of the text span. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the zero-based start position of the text span. + /// public int Start { get; } - ///------------------------------------------------------------------------------------------------- - /// Gets the length of the text span. - /// - /// The length of the text span. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the length of the text span. + /// public int Length { get; } - ///------------------------------------------------------------------------------------------------- - /// Gets the end position of the text span. - /// - /// The end position of the text span. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets the exclusive end position of the text span, computed as + . + /// public int End { get; } - ///------------------------------------------------------------------------------------------------- - /// Gets a value indicating whether this text span is empty. - /// - /// True if this text span is empty, false if not. - ///------------------------------------------------------------------------------------------------- + /// + /// Gets a value indicating whether the text span is empty (length is zero). + /// public bool IsEmpty => Length == 0; } diff --git a/source/Core/CreativeCoders.Core/Threading/AcquireLockFailedException.cs b/source/Core/CreativeCoders.Core/Threading/AcquireLockFailedException.cs index 1fba42d1..744df311 100644 --- a/source/Core/CreativeCoders.Core/Threading/AcquireLockFailedException.cs +++ b/source/Core/CreativeCoders.Core/Threading/AcquireLockFailedException.cs @@ -3,13 +3,30 @@ namespace CreativeCoders.Core.Threading; -public class AcquireLockFailedException : ApplicationException +/// +/// The exception that is thrown when a lock cannot be acquired within the specified timeout +/// or due to a lock recursion violation. +/// +public class AcquireLockFailedException : Exception { + /// + /// Initializes a new instance of the class + /// with the specified message and timeout. + /// + /// The error message that explains the reason for the exception. + /// The timeout in milliseconds that was exceeded. public AcquireLockFailedException(string message, int timeout) : base(message) { Timeout = timeout; } + /// + /// Initializes a new instance of the class + /// with the specified message, timeout, and inner . + /// + /// The error message that explains the reason for the exception. + /// The timeout in milliseconds that was exceeded. + /// The inner exception that caused this exception. public AcquireLockFailedException(string message, int timeout, LockRecursionException lockRecursionException) : base(message, lockRecursionException) @@ -17,5 +34,8 @@ public AcquireLockFailedException(string message, int timeout, Timeout = timeout; } + /// + /// Gets the timeout in milliseconds that was exceeded when attempting to acquire the lock. + /// public int Timeout { get; } } diff --git a/source/Core/CreativeCoders.Core/Threading/AcquireReaderLock.cs b/source/Core/CreativeCoders.Core/Threading/AcquireReaderLock.cs index b4769b53..1e1f8a4d 100644 --- a/source/Core/CreativeCoders.Core/Threading/AcquireReaderLock.cs +++ b/source/Core/CreativeCoders.Core/Threading/AcquireReaderLock.cs @@ -5,6 +5,10 @@ namespace CreativeCoders.Core.Threading; +/// +/// Acquires a read lock on a upon construction +/// and releases it upon disposal. +/// [PublicAPI] public sealed class AcquireReaderLock : IDisposable { @@ -12,11 +16,27 @@ public sealed class AcquireReaderLock : IDisposable private bool _disposed; + /// + /// Initializes a new instance of the class + /// with a new . + /// [ExcludeFromCodeCoverage] public AcquireReaderLock() : this(new ReaderWriterLockSlim()) { } + /// + /// Initializes a new instance of the class + /// with the specified lock and an infinite timeout. + /// + /// The reader-writer lock to acquire a read lock on. public AcquireReaderLock(ReaderWriterLockSlim lockSlim) : this(lockSlim, Timeout.Infinite) { } + /// + /// Initializes a new instance of the class + /// with the specified lock and timeout. + /// + /// The reader-writer lock to acquire a read lock on. + /// The timeout in milliseconds for acquiring the lock. + /// The read lock could not be acquired within the specified timeout or due to a lock recursion violation. public AcquireReaderLock(ReaderWriterLockSlim lockSlim, int timeout) { Ensure.IsNotNull(lockSlim); @@ -35,6 +55,7 @@ public AcquireReaderLock(ReaderWriterLockSlim lockSlim, int timeout) } } + /// public void Dispose() { Dispose(true); diff --git a/source/Core/CreativeCoders.Core/Threading/AcquireUpgradeableReaderLock.cs b/source/Core/CreativeCoders.Core/Threading/AcquireUpgradeableReaderLock.cs index f5363f88..b388d40a 100644 --- a/source/Core/CreativeCoders.Core/Threading/AcquireUpgradeableReaderLock.cs +++ b/source/Core/CreativeCoders.Core/Threading/AcquireUpgradeableReaderLock.cs @@ -5,21 +5,41 @@ namespace CreativeCoders.Core.Threading; +/// +/// Acquires an upgradeable read lock on a upon construction +/// and releases it upon disposal. Supports upgrading to a write lock via . +/// [PublicAPI] -public class AcquireUpgradeableReaderLock : IDisposable +public sealed class AcquireUpgradeableReaderLock : IDisposable { private readonly ReaderWriterLockSlim _lockSlim; private bool _disposed; + /// + /// Initializes a new instance of the class + /// with a new . + /// [ExcludeFromCodeCoverage] public AcquireUpgradeableReaderLock() : this(new ReaderWriterLockSlim()) { } + /// + /// Initializes a new instance of the class + /// with the specified lock and an infinite timeout. + /// + /// The reader-writer lock to acquire an upgradeable read lock on. public AcquireUpgradeableReaderLock(ReaderWriterLockSlim lockSlim) : this(lockSlim, Timeout.Infinite) { } + /// + /// Initializes a new instance of the class + /// with the specified lock and timeout. + /// + /// The reader-writer lock to acquire an upgradeable read lock on. + /// The timeout in milliseconds for acquiring the lock. + /// The upgradeable read lock could not be acquired within the specified timeout. public AcquireUpgradeableReaderLock(ReaderWriterLockSlim lockSlim, int timeout) { - Ensure.IsNotNull(lockSlim, nameof(lockSlim)); + Ensure.IsNotNull(lockSlim); _lockSlim = lockSlim; if (!_lockSlim.TryEnterUpgradeableReadLock(timeout)) @@ -28,11 +48,21 @@ public AcquireUpgradeableReaderLock(ReaderWriterLockSlim lockSlim, int timeout) } } + /// + /// Upgrades the current lock to a write lock with an infinite timeout. + /// + /// An that releases the write lock when disposed. public IDisposable UseWriteLock() { return UseWriteLock(Timeout.Infinite); } + /// + /// Upgrades the current lock to a write lock with the specified timeout. + /// + /// The timeout in milliseconds for acquiring the write lock. + /// An that releases the write lock when disposed. + /// The write lock could not be acquired within the specified timeout. public IDisposable UseWriteLock(int timeout) { if (!_lockSlim.TryEnterWriteLock(timeout)) @@ -43,6 +73,7 @@ public IDisposable UseWriteLock(int timeout) return new DelegateDisposable(() => _lockSlim.ExitWriteLock(), true); } + /// public void Dispose() { Dispose(true); diff --git a/source/Core/CreativeCoders.Core/Threading/AcquireWriterLock.cs b/source/Core/CreativeCoders.Core/Threading/AcquireWriterLock.cs index 5df69383..1eaaa245 100644 --- a/source/Core/CreativeCoders.Core/Threading/AcquireWriterLock.cs +++ b/source/Core/CreativeCoders.Core/Threading/AcquireWriterLock.cs @@ -5,21 +5,41 @@ namespace CreativeCoders.Core.Threading; +/// +/// Acquires a write lock on a upon construction +/// and releases it upon disposal. +/// [PublicAPI] -public class AcquireWriterLock : IDisposable +public sealed class AcquireWriterLock : IDisposable { private readonly ReaderWriterLockSlim _lockSlim; private bool _disposed; + /// + /// Initializes a new instance of the class + /// with a new . + /// [ExcludeFromCodeCoverage] public AcquireWriterLock() : this(new ReaderWriterLockSlim()) { } + /// + /// Initializes a new instance of the class + /// with the specified lock and an infinite timeout. + /// + /// The reader-writer lock to acquire a write lock on. public AcquireWriterLock(ReaderWriterLockSlim lockSlim) : this(lockSlim, Timeout.Infinite) { } + /// + /// Initializes a new instance of the class + /// with the specified lock and timeout. + /// + /// The reader-writer lock to acquire a write lock on. + /// The timeout in milliseconds for acquiring the lock. + /// The write lock could not be acquired within the specified timeout. public AcquireWriterLock(ReaderWriterLockSlim lockSlim, int timeout) { - Ensure.IsNotNull(lockSlim, nameof(lockSlim)); + Ensure.IsNotNull(lockSlim); _lockSlim = lockSlim; @@ -29,12 +49,19 @@ public AcquireWriterLock(ReaderWriterLockSlim lockSlim, int timeout) } } + /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + /// + /// Releases the write lock if it has not already been released. + /// + /// + /// to release managed resources; otherwise, . + /// protected void Dispose(bool disposing) { if (!_disposed && disposing) diff --git a/source/Core/CreativeCoders.Core/Threading/ConcurrentList.cs b/source/Core/CreativeCoders.Core/Threading/ConcurrentList.cs index dcbc4330..f1341739 100644 --- a/source/Core/CreativeCoders.Core/Threading/ConcurrentList.cs +++ b/source/Core/CreativeCoders.Core/Threading/ConcurrentList.cs @@ -6,14 +6,28 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides a thread-safe implementation of that uses +/// an to synchronize access to the underlying list. +/// +/// The type of elements in the list. [PublicAPI] public class ConcurrentList : IList, IReadOnlyCollection { private readonly List _items; private readonly ILockingMechanism _locking; + /// + /// Initializes a new instance of the class + /// with a default . + /// public ConcurrentList() : this(DefaultLockingMechanism()) { } + /// + /// Initializes a new instance of the class + /// with the specified locking mechanism. + /// + /// The locking mechanism used to synchronize access. public ConcurrentList(ILockingMechanism lockingMechanism) { Ensure.IsNotNull(lockingMechanism); @@ -22,12 +36,23 @@ public ConcurrentList(ILockingMechanism lockingMechanism) _items = []; } + /// + /// Initializes a new instance of the class + /// with elements copied from the specified collection and a default locking mechanism. + /// + /// The collection whose elements are copied to the new list. public ConcurrentList(IEnumerable collection) : this(collection, DefaultLockingMechanism()) { } + /// + /// Initializes a new instance of the class + /// with elements copied from the specified collection and the specified locking mechanism. + /// + /// The collection whose elements are copied to the new list. + /// The locking mechanism used to synchronize access. public ConcurrentList(IEnumerable collection, ILockingMechanism lockingMechanism) { - Ensure.IsNotNull(lockingMechanism, nameof(lockingMechanism)); - Ensure.IsNotNull(collection, nameof(collection)); + Ensure.IsNotNull(lockingMechanism); + Ensure.IsNotNull(collection); _locking = lockingMechanism; _items = [..collection]; @@ -38,65 +63,82 @@ private static LockSlimLockingMechanism DefaultLockingMechanism() return new LockSlimLockingMechanism(); } + /// [MustDisposeResource] public IEnumerator GetEnumerator() => _locking.Read([MustDisposeResource]() => _items.ToList().GetEnumerator()); + /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + /// public void Add(T item) { _locking.Write(() => _items.Add(item)); } + /// public void Clear() { _locking.Write(() => _items.Clear()); } + /// public bool Contains(T item) { return _locking.Read(() => _items.Contains(item)); } + /// public void CopyTo(T[] array, int arrayIndex) { _locking.Read(() => _items.CopyTo(array, arrayIndex)); } + /// public bool Remove(T item) { return _locking.Write(() => _items.Remove(item)); } + /// public int IndexOf(T item) { return _locking.Read(() => _items.IndexOf(item)); } + /// public void Insert(int index, T item) { _locking.Write(() => _items.Insert(index, item)); } + /// public void RemoveAt(int index) { _locking.Write(() => _items.RemoveAt(index)); } + /// public int Count => _locking.Read(() => _items.Count); + /// public bool IsReadOnly => false; + /// public T this[int index] { get { return _locking.Read(() => _items[index]); } set { _locking.Write(() => _items[index] = value); } } + /// + /// Gets or sets the total number of elements the internal data structure can hold + /// without resizing. + /// [ExcludeFromCodeCoverage] public int Capacity { diff --git a/source/Core/CreativeCoders.Core/Threading/ILockingMechanism.cs b/source/Core/CreativeCoders.Core/Threading/ILockingMechanism.cs index e75327bf..90e85f26 100644 --- a/source/Core/CreativeCoders.Core/Threading/ILockingMechanism.cs +++ b/source/Core/CreativeCoders.Core/Threading/ILockingMechanism.cs @@ -2,13 +2,36 @@ namespace CreativeCoders.Core.Threading; +/// +/// Defines a mechanism for executing actions and functions within read and write locks. +/// public interface ILockingMechanism { + /// + /// Executes the specified action within a read lock. + /// + /// The action to execute under the read lock. void Read(Action action); + /// + /// Executes the specified function within a read lock and returns the result. + /// + /// The type of the return value. + /// The function to execute under the read lock. + /// The result of the function. T Read(Func function); + /// + /// Executes the specified action within a write lock. + /// + /// The action to execute under the write lock. void Write(Action action); + /// + /// Executes the specified function within a write lock and returns the result. + /// + /// The type of the return value. + /// The function to execute under the write lock. + /// The result of the function. T Write(Func function); } diff --git a/source/Core/CreativeCoders.Core/Threading/IUpgradeableLockingMechanism.cs b/source/Core/CreativeCoders.Core/Threading/IUpgradeableLockingMechanism.cs index 56ed95ec..bb9c2b08 100644 --- a/source/Core/CreativeCoders.Core/Threading/IUpgradeableLockingMechanism.cs +++ b/source/Core/CreativeCoders.Core/Threading/IUpgradeableLockingMechanism.cs @@ -2,10 +2,24 @@ namespace CreativeCoders.Core.Threading; +/// +/// Extends with support for upgradeable read locks +/// that can be promoted to write locks when needed. +/// [PublicAPI] public interface IUpgradeableLockingMechanism : ILockingMechanism { + /// + /// Executes the specified action within an upgradeable read lock. + /// + /// The action to execute, which receives a factory for upgrading to a write lock. void UpgradeableRead(UpgradeableReadAction action); + /// + /// Executes the specified function within an upgradeable read lock and returns the result. + /// + /// The type of the return value. + /// The function to execute, which receives a factory for upgrading to a write lock. + /// The result of the function. T UpgradeableRead(UpgradeableReadFunc function); } diff --git a/source/Core/CreativeCoders.Core/Threading/LockExtension.cs b/source/Core/CreativeCoders.Core/Threading/LockExtension.cs index 1a977d5e..1227a40c 100644 --- a/source/Core/CreativeCoders.Core/Threading/LockExtension.cs +++ b/source/Core/CreativeCoders.Core/Threading/LockExtension.cs @@ -3,8 +3,18 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides extension methods for executing actions and functions within +/// read and write locks, and within +/// statements on arbitrary objects. +/// public static class LockExtension { + /// + /// Executes the specified action within a reader lock. + /// + /// The reader-writer lock. + /// The action to execute under the read lock. public static void Read(this ReaderWriterLockSlim self, Action action) { using (new AcquireReaderLock(self)) @@ -13,6 +23,13 @@ public static void Read(this ReaderWriterLockSlim self, Action action) } } + /// + /// Executes the specified action within a reader lock with a timeout. + /// + /// The reader-writer lock. + /// The action to execute under the read lock. + /// The timeout in milliseconds for acquiring the lock. + /// The lock could not be acquired within the specified timeout. public static void Read(this ReaderWriterLockSlim self, Action action, int timeout) { using (new AcquireReaderLock(self, timeout)) @@ -21,6 +38,13 @@ public static void Read(this ReaderWriterLockSlim self, Action action, int timeo } } + /// + /// Executes the specified function within a reader lock and returns the result. + /// + /// The type of the return value. + /// The reader-writer lock. + /// The function to execute under the read lock. + /// The result of the function. public static T Read(this ReaderWriterLockSlim self, Func function) { using (new AcquireReaderLock(self)) @@ -29,6 +53,15 @@ public static T Read(this ReaderWriterLockSlim self, Func function) } } + /// + /// Executes the specified function within a reader lock with a timeout and returns the result. + /// + /// The type of the return value. + /// The reader-writer lock. + /// The function to execute under the read lock. + /// The timeout in milliseconds for acquiring the lock. + /// The result of the function. + /// The lock could not be acquired within the specified timeout. public static T Read(this ReaderWriterLockSlim self, Func function, int timeout) { using (new AcquireReaderLock(self, timeout)) @@ -37,6 +70,11 @@ public static T Read(this ReaderWriterLockSlim self, Func function, int ti } } + /// + /// Executes the specified action within a writer lock. + /// + /// The reader-writer lock. + /// The action to execute under the write lock. public static void Write(this ReaderWriterLockSlim self, Action action) { using (new AcquireWriterLock(self)) @@ -45,6 +83,13 @@ public static void Write(this ReaderWriterLockSlim self, Action action) } } + /// + /// Executes the specified action within a writer lock with a timeout. + /// + /// The reader-writer lock. + /// The action to execute under the write lock. + /// The timeout in milliseconds for acquiring the lock. + /// The lock could not be acquired within the specified timeout. public static void Write(this ReaderWriterLockSlim self, Action action, int timeout) { using (new AcquireWriterLock(self, timeout)) @@ -53,6 +98,13 @@ public static void Write(this ReaderWriterLockSlim self, Action action, int time } } + /// + /// Executes the specified function within a writer lock and returns the result. + /// + /// The type of the return value. + /// The reader-writer lock. + /// The function to execute under the write lock. + /// The result of the function. public static T Write(this ReaderWriterLockSlim self, Func function) { using (new AcquireWriterLock(self)) @@ -61,6 +113,15 @@ public static T Write(this ReaderWriterLockSlim self, Func function) } } + /// + /// Executes the specified function within a writer lock with a timeout and returns the result. + /// + /// The type of the return value. + /// The reader-writer lock. + /// The function to execute under the write lock. + /// The timeout in milliseconds for acquiring the lock. + /// The result of the function. + /// The lock could not be acquired within the specified timeout. public static T Write(this ReaderWriterLockSlim self, Func function, int timeout) { using (new AcquireWriterLock(self, timeout)) @@ -69,6 +130,11 @@ public static T Write(this ReaderWriterLockSlim self, Func function, int t } } + /// + /// Executes the specified action within a statement on the object. + /// + /// The object to lock on. + /// The action to execute under the lock. public static void Lock(this object self, Action action) { lock (self) @@ -77,6 +143,14 @@ public static void Lock(this object self, Action action) } } + /// + /// Executes the specified function within a statement on the object + /// and returns the result. + /// + /// The type of the return value. + /// The object to lock on. + /// The function to execute under the lock. + /// The result of the function. public static T Lock(this object self, Func function) { lock (self) diff --git a/source/Core/CreativeCoders.Core/Threading/LockLockingMechanism.cs b/source/Core/CreativeCoders.Core/Threading/LockLockingMechanism.cs index c5e75e06..79964d78 100644 --- a/source/Core/CreativeCoders.Core/Threading/LockLockingMechanism.cs +++ b/source/Core/CreativeCoders.Core/Threading/LockLockingMechanism.cs @@ -2,22 +2,36 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides a locking mechanism based on the C# statement, +/// where both read and write operations use the same exclusive lock. +/// public class LockLockingMechanism : ILockingMechanism { private readonly object _lockObj; + /// + /// Initializes a new instance of the class + /// using the instance itself as the lock object. + /// public LockLockingMechanism() { _lockObj = this; } + /// + /// Initializes a new instance of the class + /// with the specified lock object. + /// + /// The object to use for locking. public LockLockingMechanism(object lockObject) { - Ensure.IsNotNull(lockObject, nameof(lockObject)); + Ensure.IsNotNull(lockObject); _lockObj = lockObject; } + /// public void Read(Action action) { lock (_lockObj) @@ -26,6 +40,7 @@ public void Read(Action action) } } + /// public T Read(Func function) { lock (_lockObj) @@ -34,6 +49,7 @@ public T Read(Func function) } } + /// public void Write(Action action) { lock (_lockObj) @@ -42,6 +58,7 @@ public void Write(Action action) } } + /// public T Write(Func function) { lock (_lockObj) diff --git a/source/Core/CreativeCoders.Core/Threading/LockSlimLockingMechanism.cs b/source/Core/CreativeCoders.Core/Threading/LockSlimLockingMechanism.cs index 6d14bbd2..d17ff984 100644 --- a/source/Core/CreativeCoders.Core/Threading/LockSlimLockingMechanism.cs +++ b/source/Core/CreativeCoders.Core/Threading/LockSlimLockingMechanism.cs @@ -3,19 +3,33 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides a locking mechanism based on +/// that supports concurrent reads, exclusive writes, and upgradeable read locks. +/// public class LockSlimLockingMechanism : IUpgradeableLockingMechanism { private readonly ReaderWriterLockSlim _lock; + /// + /// Initializes a new instance of the class + /// with a new . + /// public LockSlimLockingMechanism() : this(new ReaderWriterLockSlim()) { } + /// + /// Initializes a new instance of the class + /// with the specified . + /// + /// The reader-writer lock to use. public LockSlimLockingMechanism(ReaderWriterLockSlim lockSlim) { - Ensure.IsNotNull(lockSlim, nameof(lockSlim)); + Ensure.IsNotNull(lockSlim); _lock = lockSlim; } + /// public void Read(Action action) { _lock.EnterReadLock(); @@ -29,6 +43,7 @@ public void Read(Action action) } } + /// public T Read(Func function) { _lock.EnterReadLock(); @@ -42,6 +57,7 @@ public T Read(Func function) } } + /// public void Write(Action action) { _lock.EnterWriteLock(); @@ -55,6 +71,7 @@ public void Write(Action action) } } + /// public T Write(Func function) { _lock.EnterWriteLock(); @@ -68,6 +85,7 @@ public T Write(Func function) } } + /// public void UpgradeableRead(UpgradeableReadAction action) { _lock.EnterUpgradeableReadLock(); @@ -88,6 +106,7 @@ public void UpgradeableRead(UpgradeableReadAction action) } } + /// public T UpgradeableRead(UpgradeableReadFunc function) { _lock.EnterUpgradeableReadLock(); diff --git a/source/Core/CreativeCoders.Core/Threading/MutexLock.cs b/source/Core/CreativeCoders.Core/Threading/MutexLock.cs index c1adde5a..bd14f95a 100644 --- a/source/Core/CreativeCoders.Core/Threading/MutexLock.cs +++ b/source/Core/CreativeCoders.Core/Threading/MutexLock.cs @@ -4,6 +4,10 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides a disposable lock based on a named . Acquires the mutex +/// upon construction and releases it upon disposal. +/// public sealed class MutexLock : IDisposable { private Mutex _mutex; @@ -12,9 +16,14 @@ public sealed class MutexLock : IDisposable private readonly bool _hasMutexLock; + /// + /// Initializes a new instance of the class and acquires + /// the named mutex, blocking until the mutex is available. + /// + /// The name of the system mutex to acquire. public MutexLock(string mutexName) { - Ensure.IsNotNullOrWhitespace(mutexName, nameof(mutexName)); + Ensure.IsNotNullOrWhitespace(mutexName); if (!Mutex.TryOpenExisting(mutexName, out _mutex)) { @@ -53,6 +62,7 @@ private void Dispose(bool disposing) _disposed = true; } + /// public void Dispose() { Dispose(true); diff --git a/source/Core/CreativeCoders.Core/Threading/NoLockingMechanism.cs b/source/Core/CreativeCoders.Core/Threading/NoLockingMechanism.cs index 17aefa95..2d3f64fd 100644 --- a/source/Core/CreativeCoders.Core/Threading/NoLockingMechanism.cs +++ b/source/Core/CreativeCoders.Core/Threading/NoLockingMechanism.cs @@ -2,35 +2,31 @@ namespace CreativeCoders.Core.Threading; -///------------------------------------------------------------------------------------------------- /// -/// A locking mechanism implementation that does no locking. Can be used in single threaded -/// environments. +/// Provides a no-op locking mechanism that executes actions and functions without any +/// synchronization. Suitable for single-threaded environments. /// -/// -/// -///------------------------------------------------------------------------------------------------- public class NoLockingMechanism : ILockingMechanism { - /// + /// public void Read(Action action) { action(); } - /// + /// public T Read(Func function) { return function(); } - /// + /// public void Write(Action action) { action(); } - /// + /// public T Write(Func function) { return function(); diff --git a/source/Core/CreativeCoders.Core/Threading/SimpleMonitor.cs b/source/Core/CreativeCoders.Core/Threading/SimpleMonitor.cs index 5bc2f60c..fba46bc7 100644 --- a/source/Core/CreativeCoders.Core/Threading/SimpleMonitor.cs +++ b/source/Core/CreativeCoders.Core/Threading/SimpleMonitor.cs @@ -3,19 +3,33 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides a simple thread-safe reentrancy monitor. Call to signal +/// entry and dispose the instance to signal exit. The property indicates +/// whether the monitor is currently entered. +/// public sealed class SimpleMonitor : IDisposable { private long _counter; + /// + /// Signals entry into the monitored region by incrementing the internal counter. + /// public void Enter() { Interlocked.Increment(ref _counter); } + /// + /// Signals exit from the monitored region by decrementing the internal counter. + /// public void Dispose() { Interlocked.Decrement(ref _counter); } + /// + /// Gets a value indicating whether the monitor is currently entered. + /// public bool Busy => Interlocked.Read(ref _counter) > 0; } diff --git a/source/Core/CreativeCoders.Core/Threading/SynchronizationMethod.cs b/source/Core/CreativeCoders.Core/Threading/SynchronizationMethod.cs index 5c4d32cb..5b0e767f 100644 --- a/source/Core/CreativeCoders.Core/Threading/SynchronizationMethod.cs +++ b/source/Core/CreativeCoders.Core/Threading/SynchronizationMethod.cs @@ -1,8 +1,22 @@ namespace CreativeCoders.Core.Threading; +/// +/// Specifies the method used to dispatch operations to a synchronization context. +/// public enum SynchronizationMethod { + /// + /// No synchronization is performed. + /// None, + + /// + /// Dispatches a synchronous message to the synchronization context. + /// Send, + + /// + /// Dispatches an asynchronous message to the synchronization context. + /// Post } diff --git a/source/Core/CreativeCoders.Core/Threading/SynchronizedValue.cs b/source/Core/CreativeCoders.Core/Threading/SynchronizedValue.cs index 1fb2e359..210528d8 100644 --- a/source/Core/CreativeCoders.Core/Threading/SynchronizedValue.cs +++ b/source/Core/CreativeCoders.Core/Threading/SynchronizedValue.cs @@ -5,32 +5,66 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides factory methods for creating instances. +/// [PublicAPI] public static class SynchronizedValue { + /// + /// Creates a new with the default value for the specified value type. + /// + /// The type of the value. Must be a value type. + /// A new initialized to the default value. public static SynchronizedValue Create() where T : struct { return new SynchronizedValue(default); } + /// + /// Creates a new with the specified initial value. + /// + /// The type of the value. + /// The initial value. + /// A new initialized to the specified value. public static SynchronizedValue Create(T value) { return new SynchronizedValue(value); } + /// + /// Creates a new with the default value for the specified value type + /// and the specified locking mechanism. + /// + /// The type of the value. Must be a value type. + /// The locking mechanism used to synchronize access. + /// A new initialized to the default value. public static SynchronizedValue Create(ILockingMechanism lockingMechanism) where T : struct { return new SynchronizedValue(lockingMechanism, default); } + /// + /// Creates a new with the specified initial value + /// and locking mechanism. + /// + /// The type of the value. + /// The locking mechanism used to synchronize access. + /// The initial value. + /// A new initialized to the specified value. public static SynchronizedValue Create(ILockingMechanism lockingMechanism, T value) { return new SynchronizedValue(lockingMechanism, value); } } +/// +/// Provides a thread-safe wrapper around a value of type , +/// using an to synchronize read and write access. +/// +/// The type of the synchronized value. [PublicAPI] public class SynchronizedValue { @@ -38,8 +72,19 @@ public class SynchronizedValue private T _value; + /// + /// Initializes a new instance of the class + /// with the specified initial value and a default locking mechanism. + /// + /// The initial value. internal SynchronizedValue(T value) : this(DefaultLockingMechanism(), value) { } + /// + /// Initializes a new instance of the class + /// with the specified locking mechanism and initial value. + /// + /// The locking mechanism used to synchronize access. + /// The initial value. internal SynchronizedValue(ILockingMechanism lockingMechanism, T value) { Ensure.IsNotNull(lockingMechanism); @@ -53,6 +98,10 @@ private static LockSlimLockingMechanism DefaultLockingMechanism() return new LockSlimLockingMechanism(); } + /// + /// Sets the value by applying the specified transformation function within a write lock. + /// + /// A function that receives the current value and returns the new value. public void SetValue(Func setValue) { _lockingMechanism.Write(() => { _value = SetValueCore(setValue); }); @@ -63,6 +112,10 @@ private T SetValueCore(Func setValue) return setValue(_value); } + /// + /// Gets or sets the synchronized value. Read access uses a read lock; write access uses + /// a write lock. + /// public T Value { get => _lockingMechanism.Read(() => _value); diff --git a/source/Core/CreativeCoders.Core/Threading/TaskExtensions.cs b/source/Core/CreativeCoders.Core/Threading/TaskExtensions.cs index 006e4824..f8bed1b3 100644 --- a/source/Core/CreativeCoders.Core/Threading/TaskExtensions.cs +++ b/source/Core/CreativeCoders.Core/Threading/TaskExtensions.cs @@ -5,14 +5,28 @@ namespace CreativeCoders.Core.Threading; +/// +/// Provides extension methods for to support fire-and-forget execution +/// and result type conversion. +/// [PublicAPI] public static class TaskExtensions { + /// + /// Executes the task without awaiting it and routes any exception to the specified error handler. + /// + /// The task to execute. + /// The error handler that receives any thrown exception. public static void FireAndForgetAsync(this Task task, IErrorHandler errorHandler) { task.FireAndForgetAsync(e => errorHandler?.HandleException(e)); } + /// + /// Executes the task without awaiting it and routes any exception to the specified callback. + /// + /// The task to execute. + /// The callback that receives any thrown exception. public static async void FireAndForgetAsync(this Task task, Action errorHandler) { try @@ -25,6 +39,13 @@ public static async void FireAndForgetAsync(this Task task, Action er } } + /// + /// Awaits the task and extracts its result as the specified type using reflection. + /// + /// The type of the expected result. + /// The task whose result to extract. + /// The result of the task cast to . + /// The task does not have a Result property. public static async Task ToTask(this Task task) { await task.ConfigureAwait(false); diff --git a/source/Core/CreativeCoders.Core/Threading/UpgradeableReadAction.cs b/source/Core/CreativeCoders.Core/Threading/UpgradeableReadAction.cs index a7d93110..506662b2 100644 --- a/source/Core/CreativeCoders.Core/Threading/UpgradeableReadAction.cs +++ b/source/Core/CreativeCoders.Core/Threading/UpgradeableReadAction.cs @@ -2,4 +2,12 @@ namespace CreativeCoders.Core.Threading; +/// +/// Represents an action that executes within an upgradeable read lock and can optionally +/// upgrade to a write lock by calling the provided factory. +/// +/// +/// A factory that acquires a write lock and returns an +/// that releases it when disposed. +/// public delegate void UpgradeableReadAction(Func useWriteLock); diff --git a/source/Core/CreativeCoders.Core/Threading/UpgradeableReadFunc.cs b/source/Core/CreativeCoders.Core/Threading/UpgradeableReadFunc.cs index e1ddb34e..dcf18917 100644 --- a/source/Core/CreativeCoders.Core/Threading/UpgradeableReadFunc.cs +++ b/source/Core/CreativeCoders.Core/Threading/UpgradeableReadFunc.cs @@ -2,4 +2,14 @@ namespace CreativeCoders.Core.Threading; +/// +/// Represents a function that executes within an upgradeable read lock and can optionally +/// upgrade to a write lock by calling the provided factory. +/// +/// The type of the return value. +/// +/// A factory that acquires a write lock and returns an +/// that releases it when disposed. +/// +/// The result of the function. public delegate T UpgradeableReadFunc(Func useWriteLock); diff --git a/source/Core/CreativeCoders.Core/Visitors/AcceptForVisitorNotFoundException.cs b/source/Core/CreativeCoders.Core/Visitors/AcceptForVisitorNotFoundException.cs index 065cb32b..7c68d64a 100644 --- a/source/Core/CreativeCoders.Core/Visitors/AcceptForVisitorNotFoundException.cs +++ b/source/Core/CreativeCoders.Core/Visitors/AcceptForVisitorNotFoundException.cs @@ -2,8 +2,17 @@ namespace CreativeCoders.Core.Visitors; +/// +/// The exception that is thrown when a visitable object does not have a matching accept method +/// for the given visitor type and the visitor's +/// property is . +/// public class AcceptForVisitorNotFoundException : Exception { + /// + /// Initializes a new instance of the class. + /// + /// The fully qualified name of the visitor type that was not accepted. public AcceptForVisitorNotFoundException(string visitorName) : base( $"Visitable object has no accept method for visitor '{visitorName}'") { } } diff --git a/source/Core/CreativeCoders.Core/Visitors/IVisitable.cs b/source/Core/CreativeCoders.Core/Visitors/IVisitable.cs index a7d0bb29..58e12648 100644 --- a/source/Core/CreativeCoders.Core/Visitors/IVisitable.cs +++ b/source/Core/CreativeCoders.Core/Visitors/IVisitable.cs @@ -1,6 +1,13 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Defines a non-generic visitable object in the Visitor design pattern that accepts any visitor. +/// public interface IVisitable { + /// + /// Accepts a visitor, allowing it to perform an operation on this object. + /// + /// The visitor to accept. void Accept(object visitor); } diff --git a/source/Core/CreativeCoders.Core/Visitors/IVisitableGeneric.cs b/source/Core/CreativeCoders.Core/Visitors/IVisitableGeneric.cs index 5ecef6f0..43118c4f 100644 --- a/source/Core/CreativeCoders.Core/Visitors/IVisitableGeneric.cs +++ b/source/Core/CreativeCoders.Core/Visitors/IVisitableGeneric.cs @@ -2,10 +2,20 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Defines a strongly-typed visitable object in the Visitor design pattern that accepts +/// a specific type. +/// +/// The type of the visitor that can visit this object. +/// The type of the visitable object. [PublicAPI] public interface IVisitable where TVisitor : IVisitor where TVisitableObject : IVisitable { + /// + /// Accepts a strongly-typed visitor, allowing it to perform an operation on this object. + /// + /// The visitor to accept. void Accept(TVisitor visitor); } diff --git a/source/Core/CreativeCoders.Core/Visitors/IVisitableSubItems.cs b/source/Core/CreativeCoders.Core/Visitors/IVisitableSubItems.cs index 977d20f3..5374cc76 100644 --- a/source/Core/CreativeCoders.Core/Visitors/IVisitableSubItems.cs +++ b/source/Core/CreativeCoders.Core/Visitors/IVisitableSubItems.cs @@ -2,7 +2,14 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Defines a visitable object that exposes child items for recursive visitation in the Visitor pattern. +/// public interface IVisitableSubItems { + /// + /// Returns the child items that should be visited recursively. + /// + /// An enumerable of visitable child items. IEnumerable GetVisitableSubItems(); } diff --git a/source/Core/CreativeCoders.Core/Visitors/IVisitor.cs b/source/Core/CreativeCoders.Core/Visitors/IVisitor.cs index 7d4a4722..d4b5f9d9 100644 --- a/source/Core/CreativeCoders.Core/Visitors/IVisitor.cs +++ b/source/Core/CreativeCoders.Core/Visitors/IVisitor.cs @@ -1,7 +1,17 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Defines the visitor in the Visitor design pattern, responsible for performing operations +/// on objects without modifying their classes. +/// +/// The type of the concrete visitor. +/// The type of the visitable object. public interface IVisitor where TVisitor : IVisitor where TVisitableObject : IVisitable { + /// + /// Visits the specified visitable object and performs an operation on it. + /// + /// The visitable object to visit. void Visit(TVisitableObject visitableObject); } diff --git a/source/Core/CreativeCoders.Core/Visitors/IVisitorInfo.cs b/source/Core/CreativeCoders.Core/Visitors/IVisitorInfo.cs index 4cbc4760..2e904e4e 100644 --- a/source/Core/CreativeCoders.Core/Visitors/IVisitorInfo.cs +++ b/source/Core/CreativeCoders.Core/Visitors/IVisitorInfo.cs @@ -1,6 +1,14 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Provides configuration metadata for a visitor, controlling its behavior when +/// a visitable object does not support the visitor type. +/// public interface IVisitorInfo { + /// + /// Gets a value indicating whether an is thrown + /// when a visitable object has no matching accept method for this visitor. + /// bool ThrowIfNoAcceptMethod { get; } } diff --git a/source/Core/CreativeCoders.Core/Visitors/ListVisitor.cs b/source/Core/CreativeCoders.Core/Visitors/ListVisitor.cs index bdff0b99..20e55975 100644 --- a/source/Core/CreativeCoders.Core/Visitors/ListVisitor.cs +++ b/source/Core/CreativeCoders.Core/Visitors/ListVisitor.cs @@ -3,15 +3,28 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Applies a visitor to each element in a collection of objects. +/// +/// The type of the visitor to apply. public class ListVisitor { private readonly TVisitor _visitor; + /// + /// Initializes a new instance of the class. + /// + /// The visitor to apply to each element. public ListVisitor(TVisitor visitor) { _visitor = visitor; } + /// + /// Visits each element in the specified collection by passing the visitor to each element's + /// method. + /// + /// The collection of visitable objects to visit. public void Visit(IEnumerable visitables) { visitables.ForEach(visitable => visitable.Accept(_visitor)); diff --git a/source/Core/CreativeCoders.Core/Visitors/Visitable.cs b/source/Core/CreativeCoders.Core/Visitors/Visitable.cs index 8bc58b9e..826a65ab 100644 --- a/source/Core/CreativeCoders.Core/Visitors/Visitable.cs +++ b/source/Core/CreativeCoders.Core/Visitors/Visitable.cs @@ -4,6 +4,12 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Provides an abstract base class for visitable objects in the Visitor design pattern, +/// supporting both strongly-typed and dynamically-registered visitor types. +/// +/// The type of the primary visitor. +/// The type of the concrete visitable object. [PublicAPI] public abstract class Visitable : IVisitable, IVisitable @@ -12,6 +18,11 @@ public abstract class Visitable : IVisitable, { private readonly Dictionary> _visitors = new Dictionary>(); + /// + /// Registers an additional visitor type that this object can accept. + /// + /// The type of the visitor to register. + /// The action to invoke when a visitor of this type is accepted. protected void AddVisitorType(Action action) where T : class { AddVisitorType(typeof(T), param => DoAccept(param, action)); @@ -30,6 +41,7 @@ private static void DoAccept(object param, Action action) where T : class } } + /// public void Accept(object visitor) { if (this is not TVisitableObject self) @@ -54,6 +66,7 @@ public void Accept(object visitor) } } + /// public void Accept(TVisitor visitor) { if (this is not TVisitableObject self) diff --git a/source/Core/CreativeCoders.Core/Visitors/VisitableAction.cs b/source/Core/CreativeCoders.Core/Visitors/VisitableAction.cs index 8e9e02c3..a923c1ed 100644 --- a/source/Core/CreativeCoders.Core/Visitors/VisitableAction.cs +++ b/source/Core/CreativeCoders.Core/Visitors/VisitableAction.cs @@ -2,15 +2,25 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Represents a visitable object that delegates the accept operation to a user-supplied action, +/// allowing inline visitor handling without creating a dedicated visitable class. +/// +/// The type of visitor this action accepts. public class VisitableAction : IVisitable where TVisitor : class { private readonly Action _acceptAction; + /// + /// Initializes a new instance of the class. + /// + /// The action to invoke when a visitor is accepted. public VisitableAction(Action acceptAction) { _acceptAction = acceptAction; } + /// public void Accept(object visitor) { if (visitor is not TVisitor theVisitor) diff --git a/source/Core/CreativeCoders.Core/Visitors/VisitorBase.cs b/source/Core/CreativeCoders.Core/Visitors/VisitorBase.cs index 3687c35a..6a77c288 100644 --- a/source/Core/CreativeCoders.Core/Visitors/VisitorBase.cs +++ b/source/Core/CreativeCoders.Core/Visitors/VisitorBase.cs @@ -3,24 +3,43 @@ namespace CreativeCoders.Core.Visitors; +/// +/// Provides a base implementation for visitors in the Visitor design pattern, with support +/// for recursive sub-item visitation and configurable error behavior. +/// +/// The type of the concrete visitor. +/// The type of the visitable object. [PublicAPI] public abstract class VisitorBase : IVisitorInfo, IVisitor where TVisitor : IVisitor where TVisitableObject : IVisitable { + /// + /// Initializes a new instance of the class. + /// + /// + /// to throw an when a visitable + /// object does not have a matching accept method; otherwise, . + /// protected VisitorBase(bool throwIfNoAcceptMethod) { ThrowIfNoAcceptMethod = throwIfNoAcceptMethod; } + /// public abstract void Visit(TVisitableObject visitableObject); + /// + /// Visits the child items of the specified object if it implements . + /// + /// The object whose child items to visit. protected virtual void VisitSubItems(object visitableObject) { var visitableSubItems = visitableObject as IVisitableSubItems; visitableSubItems?.GetVisitableSubItems()?.ForEach(subItem => subItem.Accept(this)); } + /// public bool ThrowIfNoAcceptMethod { get; } } diff --git a/source/Core/CreativeCoders.Core/Weak/KeepOwnerAliveMode.cs b/source/Core/CreativeCoders.Core/Weak/KeepOwnerAliveMode.cs index c4786d15..f4d6c2db 100644 --- a/source/Core/CreativeCoders.Core/Weak/KeepOwnerAliveMode.cs +++ b/source/Core/CreativeCoders.Core/Weak/KeepOwnerAliveMode.cs @@ -1,8 +1,22 @@ namespace CreativeCoders.Core.Weak; +/// +/// Specifies how the owner of a weak reference wrapper is kept alive. +/// public enum KeepOwnerAliveMode { + /// + /// The owner is always kept alive by maintaining a strong reference. + /// KeepAlive, + + /// + /// The owner is not kept alive; only a weak reference is held. + /// NotKeepAlive, + + /// + /// The keep-alive behavior is determined automatically based on whether the owner is a compiler-generated closure type. + /// AutoGuess } diff --git a/source/Core/CreativeCoders.Core/Weak/NullDisposable.cs b/source/Core/CreativeCoders.Core/Weak/NullDisposable.cs index d3f94329..f8d48039 100644 --- a/source/Core/CreativeCoders.Core/Weak/NullDisposable.cs +++ b/source/Core/CreativeCoders.Core/Weak/NullDisposable.cs @@ -4,9 +4,13 @@ namespace CreativeCoders.Core.Weak; +/// +/// Represents a no-op implementation that performs no action when disposed. +/// [PublicAPI] [ExcludeFromCodeCoverage] public sealed class NullDisposable : IDisposable { + /// public void Dispose() { } } diff --git a/source/Core/CreativeCoders.Core/Weak/WeakAction.cs b/source/Core/CreativeCoders.Core/Weak/WeakAction.cs index 04860d59..81951c2b 100644 --- a/source/Core/CreativeCoders.Core/Weak/WeakAction.cs +++ b/source/Core/CreativeCoders.Core/Weak/WeakAction.cs @@ -4,17 +4,40 @@ namespace CreativeCoders.Core.Weak; +/// +/// Represents a weak reference wrapper around an delegate, preventing the delegate's target from being kept alive solely by the reference. +/// [PublicAPI] public class WeakAction : WeakBase, IExecutable { + /// + /// Initializes a new instance of the class using the action's target as the owner. + /// + /// The action delegate to wrap. public WeakAction(Action action) : this(action?.Target, action) { } + /// + /// Initializes a new instance of the class using the action's target as the owner and the specified keep-alive mode. + /// + /// The action delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakAction(Action action, KeepOwnerAliveMode keepOwnerAliveMode) : this(action?.Target, action, keepOwnerAliveMode) { } + /// + /// Initializes a new instance of the class with an explicit target owner. + /// + /// The object that owns the action. + /// The action delegate to wrap. public WeakAction(object target, Action action) : base(target ?? action?.Target, action, GetAliveMode(action)) { } + /// + /// Initializes a new instance of the class with an explicit target owner and the specified keep-alive mode. + /// + /// The object that owns the action. + /// The action delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakAction(object target, Action action, KeepOwnerAliveMode keepOwnerAliveMode) : base(target ?? action?.Target, action, keepOwnerAliveMode) { } @@ -25,6 +48,7 @@ private static KeepOwnerAliveMode GetAliveMode(Action action) : KeepOwnerAliveMode.AutoGuess; } + /// public void Execute() { var action = GetData(); diff --git a/source/Core/CreativeCoders.Core/Weak/WeakActionGeneric.cs b/source/Core/CreativeCoders.Core/Weak/WeakActionGeneric.cs index 6ff9e0ba..359c6b48 100644 --- a/source/Core/CreativeCoders.Core/Weak/WeakActionGeneric.cs +++ b/source/Core/CreativeCoders.Core/Weak/WeakActionGeneric.cs @@ -4,31 +4,58 @@ namespace CreativeCoders.Core.Weak; +/// +/// Represents a weak reference wrapper around an delegate, preventing the delegate's target from being kept alive solely by the reference. +/// +/// The type of the parameter passed to the action. [PublicAPI] public class WeakAction : WeakBase>, IExecutable, IExecutable, IExecutableWithParameter { + /// + /// Initializes a new instance of the class using the action's target as the owner. + /// + /// The action delegate to wrap. public WeakAction(Action action) : this(action?.Target, action) { } + /// + /// Initializes a new instance of the class using the action's target as the owner and the specified keep-alive mode. + /// + /// The action delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakAction(Action action, KeepOwnerAliveMode keepOwnerAliveMode) : this(action?.Target, action, keepOwnerAliveMode) { } + /// + /// Initializes a new instance of the class with an explicit target owner. + /// + /// The object that owns the action. + /// The action delegate to wrap. public WeakAction(object target, Action action) : base(target ?? action?.Target, action, KeepOwnerAliveMode.NotKeepAlive) { } + /// + /// Initializes a new instance of the class with an explicit target owner and the specified keep-alive mode. + /// + /// The object that owns the action. + /// The action delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakAction(object target, Action action, KeepOwnerAliveMode keepOwnerAliveMode) : base(target ?? action?.Target, action, keepOwnerAliveMode) { } + /// public void Execute() { Execute(default); } + /// public void Execute(T parameter) { var action = GetData(); action?.Invoke(parameter); } + /// public void Execute(object parameter) { Execute((T) parameter); diff --git a/source/Core/CreativeCoders.Core/Weak/WeakBase.cs b/source/Core/CreativeCoders.Core/Weak/WeakBase.cs index 75727797..2fe57718 100644 --- a/source/Core/CreativeCoders.Core/Weak/WeakBase.cs +++ b/source/Core/CreativeCoders.Core/Weak/WeakBase.cs @@ -4,6 +4,10 @@ namespace CreativeCoders.Core.Weak; +/// +/// Provides a base class for weak reference wrappers that hold a weak reference to both an owner and associated data. +/// +/// The type of data held by the weak reference wrapper. [PublicAPI] public class WeakBase : IDisposable where T : class @@ -17,6 +21,12 @@ public class WeakBase : IDisposable [SuppressMessage("csharpsquid", "S4487", Justification = "Needed to hold a strong reference to the data")] private T _data; + /// + /// Initializes a new instance of the class. + /// + /// The object that owns the data. + /// The data to hold a weak reference to. + /// The mode that determines whether the owner is kept alive. public WeakBase(object owner, T data, KeepOwnerAliveMode keepOwnerAliveMode) { Ensure.IsNotNull(data); @@ -53,6 +63,10 @@ private static bool GetKeepAlive(KeepOwnerAliveMode keepOwnerAliveMode, object t }; } + /// + /// Retrieves the data held by this weak reference wrapper. + /// + /// The data if both the owner and data are still alive; otherwise, . public T GetData() { if (_ownerReference == null) @@ -67,8 +81,15 @@ public T GetData() : _dataReference?.GetTarget(); } + /// + /// Gets a value indicating whether the owner is kept alive by a strong reference. + /// public bool KeepOwnerAlive { get; } + /// + /// Determines whether the owner and data references are still alive. + /// + /// if the references are still alive; otherwise, . public bool IsAlive() { return _ownerReference == null @@ -76,11 +97,19 @@ public bool IsAlive() : _ownerReference.IsAlive && _dataReference?.GetIsAlive() == true; } + /// + /// Retrieves the owner target of this weak reference wrapper. + /// + /// The owner object if it is still alive; otherwise, . public object GetTarget() { return _owner ?? _ownerReference?.Target; } + /// + /// Releases the managed resources used by this instance. + /// + /// to release managed resources; if called from a finalizer. [ExcludeFromCodeCoverage] protected virtual void Dispose(bool disposing) { @@ -95,6 +124,7 @@ protected virtual void Dispose(bool disposing) _dataReference = null; } + /// public void Dispose() { Dispose(true); diff --git a/source/Core/CreativeCoders.Core/Weak/WeakFunc.cs b/source/Core/CreativeCoders.Core/Weak/WeakFunc.cs index 00affb54..9f0effe8 100644 --- a/source/Core/CreativeCoders.Core/Weak/WeakFunc.cs +++ b/source/Core/CreativeCoders.Core/Weak/WeakFunc.cs @@ -4,20 +4,45 @@ namespace CreativeCoders.Core.Weak; +/// +/// Represents a weak reference wrapper around a delegate, preventing the delegate's target from being kept alive solely by the reference. +/// +/// The type of the return value of the function. [PublicAPI] public class WeakFunc : WeakBase>, IExecutableWithResult { + /// + /// Initializes a new instance of the class using the function's target as the owner. + /// + /// The function delegate to wrap. public WeakFunc(Func function) : this(function?.Target, function) { } + /// + /// Initializes a new instance of the class using the function's target as the owner and the specified keep-alive mode. + /// + /// The function delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakFunc(Func function, KeepOwnerAliveMode keepOwnerAliveMode) : this(function?.Target, function, keepOwnerAliveMode) { } + /// + /// Initializes a new instance of the class with an explicit target owner. + /// + /// The object that owns the function. + /// The function delegate to wrap. public WeakFunc(object target, Func function) : this(target, function, KeepOwnerAliveMode.NotKeepAlive) { } + /// + /// Initializes a new instance of the class with an explicit target owner and the specified keep-alive mode. + /// + /// The object that owns the function. + /// The function delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakFunc(object target, Func function, KeepOwnerAliveMode keepOwnerAliveMode) : base(target ?? function?.Target, function, keepOwnerAliveMode) { } + /// public T Execute() { var function = GetData(); @@ -25,21 +50,47 @@ public T Execute() } } +/// +/// Represents a weak reference wrapper around a delegate, preventing the delegate's target from being kept alive solely by the reference. +/// +/// The type of the parameter passed to the function. +/// The type of the return value of the function. [PublicAPI] public class WeakFunc : WeakBase>, IExecutableWithResult { + /// + /// Initializes a new instance of the class using the function's target as the owner. + /// + /// The function delegate to wrap. public WeakFunc(Func function) : this(function?.Target, function) { } + /// + /// Initializes a new instance of the class using the function's target as the owner and the specified keep-alive mode. + /// + /// The function delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakFunc(Func function, KeepOwnerAliveMode keepOwnerAliveMode) : this(function?.Target, function, keepOwnerAliveMode) { } + /// + /// Initializes a new instance of the class with an explicit target owner. + /// + /// The object that owns the function. + /// The function delegate to wrap. public WeakFunc(object target, Func function) : base(target ?? function?.Target, function, KeepOwnerAliveMode.NotKeepAlive) { } + /// + /// Initializes a new instance of the class with an explicit target owner and the specified keep-alive mode. + /// + /// The object that owns the function. + /// The function delegate to wrap. + /// The mode that determines whether the owner is kept alive. public WeakFunc(object target, Func function, KeepOwnerAliveMode keepOwnerAliveMode) : base(target ?? function?.Target, function, keepOwnerAliveMode) { } + /// public TResult Execute(TParameter parameter) { var function = GetData(); diff --git a/source/Core/CreativeCoders.Core/Weak/WeakReferenceExtensions.cs b/source/Core/CreativeCoders.Core/Weak/WeakReferenceExtensions.cs index 0dde6723..1b126b74 100644 --- a/source/Core/CreativeCoders.Core/Weak/WeakReferenceExtensions.cs +++ b/source/Core/CreativeCoders.Core/Weak/WeakReferenceExtensions.cs @@ -2,8 +2,17 @@ namespace CreativeCoders.Core.Weak; +/// +/// Provides extension methods for . +/// public static class WeakReferenceExtensions { + /// + /// Retrieves the target object from the weak reference. + /// + /// The type of the referenced object. + /// The weak reference to retrieve the target from. + /// The target object if it is still alive; otherwise, . public static T GetTarget(this WeakReference weakReference) where T : class { @@ -12,6 +21,12 @@ public static T GetTarget(this WeakReference weakReference) : null; } + /// + /// Determines whether the target of the weak reference is still alive. + /// + /// The type of the referenced object. + /// The weak reference to check. + /// if the target is still alive; otherwise, . public static bool GetIsAlive(this WeakReference weakReference) where T : class { diff --git a/source/Data/CreativeCoders.Data.EfCore/EfCoreRepository.cs b/source/Data/CreativeCoders.Data.EfCore/EfCoreRepository.cs index 08d82814..cb8de958 100644 --- a/source/Data/CreativeCoders.Data.EfCore/EfCoreRepository.cs +++ b/source/Data/CreativeCoders.Data.EfCore/EfCoreRepository.cs @@ -14,7 +14,7 @@ public class EfCoreRepository : IRepository { public EfCoreRepository(DbContext dbContext) { - Ensure.IsNotNull(dbContext, nameof(dbContext)); + Ensure.IsNotNull(dbContext); DbSet = dbContext.Set(); } diff --git a/source/Data/CreativeCoders.Data.EfCore/Modeling/EfCoreEntityModelBuilderSource.cs b/source/Data/CreativeCoders.Data.EfCore/Modeling/EfCoreEntityModelBuilderSource.cs index ae40f5bc..b28d17a4 100644 --- a/source/Data/CreativeCoders.Data.EfCore/Modeling/EfCoreEntityModelBuilderSource.cs +++ b/source/Data/CreativeCoders.Data.EfCore/Modeling/EfCoreEntityModelBuilderSource.cs @@ -18,7 +18,7 @@ public class EfCoreEntityModelBuilderSource : IEfCoreEntityModelBuilderSource public EfCoreEntityModelBuilderSource(IServiceProvider serviceProvider) { - _diContainer = Ensure.NotNull(serviceProvider, nameof(serviceProvider)); + _diContainer = Ensure.NotNull(serviceProvider); _builders = new List(); } diff --git a/source/Data/CreativeCoders.Data.Nhibernate/NhibernateRepository.cs b/source/Data/CreativeCoders.Data.Nhibernate/NhibernateRepository.cs index beaa9af6..5c75c831 100644 --- a/source/Data/CreativeCoders.Data.Nhibernate/NhibernateRepository.cs +++ b/source/Data/CreativeCoders.Data.Nhibernate/NhibernateRepository.cs @@ -14,7 +14,7 @@ public class NhibernateRepository : IRepository public NhibernateRepository(ISession session) { - _session = Ensure.NotNull(session, nameof(session)); + _session = Ensure.NotNull(session); } public IIncludableDataQueryable QueryAll() diff --git a/source/Data/CreativeCoders.Data.Nhibernate/NhibernateUnitOfWork.cs b/source/Data/CreativeCoders.Data.Nhibernate/NhibernateUnitOfWork.cs index 9975bc1b..35214ad3 100644 --- a/source/Data/CreativeCoders.Data.Nhibernate/NhibernateUnitOfWork.cs +++ b/source/Data/CreativeCoders.Data.Nhibernate/NhibernateUnitOfWork.cs @@ -15,7 +15,7 @@ public sealed class NhibernateUnitOfWork : UnitOfWorkBase public NhibernateUnitOfWork(ISessionFactory sessionFactory) { - Ensure.IsNotNull(sessionFactory, nameof(sessionFactory)); + Ensure.IsNotNull(sessionFactory); _session = sessionFactory.OpenSession(); _transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted); diff --git a/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilder.cs b/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilder.cs index 1ada192f..4ee3a59f 100644 --- a/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilder.cs +++ b/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilder.cs @@ -10,7 +10,7 @@ public class ProxyBuilder : IProxyBuilder public ProxyBuilder(IProxyGenerator proxyGenerator) { - _proxyGenerator = Ensure.NotNull(proxyGenerator, nameof(proxyGenerator)); + _proxyGenerator = Ensure.NotNull(proxyGenerator); } public T Build(InterceptorBase interceptor) diff --git a/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilderFactory.cs b/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilderFactory.cs index 42bedfd1..fb8b9512 100644 --- a/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilderFactory.cs +++ b/source/DynamicCode/CreativeCoders.DynamicCode.Proxying/ProxyBuilderFactory.cs @@ -11,7 +11,7 @@ public class ProxyBuilderFactory : IProxyBuilderFactory public ProxyBuilderFactory(IServiceProvider serviceProvider) { - _serviceProvider = Ensure.NotNull(serviceProvider, nameof(serviceProvider)); + _serviceProvider = Ensure.NotNull(serviceProvider); } public IProxyBuilder Create() where T : class diff --git a/source/Messaging/CreativeCoders.Messaging.DefaultMediator/AsyncMediatorRegistration.cs b/source/Messaging/CreativeCoders.Messaging.DefaultMediator/AsyncMediatorRegistration.cs index 0d3d763a..5018a48f 100644 --- a/source/Messaging/CreativeCoders.Messaging.DefaultMediator/AsyncMediatorRegistration.cs +++ b/source/Messaging/CreativeCoders.Messaging.DefaultMediator/AsyncMediatorRegistration.cs @@ -4,7 +4,7 @@ namespace CreativeCoders.Messaging.DefaultMediator; -internal class AsyncMediatorRegistration : IMediatorRegistration +internal sealed class AsyncMediatorRegistration : IMediatorRegistration { private readonly WeakFunc _weakAsyncAction; diff --git a/source/Net/CreativeCoders.Net.Avm/FritzBoxFactory.cs b/source/Net/CreativeCoders.Net.Avm/FritzBoxFactory.cs index 97f6257f..efb2779b 100644 --- a/source/Net/CreativeCoders.Net.Avm/FritzBoxFactory.cs +++ b/source/Net/CreativeCoders.Net.Avm/FritzBoxFactory.cs @@ -9,7 +9,7 @@ public class FritzBoxFactory : IFritzBoxFactory public FritzBoxFactory(IHttpClientFactory httpClientFactory) { - _httpClientFactory = Ensure.NotNull(httpClientFactory, nameof(httpClientFactory)); + _httpClientFactory = Ensure.NotNull(httpClientFactory); } public IFritzBox Create(string name) diff --git a/source/Net/CreativeCoders.Net.Avm/FritzBoxServiceCollectionExtensions.cs b/source/Net/CreativeCoders.Net.Avm/FritzBoxServiceCollectionExtensions.cs index f76e3df5..b7a146c0 100644 --- a/source/Net/CreativeCoders.Net.Avm/FritzBoxServiceCollectionExtensions.cs +++ b/source/Net/CreativeCoders.Net.Avm/FritzBoxServiceCollectionExtensions.cs @@ -15,8 +15,8 @@ public static class FritzBoxServiceCollectionExtensions public static void AddFritzBox(this IServiceCollection services, Action configureOptions) { - Ensure.NotNull(services, nameof(services)); - Ensure.NotNull(configureOptions, nameof(configureOptions)); + Ensure.NotNull(services); + Ensure.NotNull(configureOptions); services.Configure(configureOptions); @@ -58,7 +58,7 @@ public static void AddFritzBox(this IServiceCollection services, public static void AddFritzBox(this IServiceCollection services) { - Ensure.NotNull(services, nameof(services)); + Ensure.NotNull(services); services.AddDynamicHttpClient(); diff --git a/source/Net/CreativeCoders.Net.Avm/Hosts/HostsImpl.cs b/source/Net/CreativeCoders.Net.Avm/Hosts/HostsImpl.cs index bede9145..5f3c3756 100644 --- a/source/Net/CreativeCoders.Net.Avm/Hosts/HostsImpl.cs +++ b/source/Net/CreativeCoders.Net.Avm/Hosts/HostsImpl.cs @@ -20,7 +20,7 @@ public HostsImpl(HttpClient httpClient) public async Task GetHostEntryAsync(string macAddress) { - Ensure.IsNotNullOrWhitespace(macAddress, nameof(macAddress)); + Ensure.IsNotNullOrWhitespace(macAddress); var response = await _hostsApi.GetSpecificHostEntryAsync(macAddress) .ConfigureAwait(false); diff --git a/source/Net/CreativeCoders.Net.JsonRpc/ApiBuilder/JsonRpcApiBuilder.cs b/source/Net/CreativeCoders.Net.JsonRpc/ApiBuilder/JsonRpcApiBuilder.cs index f6c33b11..3475ac4a 100644 --- a/source/Net/CreativeCoders.Net.JsonRpc/ApiBuilder/JsonRpcApiBuilder.cs +++ b/source/Net/CreativeCoders.Net.JsonRpc/ApiBuilder/JsonRpcApiBuilder.cs @@ -15,8 +15,8 @@ public class JsonRpcApiBuilder : IJsonRpcApiBuilder public JsonRpcApiBuilder(IProxyBuilder proxyBuilder, IJsonRpcClientFactory jsonRpcClientFactory) { - _proxyBuilder = Ensure.NotNull(proxyBuilder, nameof(proxyBuilder)); - _jsonRpcClientFactory = Ensure.NotNull(jsonRpcClientFactory, nameof(jsonRpcClientFactory)); + _proxyBuilder = Ensure.NotNull(proxyBuilder); + _jsonRpcClientFactory = Ensure.NotNull(jsonRpcClientFactory); } public IJsonRpcApiBuilder ForUrl(Uri url) diff --git a/source/Net/CreativeCoders.Net.Servers.Http.AspNetCore/AspNetCoreWebHost.cs b/source/Net/CreativeCoders.Net.Servers.Http.AspNetCore/AspNetCoreWebHost.cs index 6a605ff0..dc6e6e30 100644 --- a/source/Net/CreativeCoders.Net.Servers.Http.AspNetCore/AspNetCoreWebHost.cs +++ b/source/Net/CreativeCoders.Net.Servers.Http.AspNetCore/AspNetCoreWebHost.cs @@ -21,14 +21,14 @@ public sealed class AspNetCoreWebHost : IDisposable public AspNetCoreWebHost(Func handleRequest) { - Ensure.IsNotNull(handleRequest, nameof(handleRequest)); + Ensure.IsNotNull(handleRequest); _handleRequest = handleRequest; } public Task StartAsync(IWebHostConfig webHostConfig) { - Ensure.IsNotNull(webHostConfig, nameof(webHostConfig)); + Ensure.IsNotNull(webHostConfig); var webHostBuilder = CreateWebHostBuilder(webHostConfig.Urls, webHostConfig.AllowSynchronousIO); if (webHostConfig.DisableLogging) diff --git a/source/Net/CreativeCoders.Net.WebApi/Building/ApiBuilder.cs b/source/Net/CreativeCoders.Net.WebApi/Building/ApiBuilder.cs index c1f0dd42..dbf04e96 100644 --- a/source/Net/CreativeCoders.Net.WebApi/Building/ApiBuilder.cs +++ b/source/Net/CreativeCoders.Net.WebApi/Building/ApiBuilder.cs @@ -14,8 +14,8 @@ internal class ApiBuilder : IApiBuilder public ApiBuilder(IHttpClientFactory httpClientFactory, IProxyBuilderFactory proxyBuilderFactory) { - _httpClientFactory = Ensure.NotNull(httpClientFactory, nameof(httpClientFactory)); - _proxyBuilderFactory = Ensure.NotNull(proxyBuilderFactory, nameof(proxyBuilderFactory)); + _httpClientFactory = Ensure.NotNull(httpClientFactory); + _proxyBuilderFactory = Ensure.NotNull(proxyBuilderFactory); } public T BuildApi(string baseUri, IDataFormatter defaultDataFormatter) diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Client/XmlRpcClient.cs b/source/Net/CreativeCoders.Net.XmlRpc/Client/XmlRpcClient.cs index bd0b3776..1d440d62 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Client/XmlRpcClient.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Client/XmlRpcClient.cs @@ -25,7 +25,7 @@ public class XmlRpcClient : IXmlRpcClient public XmlRpcClient(HttpClient httpClient) { - Ensure.IsNotNull(httpClient, nameof(httpClient)); + Ensure.IsNotNull(httpClient); _httpClient = httpClient; _requestBuilder = new RequestBuilder(new DataToXmlRpcValueConverter()); @@ -33,7 +33,7 @@ public XmlRpcClient(HttpClient httpClient) public async Task SendRequestAsync(XmlRpcRequest request) { - Ensure.IsNotNull(request, nameof(request)); + Ensure.IsNotNull(request); var httpRequest = await CreateHttpRequestAsync(request).ConfigureAwait(false); @@ -97,7 +97,7 @@ public async Task ExecuteAsync(string methodName, params object[] parameters) public async Task InvokeAsync(string methodName, params object[] parameters) { - Ensure.IsNotNullOrWhitespace(methodName, nameof(methodName)); + Ensure.IsNotNullOrWhitespace(methodName); var request = _requestBuilder.Build(methodName, parameters); diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Model/XmlRpcMethodCall.cs b/source/Net/CreativeCoders.Net.XmlRpc/Model/XmlRpcMethodCall.cs index 89bef46f..c2b94244 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Model/XmlRpcMethodCall.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Model/XmlRpcMethodCall.cs @@ -9,7 +9,7 @@ public class XmlRpcMethodCall public XmlRpcMethodCall(string name, params XmlRpcValue[] parameters) { - Ensure.IsNotNullOrWhitespace(name, nameof(name)); + Ensure.IsNotNullOrWhitespace(name); Name = name; diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Proxy/XmlRpcProxyBuilder.cs b/source/Net/CreativeCoders.Net.XmlRpc/Proxy/XmlRpcProxyBuilder.cs index ed41c345..9871dde7 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Proxy/XmlRpcProxyBuilder.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Proxy/XmlRpcProxyBuilder.cs @@ -25,8 +25,8 @@ public class XmlRpcProxyBuilder : IXmlRpcProxyBuilder public XmlRpcProxyBuilder(IProxyBuilder proxyBuilder, IHttpClientFactory httpClientFactory) { - Ensure.IsNotNull(proxyBuilder, nameof(proxyBuilder)); - Ensure.IsNotNull(httpClientFactory, nameof(httpClientFactory)); + Ensure.IsNotNull(proxyBuilder); + Ensure.IsNotNull(httpClientFactory); if (!typeof(T).IsInterface) { diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Reader/ModelReaderBase.cs b/source/Net/CreativeCoders.Net.XmlRpc/Reader/ModelReaderBase.cs index 5af74dd5..f4fcc400 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Reader/ModelReaderBase.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Reader/ModelReaderBase.cs @@ -15,7 +15,7 @@ public abstract class ModelReaderBase protected ModelReaderBase(IValueReaders readers) { - Ensure.IsNotNull(readers, nameof(readers)); + Ensure.IsNotNull(readers); _readers = readers; } diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Reader/RequestModelReader.cs b/source/Net/CreativeCoders.Net.XmlRpc/Reader/RequestModelReader.cs index 99980fb0..715e07ef 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Reader/RequestModelReader.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Reader/RequestModelReader.cs @@ -17,7 +17,7 @@ public RequestModelReader(IValueReaders valueReaders) : base(valueReaders) { } public async Task ReadAsync(Stream inputStream) { - Ensure.IsNotNull(inputStream, nameof(inputStream)); + Ensure.IsNotNull(inputStream); var xmlDoc = await ReadXmlDocAsync(inputStream).ConfigureAwait(false); diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Reader/ResponseModelReader.cs b/source/Net/CreativeCoders.Net.XmlRpc/Reader/ResponseModelReader.cs index 2f145a07..2127a298 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Reader/ResponseModelReader.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Reader/ResponseModelReader.cs @@ -18,7 +18,7 @@ public ResponseModelReader(IValueReaders readers) : base(readers) { } public async Task ReadAsync(Stream inputStream, bool isMultiCallResponse) { - Ensure.IsNotNull(inputStream, nameof(inputStream)); + Ensure.IsNotNull(inputStream); var xmlDoc = await ReadXmlDocAsync(inputStream).ConfigureAwait(false); diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcMethodExecutor.cs b/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcMethodExecutor.cs index 192a3db1..b70c23ff 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcMethodExecutor.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcMethodExecutor.cs @@ -19,7 +19,7 @@ public class XmlRpcMethodExecutor public XmlRpcMethodExecutor(IXmlRpcServerMethods xmlRpcServerMethods) { - Ensure.IsNotNull(xmlRpcServerMethods, nameof(xmlRpcServerMethods)); + Ensure.IsNotNull(xmlRpcServerMethods); _xmlRpcServerMethods = xmlRpcServerMethods; _xmlRpcServerMethods.RegisterMethods("system", this); @@ -27,7 +27,7 @@ public XmlRpcMethodExecutor(IXmlRpcServerMethods xmlRpcServerMethods) public async Task Invoke(XmlRpcMethodCall methodCall) { - Ensure.IsNotNull(methodCall, nameof(methodCall)); + Ensure.IsNotNull(methodCall); var methodRegistration = _xmlRpcServerMethods.GetMethod(methodCall.Name); diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServer.cs b/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServer.cs index 878c6cc6..b11cab2d 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServer.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServer.cs @@ -33,7 +33,7 @@ public sealed class XmlRpcServer : IXmlRpcServer, IHttpRequestHandler public XmlRpcServer(IHttpServer httpServer, bool disposeHttpServer) { - _httpServer = Ensure.NotNull(httpServer, nameof(httpServer)); + _httpServer = Ensure.NotNull(httpServer); _disposeHttpServer = disposeHttpServer; Encoding = Encoding.UTF8; @@ -71,8 +71,8 @@ public Task StopAsync() public async Task ProcessAsync(IHttpRequest request, IHttpResponse response) { - Ensure.IsNotNull(request, nameof(request)); - Ensure.IsNotNull(response, nameof(response)); + Ensure.IsNotNull(request); + Ensure.IsNotNull(response); if (!request.HttpMethod.Equals(HttpMethod.Post.Method, StringComparison.InvariantCultureIgnoreCase)) { diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServerMethods.cs b/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServerMethods.cs index f1ab958e..604746fb 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServerMethods.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Server/XmlRpcServerMethods.cs @@ -35,7 +35,7 @@ private void RegisterMethod(XmlRpcMethodAttribute methodAttribute, MethodInfo public void RegisterMethods(string methodSuffix, T methodsInterface) where T : class { - Ensure.IsNotNull(methodsInterface, nameof(methodsInterface)); + Ensure.IsNotNull(methodsInterface); var methods = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); diff --git a/source/Net/CreativeCoders.Net.XmlRpc/Writer/ModelWriterBase.cs b/source/Net/CreativeCoders.Net.XmlRpc/Writer/ModelWriterBase.cs index 8359a774..4d76440f 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/Writer/ModelWriterBase.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/Writer/ModelWriterBase.cs @@ -11,16 +11,16 @@ public abstract class ModelWriterBase { protected ModelWriterBase(IValueWriters writers) { - Ensure.IsNotNull(writers, nameof(writers)); + Ensure.IsNotNull(writers); Writers = writers; } public async Task WriteAsync(Stream outputStream, T data, Encoding encoding) { - Ensure.IsNotNull(outputStream, nameof(outputStream)); - Ensure.IsNotNull(data, nameof(data)); - Ensure.IsNotNull(encoding, nameof(encoding)); + Ensure.IsNotNull(outputStream); + Ensure.IsNotNull(data); + Ensure.IsNotNull(encoding); var xmlDoc = CreateXml(data, encoding); diff --git a/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcResponse.cs b/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcResponse.cs index 25b0fab3..9eb62ec8 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcResponse.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcResponse.cs @@ -10,7 +10,7 @@ public class XmlRpcResponse public XmlRpcResponse(IEnumerable results, bool isMultiCall) { - Ensure.IsNotNull(results, nameof(results)); + Ensure.IsNotNull(results); _results = new List(results); IsMultiCall = isMultiCall; diff --git a/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcServerFactory.cs b/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcServerFactory.cs index 82728dcf..970011c7 100644 --- a/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcServerFactory.cs +++ b/source/Net/CreativeCoders.Net.XmlRpc/XmlRpcServerFactory.cs @@ -12,7 +12,7 @@ public class XmlRpcServerFactory : IXmlRpcServerFactory public XmlRpcServerFactory(IServiceProvider serviceProvider) { - _serviceProvider = Ensure.NotNull(serviceProvider, nameof(serviceProvider)); + _serviceProvider = Ensure.NotNull(serviceProvider); } public IXmlRpcServer CreateServer() diff --git a/source/Net/CreativeCoders.Net/Http/Auth/AuthenticationHttpClientFactory.cs b/source/Net/CreativeCoders.Net/Http/Auth/AuthenticationHttpClientFactory.cs index 4da3841f..3b8c76ff 100644 --- a/source/Net/CreativeCoders.Net/Http/Auth/AuthenticationHttpClientFactory.cs +++ b/source/Net/CreativeCoders.Net/Http/Auth/AuthenticationHttpClientFactory.cs @@ -10,7 +10,7 @@ public class AuthenticationHttpClientFactory : IAuthenticationHttpClientFactory public AuthenticationHttpClientFactory(IHttpMessageHandlerFactory httpMessageHandlerFactory) { - Ensure.IsNotNull(httpMessageHandlerFactory, nameof(httpMessageHandlerFactory)); + Ensure.IsNotNull(httpMessageHandlerFactory); _httpMessageHandlerFactory = httpMessageHandlerFactory; } diff --git a/source/Net/CreativeCoders.Net/Http/Auth/Jwt/JwtHttpClientAuthenticator.cs b/source/Net/CreativeCoders.Net/Http/Auth/Jwt/JwtHttpClientAuthenticator.cs index 2b964f2b..0521423d 100644 --- a/source/Net/CreativeCoders.Net/Http/Auth/Jwt/JwtHttpClientAuthenticator.cs +++ b/source/Net/CreativeCoders.Net/Http/Auth/Jwt/JwtHttpClientAuthenticator.cs @@ -17,7 +17,7 @@ public class JwtHttpClientAuthenticator : IJwtHttpClientAuthenticator public JwtHttpClientAuthenticator(IJwtClient jwtClient) { - Ensure.IsNotNull(jwtClient, nameof(jwtClient)); + Ensure.IsNotNull(jwtClient); _jwtClient = jwtClient; } diff --git a/source/Net/CreativeCoders.Net/Http/DelegateHttpClientFactory.cs b/source/Net/CreativeCoders.Net/Http/DelegateHttpClientFactory.cs index 1615cb24..c6e968b4 100644 --- a/source/Net/CreativeCoders.Net/Http/DelegateHttpClientFactory.cs +++ b/source/Net/CreativeCoders.Net/Http/DelegateHttpClientFactory.cs @@ -26,7 +26,7 @@ public class DelegateHttpClientFactory : IHttpClientFactory ///------------------------------------------------------------------------------------------------- public DelegateHttpClientFactory(Func createClient) { - Ensure.IsNotNull(createClient, nameof(createClient)); + Ensure.IsNotNull(createClient); _createClient = createClient; } diff --git a/source/Net/CreativeCoders.Net/Http/DelegateHttpMessageHandler.cs b/source/Net/CreativeCoders.Net/Http/DelegateHttpMessageHandler.cs index a290a5d7..201a90b9 100644 --- a/source/Net/CreativeCoders.Net/Http/DelegateHttpMessageHandler.cs +++ b/source/Net/CreativeCoders.Net/Http/DelegateHttpMessageHandler.cs @@ -28,7 +28,7 @@ public class DelegateHttpMessageHandler : HttpMessageHandler public DelegateHttpMessageHandler( Func> sendAsync) { - Ensure.IsNotNull(sendAsync, nameof(sendAsync)); + Ensure.IsNotNull(sendAsync); _sendAsync = sendAsync; } @@ -43,7 +43,7 @@ public DelegateHttpMessageHandler( ///------------------------------------------------------------------------------------------------- public DelegateHttpMessageHandler(Func> sendAsync) { - Ensure.IsNotNull(sendAsync, nameof(sendAsync)); + Ensure.IsNotNull(sendAsync); _sendAsync = (request, _) => sendAsync(request); } diff --git a/source/Net/CreativeCoders.Net/Http/DynamicHttpClientFactoryOptions.cs b/source/Net/CreativeCoders.Net/Http/DynamicHttpClientFactoryOptions.cs index f759ffc5..c0b4f4c0 100644 --- a/source/Net/CreativeCoders.Net/Http/DynamicHttpClientFactoryOptions.cs +++ b/source/Net/CreativeCoders.Net/Http/DynamicHttpClientFactoryOptions.cs @@ -11,7 +11,7 @@ public class DynamicHttpClientFactoryOptions : IConfigureNamedOptions configure) { - Ensure.NotNull(name, nameof(name)); - Ensure.NotNull(configure, nameof(configure)); + Ensure.NotNull(name); + Ensure.NotNull(configure); _options[name] = configure; } public IHttpClientSetup Add(string name) { - Ensure.NotNull(name, nameof(name)); + Ensure.NotNull(name); var clientSetup = new HttpClientSetup(); @@ -33,7 +33,7 @@ public IHttpClientSetup Add(string name) public Action? Get(string name) { - Ensure.NotNull(name, nameof(name)); + Ensure.NotNull(name); return _options.GetValueOrDefault(name); } diff --git a/source/Net/CreativeCoders.Net/Http/HttpClientSetup.cs b/source/Net/CreativeCoders.Net/Http/HttpClientSetup.cs index a1eb54ac..73d28447 100644 --- a/source/Net/CreativeCoders.Net/Http/HttpClientSetup.cs +++ b/source/Net/CreativeCoders.Net/Http/HttpClientSetup.cs @@ -18,7 +18,7 @@ public void InvokeSetup(HttpClientFactoryOptions options) public IHttpClientSetup ConfigureClient(Action configureClient) { - Ensure.NotNull(configureClient, nameof(configureClient)); + Ensure.NotNull(configureClient); _configureActions.Add(options => options.HttpClientActions.Add(configureClient)); @@ -27,7 +27,7 @@ public IHttpClientSetup ConfigureClient(Action configureClient) public IHttpClientSetup ConfigureClientHandler(Func configureClientHandler) { - Ensure.NotNull(configureClientHandler, nameof(configureClientHandler)); + Ensure.NotNull(configureClientHandler); _configureActions.Add(options => options.HttpMessageHandlerBuilderActions.Add(builder => diff --git a/source/Net/CreativeCoders.Net/NetworkInfo.cs b/source/Net/CreativeCoders.Net/NetworkInfo.cs index a9bc1b59..7587773b 100644 --- a/source/Net/CreativeCoders.Net/NetworkInfo.cs +++ b/source/Net/CreativeCoders.Net/NetworkInfo.cs @@ -15,7 +15,7 @@ public class NetworkInfo : INetworkInfo public int FindFreePort(IEnumerable portRange) { var ports = portRange?.ToArray(); - Ensure.IsNotNullOrEmpty(ports, nameof(ports)); + Ensure.IsNotNullOrEmpty(ports); var ipProperties = IPGlobalProperties.GetIPGlobalProperties(); var ipEndpoints = ipProperties.GetActiveTcpListeners().Concat(ipProperties.GetActiveUdpListeners()) diff --git a/source/Net/CreativeCoders.Net/Soap/Request/RequestXmlWriter.cs b/source/Net/CreativeCoders.Net/Soap/Request/RequestXmlWriter.cs index e1777157..88b1c48c 100644 --- a/source/Net/CreativeCoders.Net/Soap/Request/RequestXmlWriter.cs +++ b/source/Net/CreativeCoders.Net/Soap/Request/RequestXmlWriter.cs @@ -15,8 +15,8 @@ internal class RequestXmlWriter public RequestXmlWriter(StreamWriter streamWriter, SoapRequestInfo soapRequestInfo) { - Ensure.IsNotNull(streamWriter, nameof(streamWriter)); - Ensure.IsNotNull(soapRequestInfo, nameof(soapRequestInfo)); + Ensure.IsNotNull(streamWriter); + Ensure.IsNotNull(soapRequestInfo); _streamWriter = streamWriter; _soapRequestInfo = soapRequestInfo; diff --git a/source/Net/CreativeCoders.Net/Soap/Response/SoapResponder.cs b/source/Net/CreativeCoders.Net/Soap/Response/SoapResponder.cs index 518bd00e..b2384041 100644 --- a/source/Net/CreativeCoders.Net/Soap/Response/SoapResponder.cs +++ b/source/Net/CreativeCoders.Net/Soap/Response/SoapResponder.cs @@ -16,8 +16,8 @@ namespace CreativeCoders.Net.Soap.Response; public SoapResponder(Stream responseStream, SoapResponseInfo responseInfo) { - Ensure.IsNotNull(responseStream, nameof(responseStream)); - Ensure.IsNotNull(responseInfo, nameof(responseInfo)); + Ensure.IsNotNull(responseStream); + Ensure.IsNotNull(responseInfo); _responseStream = responseStream; _responseInfo = responseInfo; diff --git a/source/Net/CreativeCoders.Net/Soap/SoapHttpClient.cs b/source/Net/CreativeCoders.Net/Soap/SoapHttpClient.cs index af3b1621..302ba213 100644 --- a/source/Net/CreativeCoders.Net/Soap/SoapHttpClient.cs +++ b/source/Net/CreativeCoders.Net/Soap/SoapHttpClient.cs @@ -24,13 +24,13 @@ public class SoapHttpClient : ISoapHttpClient public SoapHttpClient(HttpClient httpClient) { - _httpClient = Ensure.NotNull(httpClient, nameof(httpClient)); + _httpClient = Ensure.NotNull(httpClient); } public async Task InvokeAsync(Uri uri, TRequest actionRequest) where TResponse : class, new() { - Ensure.IsNotNull(actionRequest, nameof(actionRequest)); + Ensure.IsNotNull(actionRequest); var soapRequestInfo = CreateRequestInfo(actionRequest, uri); diff --git a/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopic.cs b/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopic.cs index 4d5cdc43..80630439 100644 --- a/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopic.cs +++ b/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopic.cs @@ -19,7 +19,7 @@ public MessageTopic() public void Publish(TMessage message) { - Ensure.IsNotNull(message, nameof(message)); + Ensure.IsNotNull(message); _messageSubject.OnNext(message); } @@ -36,7 +36,7 @@ public IObservable Register() public IObservable Register(IScheduler scheduler) { - Ensure.IsNotNull(scheduler, nameof(scheduler)); + Ensure.IsNotNull(scheduler); return Register().ObserveOn(scheduler); } diff --git a/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopicGeneric.cs b/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopicGeneric.cs index f91d316c..4f34d103 100644 --- a/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopicGeneric.cs +++ b/source/Reactive/CreativeCoders.Reactive.Messaging/MessageTopicGeneric.cs @@ -19,7 +19,7 @@ public MessageTopic() public void Publish(TMessage message) { - Ensure.IsNotNull(message, nameof(message)); + Ensure.IsNotNull(message); _messageSubject.OnNext(message); } @@ -45,7 +45,7 @@ public IObservable Register() public IObservable Register(IScheduler scheduler) { - Ensure.IsNotNull(scheduler, nameof(scheduler)); + Ensure.IsNotNull(scheduler); return Register().ObserveOn(scheduler); } @@ -53,7 +53,7 @@ public IObservable Register(IScheduler scheduler) public IObservable Register(IScheduler scheduler) where TRegisterMessage : TMessage { - Ensure.IsNotNull(scheduler, nameof(scheduler)); + Ensure.IsNotNull(scheduler); return Register().ObserveOn(scheduler); } diff --git a/source/Scripting/CreativeCoders.Scripting.Base/SourceCode/DelegateSourceCode.cs b/source/Scripting/CreativeCoders.Scripting.Base/SourceCode/DelegateSourceCode.cs index a8c1c5de..a46adde8 100644 --- a/source/Scripting/CreativeCoders.Scripting.Base/SourceCode/DelegateSourceCode.cs +++ b/source/Scripting/CreativeCoders.Scripting.Base/SourceCode/DelegateSourceCode.cs @@ -11,7 +11,7 @@ public class DelegateSourceCode : ISourceCode public DelegateSourceCode(Func getSourceCode) { - Ensure.IsNotNull(getSourceCode, nameof(getSourceCode)); + Ensure.IsNotNull(getSourceCode); _getSourceCode = getSourceCode; } diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassInjections.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassInjections.cs index 1e8033c5..171c6808 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassInjections.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassInjections.cs @@ -13,7 +13,7 @@ public class ScriptClassInjections public ScriptClassInjections(ScriptClassTemplate template) { - Ensure.IsNotNull(template, nameof(template)); + Ensure.IsNotNull(template); _template = template; _injections = new List(); @@ -21,8 +21,8 @@ public ScriptClassInjections(ScriptClassTemplate template) public void AddProperty(string propertyName, Func getInjectionData) { - Ensure.IsNotNullOrWhitespace(propertyName, nameof(propertyName)); - Ensure.IsNotNull(getInjectionData, nameof(getInjectionData)); + Ensure.IsNotNullOrWhitespace(propertyName); + Ensure.IsNotNull(getInjectionData); var propertyInjection = new PropertyInjection(propertyName, getInjectionData); _injections.Add(propertyInjection); @@ -31,7 +31,7 @@ public void AddProperty(string propertyName, Func getInjectionData) public void SetupScriptObject(object scriptObject) { - Ensure.IsNotNull(scriptObject, nameof(scriptObject)); + Ensure.IsNotNull(scriptObject); _injections.ForEach(injection => injection.Inject(scriptObject)); } diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMember.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMember.cs index 26504141..1206080f 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMember.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMember.cs @@ -8,7 +8,7 @@ public abstract class ScriptClassMember { protected ScriptClassMember(ScriptClassMemberType memberType, string name) { - Ensure.IsNotNullOrWhitespace(name, nameof(name)); + Ensure.IsNotNullOrWhitespace(name); MemberType = memberType; Name = name; diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMembers.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMembers.cs index 5a3dc8fc..ba9117b0 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMembers.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassMembers.cs @@ -12,8 +12,8 @@ public class ScriptClassMembers : IEnumerable public ScriptClassMethod AddMethod(string methodName, string methodSourceCode) { - Ensure.IsNotNullOrWhitespace(methodName, nameof(methodName)); - Ensure.IsNotNull(methodSourceCode, nameof(methodSourceCode)); + Ensure.IsNotNullOrWhitespace(methodName); + Ensure.IsNotNull(methodSourceCode); var method = new ScriptClassMethod(methodName, methodSourceCode); _members.Add(method); @@ -33,8 +33,8 @@ public ScriptClassProperty AddProperty(string propertyName, string valueType, st public ScriptClassProperty AddProperty(string propertyName, string valueType, string getterSourceCode, string setterSourceCode) { - Ensure.IsNotNullOrWhitespace(propertyName, nameof(propertyName)); - Ensure.IsNotNullOrWhitespace(valueType, nameof(valueType)); + Ensure.IsNotNullOrWhitespace(propertyName); + Ensure.IsNotNullOrWhitespace(valueType); var property = new ScriptClassProperty(propertyName, valueType, getterSourceCode, setterSourceCode); _members.Add(property); diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassProperty.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassProperty.cs index 2d37e0cf..e77bcce1 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassProperty.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/ClassTemplating/ScriptClassProperty.cs @@ -8,7 +8,7 @@ internal ScriptClassProperty(string name, string valueType, string getterSourceC string setterSourceCode) : base( ScriptClassMemberType.Property, name) { - Ensure.IsNotNullOrWhitespace(valueType, nameof(valueType)); + Ensure.IsNotNullOrWhitespace(valueType); ValueType = valueType; GetterSourceCode = getterSourceCode; diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTree.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTree.cs index ed59fff5..dfc5949d 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTree.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTree.cs @@ -9,7 +9,7 @@ public class ClassSyntaxTree { public string Emit(string sourceCode) { - Ensure.IsNotNull(sourceCode, nameof(sourceCode)); + Ensure.IsNotNull(sourceCode); var sb = new StringBuilder(); diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeBuilder.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeBuilder.cs index d607e36b..f66f55ac 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeBuilder.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeBuilder.cs @@ -22,10 +22,10 @@ public class ClassSyntaxTreeBuilder public ClassSyntaxTreeBuilder(ScriptClassTemplate template, string nameSpace, string className, IEnumerable scriptUsings) { - Ensure.IsNotNull(template, nameof(template)); - Ensure.IsNotNullOrWhitespace(nameSpace, nameof(nameSpace)); - Ensure.IsNotNullOrWhitespace(className, nameof(className)); - Ensure.IsNotNull(scriptUsings, nameof(scriptUsings)); + Ensure.IsNotNull(template); + Ensure.IsNotNullOrWhitespace(nameSpace); + Ensure.IsNotNullOrWhitespace(className); + Ensure.IsNotNull(scriptUsings); _template = template; _nameSpace = nameSpace; diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeNode.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeNode.cs index 5e4d5a59..01658143 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeNode.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ClassSyntaxTreeNode.cs @@ -13,7 +13,7 @@ public class ClassSyntaxTreeNode : IVisitableSubItems public void AddSubNode(ClassSyntaxTreeNode subNode) { - Ensure.IsNotNull(subNode, nameof(subNode)); + Ensure.IsNotNull(subNode); _subNodes.Add(subNode); } diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ScriptClassSourceGenerator.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ScriptClassSourceGenerator.cs index e20aabbc..bdd8a921 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ScriptClassSourceGenerator.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/ScriptClassSourceGenerator.cs @@ -12,8 +12,8 @@ public class ScriptClassSourceGenerator public ScriptClassSourceGenerator(ScriptClassTemplate template, CSharpScriptClassDefinition scriptClassDefinition) { - Ensure.IsNotNull(template, nameof(template)); - Ensure.IsNotNull(scriptClassDefinition, nameof(scriptClassDefinition)); + Ensure.IsNotNull(template); + Ensure.IsNotNull(scriptClassDefinition); _template = template; _scriptClassDefinition = scriptClassDefinition; diff --git a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/SyntaxSourceCodeEmitVisitor.cs b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/SyntaxSourceCodeEmitVisitor.cs index fc6f056b..46efae4c 100644 --- a/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/SyntaxSourceCodeEmitVisitor.cs +++ b/source/Scripting/CreativeCoders.Scripting.CSharp/SourceCodeGenerator/SyntaxSourceCodeEmitVisitor.cs @@ -25,8 +25,8 @@ public class SyntaxSourceCodeEmitVisitor : VisitorBase ExecuteAsync(string[] args) diff --git a/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/ConsoleAppBuilderExtensions.cs b/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/ConsoleAppBuilderExtensions.cs index a2b63d39..2c8ffbc7 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/ConsoleAppBuilderExtensions.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/ConsoleAppBuilderExtensions.cs @@ -52,7 +52,7 @@ public static ConsoleAppBuilder UseActions(this ConsoleAppBuilder c Action setupRuntime) where TCliStartup : class, ICliStartup, new() { - Ensure.NotNull(setupRuntime, nameof(setupRuntime)); + Ensure.NotNull(setupRuntime); return consoleAppBuilder .UseStartup() @@ -86,7 +86,7 @@ public static ConsoleAppBuilder UseSimpleActions(this ConsoleAppBuilde Action setupRuntime) where TStartup : class, IStartup, new() { - Ensure.NotNull(setupRuntime, nameof(setupRuntime)); + Ensure.NotNull(setupRuntime); return consoleAppBuilder .UseStartup() @@ -114,7 +114,7 @@ public static ConsoleAppBuilder UseSimpleActions(this ConsoleAppBuilde public static ConsoleAppBuilder UseActions(this ConsoleAppBuilder consoleAppBuilder, Action setupRuntime) { - Ensure.NotNull(setupRuntime, nameof(setupRuntime)); + Ensure.NotNull(setupRuntime); return consoleAppBuilder .ConfigureServices(services => services.AddCliActions()) diff --git a/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/Runtime/Middleware/ExceptionHandlerMiddleware.cs b/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/Runtime/Middleware/ExceptionHandlerMiddleware.cs index 29e3c08d..a097a8f7 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/Runtime/Middleware/ExceptionHandlerMiddleware.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.Cli.Actions/Runtime/Middleware/ExceptionHandlerMiddleware.cs @@ -18,7 +18,7 @@ public ExceptionHandlerMiddleware(Func next, Action exceptionHandler, int errorReturnCode) : base(next) { _errorReturnCode = errorReturnCode; - _exceptionHandler = Ensure.NotNull(exceptionHandler, nameof(exceptionHandler)); + _exceptionHandler = Ensure.NotNull(exceptionHandler); } public override async Task InvokeAsync(CliActionContext context) diff --git a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Building/CliBuilderExtensions.cs b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Building/CliBuilderExtensions.cs index f82aaa34..ccd95dec 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Building/CliBuilderExtensions.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Building/CliBuilderExtensions.cs @@ -16,8 +16,8 @@ public static ICliBuilder AddCommand(this ICliBuilder cliBuilder, string name, Func> executeAsync) where TOptions : class, new() { - Ensure.IsNotNullOrWhitespace(name, nameof(name)); - Ensure.NotNull(executeAsync, nameof(executeAsync)); + Ensure.IsNotNullOrWhitespace(name); + Ensure.NotNull(executeAsync); cliBuilder.AddCommand, TOptions>(x => { @@ -30,7 +30,7 @@ public static ICliBuilder AddCommand(this ICliBuilder cliBuilder, public static ICliBuilder AddModule(this ICliBuilder cliBuilder, ICliModule cliModule) { - Ensure.NotNull(cliModule, nameof(cliModule)); + Ensure.NotNull(cliModule); cliModule.Configure(cliBuilder); @@ -39,7 +39,7 @@ public static ICliBuilder AddModule(this ICliBuilder cliBuilder, ICliModule cliM public static ICliBuilder AddModule(this ICliBuilder cliBuilder, Type cliModuleType) { - Ensure.NotNull(cliModuleType, nameof(cliModuleType)); + Ensure.NotNull(cliModuleType); if (Activator.CreateInstance(cliModuleType) is ICliModule cliModule) { @@ -57,7 +57,7 @@ public static ICliBuilder AddModule(this ICliBuilder cliBuilder) public static ICliBuilder AddModules(this ICliBuilder cliBuilder, Assembly assembly) { - Ensure.NotNull(assembly, nameof(assembly)); + Ensure.NotNull(assembly); var cliModuleTypes = assembly .ExportedTypes diff --git a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/CliBuilderExecutor.cs b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/CliBuilderExecutor.cs index 75efdcd1..51553bc9 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/CliBuilderExecutor.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/CliBuilderExecutor.cs @@ -14,8 +14,8 @@ public class CliBuilderExecutor : IConsoleAppExecutor public CliBuilderExecutor(Action setupCliBuilder, IServiceProvider serviceProvider) { - _setupCliBuilder = Ensure.NotNull(setupCliBuilder, nameof(setupCliBuilder)); - _serviceProvider = Ensure.NotNull(serviceProvider, nameof(serviceProvider)); + _setupCliBuilder = Ensure.NotNull(setupCliBuilder); + _serviceProvider = Ensure.NotNull(serviceProvider); } public async Task ExecuteAsync(string[] args) diff --git a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/DefaultCliExecutor.cs b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/DefaultCliExecutor.cs index 0c39ca4d..3f1598ea 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/DefaultCliExecutor.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/DefaultCliExecutor.cs @@ -12,12 +12,12 @@ public class DefaultCliExecutor : ICliExecutor public DefaultCliExecutor(ExecutionContext context) { - _context = Ensure.NotNull(context, nameof(context)); + _context = Ensure.NotNull(context); } public async Task ExecuteAsync(string[] args) { - Ensure.NotNull(args, nameof(args)); + Ensure.NotNull(args); var (groupCommandIsExecuted, groupCommandResult) = await TryExecuteGroupCommandAsync(args).ConfigureAwait(false); diff --git a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/ExecutionContext.cs b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/ExecutionContext.cs index dfc9b3e8..3b950665 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/ExecutionContext.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.CliArguments/Execution/ExecutionContext.cs @@ -10,8 +10,8 @@ public ExecutionContext(IEnumerable commandGroups, IEnumerable commands, ICliCommand? defaultCommand, int defaultErrorReturnCode) { - CommandGroups = Ensure.NotNull(commandGroups, nameof(commandGroups)); - Commands = Ensure.NotNull(commands, nameof(commands)); + CommandGroups = Ensure.NotNull(commandGroups); + Commands = Ensure.NotNull(commands); DefaultCommand = defaultCommand; DefaultErrorReturnCode = defaultErrorReturnCode; } diff --git a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentNotNullTests.cs b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentNotNullTests.cs index 7b4c9630..76a5aa88 100644 --- a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentNotNullTests.cs +++ b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentNotNullTests.cs @@ -17,7 +17,7 @@ public class ArgumentNotNullTests public void ArgumentNotNull_DifferentValues_ValueAndNameAndHasValueAreCorrect(string? textValue) { // Act - var argument = Ensure.Argument(textValue, nameof(textValue)).NotNull(); + var argument = Ensure.Argument(textValue).NotNull(); // Assert argument.Name @@ -50,7 +50,7 @@ public void Cast_StringValueToEnumerableOfChar_EnumerableForStringIsReturned() { const string text = "1234"; - var argument = Ensure.Argument(text, nameof(text)).NotNull(); + var argument = Ensure.Argument(text).NotNull(); // Act var value = argument.Cast>(); @@ -70,7 +70,7 @@ public void ImplicitCast_StringValueToEnumerableOfChar_EnumerableForStringIsRetu { const string text = "1234"; - var argument = Ensure.Argument(text, nameof(text)).NotNull(); + var argument = Ensure.Argument(text).NotNull(); // Act string value = argument; diff --git a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentTests.cs b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentTests.cs index af01863a..2ba68772 100644 --- a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentTests.cs +++ b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/ArgumentTests.cs @@ -15,7 +15,7 @@ public void Cast_StringValueToEnumerableOfChar_EnumerableForStringIsReturned() { const string text = "1234"; - var argument = Ensure.Argument(text, nameof(text)); + var argument = Ensure.Argument(text); // Act var value = argument.Cast>(); @@ -35,7 +35,7 @@ public void ImplicitCast_StringValueToEnumerableOfChar_EnumerableForStringIsRetu { const string text = "1234"; - var argument = Ensure.Argument(text, nameof(text)); + var argument = Ensure.Argument(text); // Act string? value = argument; diff --git a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/EnumerableArgumentExtensionsTests.cs b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/EnumerableArgumentExtensionsTests.cs index 8408a520..9a31229e 100644 --- a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/EnumerableArgumentExtensionsTests.cs +++ b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/EnumerableArgumentExtensionsTests.cs @@ -15,7 +15,7 @@ public void NotNullOrEmpty_ArrayContainingItems_ReturnsArgument() var items = new[] {1, 2, 3}; // Act - var argument = Ensure.Argument(items, nameof(items)).NotNullOrEmpty(); + var argument = Ensure.Argument(items).NotNullOrEmpty(); // Assert argument.Value @@ -31,7 +31,7 @@ public void NotNullOrEmpty_EnumerableContainingItems_ReturnsArgument() var items = new[] {1, 2, 3}.Where(_ => true); // Act - var argument = Ensure.Argument(items, nameof(items)).NotNullOrEmpty(); + var argument = Ensure.Argument(items).NotNullOrEmpty(); // Assert argument.Value @@ -45,7 +45,7 @@ public void NotNullOrEmpty_ArrayEmpty_ThrowsException() var items = Array.Empty(); // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNullOrEmpty(); + Action act = () => Ensure.Argument(items).NotNullOrEmpty(); // Assert act @@ -59,7 +59,7 @@ public void NotNullOrEmpty_ArrayNull_ThrowsException() int[]? items = null; // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNullOrEmpty(); + Action act = () => Ensure.Argument(items).NotNullOrEmpty(); // Assert act @@ -73,7 +73,7 @@ public void NotEmpty_ArrayContainingItems_ReturnsArgument() var items = new[] {1, 2, 3}; // Act - var argument = Ensure.Argument(items, nameof(items)).NotNull().NotEmpty(); + var argument = Ensure.Argument(items).NotNull().NotEmpty(); // Assert argument.Value @@ -89,7 +89,7 @@ public void NotEmpty_EnumerableContainingItems_ReturnsArgument() var items = new[] {1, 2, 3}.Where(_ => true); // Act - var argument = Ensure.Argument(items, nameof(items)).NotNull().NotEmpty(); + var argument = Ensure.Argument(items).NotNull().NotEmpty(); // Assert argument.Value @@ -103,7 +103,7 @@ public void NotEmpty_ArrayEmpty_ThrowsException() var items = Array.Empty(); // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNull().NotEmpty(); + Action act = () => Ensure.Argument(items).NotNull().NotEmpty(); // Assert act @@ -120,7 +120,7 @@ public void MinCount_ItemsCountMeetsMinCount_ReturnsArgumentNotNullWithItems(int int minCount) { // Act - var argument = Ensure.Argument(items, nameof(items)).NotNull().MinCount(minCount); + var argument = Ensure.Argument(items).NotNull().MinCount(minCount); // Assert argument.Value @@ -136,7 +136,7 @@ public void MinCount_ItemsCountMeetsMinCount_ReturnsArgumentNotNullWithItems(int public void MinCount_ItemsCountNotMeetsMinCount_ThrowsException(int[] items, int minCount) { // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNull().MinCount(minCount); + Action act = () => Ensure.Argument(items).NotNull().MinCount(minCount); // Assert act @@ -155,7 +155,7 @@ public void MinCount_ItemsCountNotMeetsMinCountWithMessage_ThrowsExceptionWithMe const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNull().MinCount(minCount, message); + Action act = () => Ensure.Argument(items).NotNull().MinCount(minCount, message); // Assert act @@ -173,7 +173,7 @@ public void MaxCount_ItemsCountMeetsMaxCount_ReturnsArgumentNotNullWithItems(int int maxCount) { // Act - var argument = Ensure.Argument(items, nameof(items)).NotNull().MaxCount(maxCount); + var argument = Ensure.Argument(items).NotNull().MaxCount(maxCount); // Assert argument.Value @@ -189,7 +189,7 @@ public void MaxCount_ItemsCountMeetsMaxCount_ReturnsArgumentNotNullWithItems(int public void MaxCount_ItemsCountNotMeetsMaxCount_ThrowsException(int[] items, int maxCount) { // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNull().MaxCount(maxCount); + Action act = () => Ensure.Argument(items).NotNull().MaxCount(maxCount); // Assert act @@ -208,7 +208,7 @@ public void MaxCount_ItemsCountNotMeetsMaxCountWithMessage_ThrowsExceptionWithMe const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNull().MaxCount(maxCount, message); + Action act = () => Ensure.Argument(items).NotNull().MaxCount(maxCount, message); // Assert act @@ -226,7 +226,7 @@ public void InRange_ItemsCountIsInRange_ReturnsArgumentNotNullWithItems( int[] items, int minCount, int maxCount) { // Act - var argument = Ensure.Argument(items, nameof(items)).NotNull().InRange(minCount, maxCount); + var argument = Ensure.Argument(items).NotNull().InRange(minCount, maxCount); // Assert argument.Value @@ -243,7 +243,7 @@ public void InRange_ItemsCountIsInRange_ReturnsArgumentNotNullWithItems( public void InRange_ItemsCountIsNotInRange_ThrowsException(int[] items, int minCount, int maxCount) { // Act - Action act = () => Ensure.Argument(items, nameof(items)).NotNull().InRange(minCount, maxCount); + Action act = () => Ensure.Argument(items).NotNull().InRange(minCount, maxCount); // Assert act @@ -264,7 +264,7 @@ public void InRange_ItemsCountIsNotInRangeWithMessage_ThrowsExceptionWithMessage // Act Action act = () => - Ensure.Argument(items, nameof(items)).NotNull().InRange(minCount, maxCount, message); + Ensure.Argument(items).NotNull().InRange(minCount, maxCount, message); // Assert act diff --git a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/GuidArgumentExtensionsTests.cs b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/GuidArgumentExtensionsTests.cs index 2fdc3ddd..c2e55afc 100644 --- a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/GuidArgumentExtensionsTests.cs +++ b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/GuidArgumentExtensionsTests.cs @@ -13,7 +13,7 @@ public void NotEmpty_NewGuid_ReturnsArgumentWithGuid() var guid = Guid.NewGuid(); // Act - var argument = Ensure.Argument(guid, nameof(guid)).NotEmpty(); + var argument = Ensure.Argument(guid).NotEmpty(); // Assert argument.Value @@ -27,7 +27,7 @@ public void NotEmpty_EmptyGuid_ThrowsException() var guid = Guid.Empty; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotEmpty(); + Action act = () => Ensure.Argument(guid).NotEmpty(); // Assert act @@ -42,7 +42,7 @@ public void NotEmpty_EmptyGuidWithMessage_ThrowsException() const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotEmpty(message); + Action act = () => Ensure.Argument(guid).NotEmpty(message); // Assert act @@ -57,7 +57,7 @@ public void NotEmpty_NewGuidNullable_ReturnsArgumentWithGuid() Guid? guid = Guid.NewGuid(); // Act - var argument = Ensure.Argument(guid, nameof(guid)).NotEmpty(); + var argument = Ensure.Argument(guid).NotEmpty(); // Assert argument.Value @@ -71,7 +71,7 @@ public void NotEmpty_NullGuidNullable_ThrowsException() Guid? guid = null; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotEmpty(); + Action act = () => Ensure.Argument(guid).NotEmpty(); // Assert act @@ -85,7 +85,7 @@ public void NotEmpty_EmptyGuidNullable_ThrowsException() Guid? guid = Guid.Empty; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotEmpty(); + Action act = () => Ensure.Argument(guid).NotEmpty(); // Assert act @@ -100,7 +100,7 @@ public void NotEmpty_EmptyGuidWithMessageNullable_ThrowsException() const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotEmpty(message); + Action act = () => Ensure.Argument(guid).NotEmpty(message); // Assert act @@ -116,7 +116,7 @@ public void NotEmpty_ArgNotNullNewGuid_ReturnsArgumentWithGuid() var guid = Guid.NewGuid(); // Act - var argument = Ensure.Argument(guid, nameof(guid)).NotNull().NotEmpty(); + var argument = Ensure.Argument(guid).NotNull().NotEmpty(); // Assert argument.Value @@ -130,7 +130,7 @@ public void NotEmpty_ArgNotNullEmptyGuid_ThrowsException() var guid = Guid.Empty; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotNull().NotEmpty(); + Action act = () => Ensure.Argument(guid).NotNull().NotEmpty(); // Assert act @@ -145,7 +145,7 @@ public void NotEmpty_ArgNotNullEmptyGuidWithMessage_ThrowsException() const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotNull().NotEmpty(message); + Action act = () => Ensure.Argument(guid).NotNull().NotEmpty(message); // Assert act @@ -160,7 +160,7 @@ public void NotEmpty_ArgNotNullNewGuidNullable_ReturnsArgumentWithGuid() Guid? guid = Guid.NewGuid(); // Act - var argument = Ensure.Argument(guid, nameof(guid)).NotNull().NotEmpty(); + var argument = Ensure.Argument(guid).NotNull().NotEmpty(); // Assert argument.Value @@ -174,7 +174,7 @@ public void NotEmpty_ArgNotNullNullGuidNullable_ThrowsException() Guid? guid = null; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotNull().NotEmpty(); + Action act = () => Ensure.Argument(guid).NotNull().NotEmpty(); // Assert act @@ -188,7 +188,7 @@ public void NotEmpty_ArgNotNullEmptyGuidNullable_ThrowsException() Guid? guid = Guid.Empty; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotNull().NotEmpty(); + Action act = () => Ensure.Argument(guid).NotNull().NotEmpty(); // Assert act @@ -203,7 +203,7 @@ public void NotEmpty_ArgNotNullEmptyGuidWithMessageNullable_ThrowsException() const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(guid, nameof(guid)).NotNull().NotEmpty(message); + Action act = () => Ensure.Argument(guid).NotNull().NotEmpty(message); // Assert act diff --git a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/MustArgumentExtensionsTests.cs b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/MustArgumentExtensionsTests.cs index b334fd94..dcd16ac1 100644 --- a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/MustArgumentExtensionsTests.cs +++ b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/MustArgumentExtensionsTests.cs @@ -13,7 +13,7 @@ public void Must_WithValueMatching_ReturnsArgumentWithValue() const string testValue = "Test"; // Act - var argument = Ensure.Argument(testValue, nameof(testValue)) + var argument = Ensure.Argument(testValue) .Must(x => x?.StartsWith("Te") == true); // Assert @@ -28,7 +28,7 @@ public void Must_WithValueNotMatching_ThrowsException() const string testValue = "Test"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)) + Action act = () => Ensure.Argument(testValue) .Must(x => x?.StartsWith("12") == true); // Assert @@ -44,7 +44,7 @@ public void Must_WithValueNotMatchingAndMessage_ThrowsExceptionWithMessage() const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)) + Action act = () => Ensure.Argument(testValue) .Must(x => x?.StartsWith("12") == true, message); // Assert @@ -60,7 +60,7 @@ public void Must_ArgNotNullWithValueMatching_ReturnsArgumentWithValue() const string testValue = "Test"; // Act - var argument = Ensure.Argument(testValue, nameof(testValue)).NotNull() + var argument = Ensure.Argument(testValue).NotNull() .Must(x => x.StartsWith("Te", StringComparison.Ordinal)); // Assert @@ -75,7 +75,7 @@ public void Must_ArgNotNullWithValueNotMatching_ThrowsException() const string testValue = "Test"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).NotNull() + Action act = () => Ensure.Argument(testValue).NotNull() .Must(x => x.StartsWith("12", StringComparison.Ordinal)); // Assert @@ -91,7 +91,7 @@ public void Must_ArgNotNullWithValueNotMatchingAndMessage_ThrowsExceptionWithMes const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).NotNull() + Action act = () => Ensure.Argument(testValue).NotNull() .Must(x => x.StartsWith("12", StringComparison.Ordinal), message); @@ -109,7 +109,7 @@ public void MustNot_WithValueNotMatching_ReturnsArgumentWithValue() const string testValue = "Test"; // Act - var argument = Ensure.Argument(testValue, nameof(testValue)) + var argument = Ensure.Argument(testValue) .MustNot(x => x?.StartsWith("12") == true); // Assert @@ -124,7 +124,7 @@ public void MustNot_WithValueMatching_ThrowsException() const string testValue = "Test"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)) + Action act = () => Ensure.Argument(testValue) .MustNot(x => x?.StartsWith("Te") == true); // Assert @@ -140,7 +140,7 @@ public void MustNot_WithValueMatchingAndMessage_ThrowsExceptionWithMessage() const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)) + Action act = () => Ensure.Argument(testValue) .MustNot(x => x?.StartsWith("Te") == true, message); // Assert @@ -156,7 +156,7 @@ public void MustNot_ArgNotNullWithValueNotMatching_ReturnsArgumentWithValue() const string testValue = "Test"; // Act - var argument = Ensure.Argument(testValue, nameof(testValue)).NotNull() + var argument = Ensure.Argument(testValue).NotNull() .MustNot(x => x.StartsWith("12", StringComparison.Ordinal)); // Assert @@ -171,7 +171,7 @@ public void MustNot_ArgNotNullWithValueMatching_ThrowsException() const string testValue = "Test"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).NotNull() + Action act = () => Ensure.Argument(testValue).NotNull() .MustNot(x => x.StartsWith("Te", StringComparison.Ordinal)); // Assert @@ -187,7 +187,7 @@ public void MustNot_ArgNotNullWithValueMatchingAndMessage_ThrowsExceptionWithMes const string message = "TestMessage"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).NotNull() + Action act = () => Ensure.Argument(testValue).NotNull() .MustNot(x => x.StartsWith("Te", StringComparison.Ordinal), message); diff --git a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/NullArgumentExtensionsTests.cs b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/NullArgumentExtensionsTests.cs index 9cf50131..62d08f27 100644 --- a/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/NullArgumentExtensionsTests.cs +++ b/tests/CreativeCoders.Core.UnitTests/EnsureArguments/Extensions/NullArgumentExtensionsTests.cs @@ -13,7 +13,7 @@ public void NotNull_ValueNotNull_ReturnsArgumentNotNull() const string testValue = "Test"; // Act - var argument = Ensure.Argument(testValue, nameof(testValue)).NotNull(); + var argument = Ensure.Argument(testValue).NotNull(); // Assert argument @@ -31,7 +31,7 @@ public void NotNull_ValueIsNull_ThrowsException() string? testValue = null; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).NotNull(); + Action act = () => Ensure.Argument(testValue).NotNull(); // Assert act @@ -46,7 +46,7 @@ public void NotNull_WithMessageValueIsNull_ThrowsException() string? testValue = null; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).NotNull(message); + Action act = () => Ensure.Argument(testValue).NotNull(message); // Assert act @@ -61,7 +61,7 @@ public void Null_ValueNull_ReturnsNull() const string? testValue = null; // Act - var value = Ensure.Argument(testValue, nameof(testValue)).Null(); + var value = Ensure.Argument(testValue).Null(); // Assert value @@ -75,7 +75,7 @@ public void Null_ValueIsNotNull_ThrowsException() const string testValue = "TestText"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).Null(); + Action act = () => Ensure.Argument(testValue).Null(); // Assert act @@ -90,7 +90,7 @@ public void Null_WithMessageValueIsNotNull_ThrowsException() const string testValue = "TestText"; // Act - Action act = () => Ensure.Argument(testValue, nameof(testValue)).Null(message); + Action act = () => Ensure.Argument(testValue).Null(message); // Assert act diff --git a/tests/CreativeCoders.SysConsole.App.UnitTests/TestData/TestProgramMainWithInvalidCtorArgs.cs b/tests/CreativeCoders.SysConsole.App.UnitTests/TestData/TestProgramMainWithInvalidCtorArgs.cs index 1bff531a..fd203e97 100644 --- a/tests/CreativeCoders.SysConsole.App.UnitTests/TestData/TestProgramMainWithInvalidCtorArgs.cs +++ b/tests/CreativeCoders.SysConsole.App.UnitTests/TestData/TestProgramMainWithInvalidCtorArgs.cs @@ -10,7 +10,7 @@ public class TestProgramMainWithInvalidCtorArgs : IMain { public TestProgramMainWithInvalidCtorArgs(string text) { - Ensure.NotNull(text, nameof(text)); + Ensure.NotNull(text); } public Task ExecuteAsync(string[] args) diff --git a/tests/CreativeCoders.SysConsole.Cli.Actions.UnitTests/TestData/DemoCliController.cs b/tests/CreativeCoders.SysConsole.Cli.Actions.UnitTests/TestData/DemoCliController.cs index 733c5a6b..aa8e09f7 100644 --- a/tests/CreativeCoders.SysConsole.Cli.Actions.UnitTests/TestData/DemoCliController.cs +++ b/tests/CreativeCoders.SysConsole.Cli.Actions.UnitTests/TestData/DemoCliController.cs @@ -18,7 +18,7 @@ public Task DoAsync() [CliAction("more/command")] public Task DoMoreAsync(DoCmdOptions options) { - Ensure.NotNull(options, nameof(options)); + Ensure.NotNull(options); return Task.FromResult(new CliActionResult()); }