Repository: CatLib/Core Branch: 2.0 Commit: 98a7b1db446d Files: 114 Total size: 544.0 KB Directory structure: gitextract_rz18cs7b/ ├── .dist/ │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── bucket.json │ └── netstandard2.0/ │ ├── CatLib.Core.deps.json │ └── CatLib.Core.pdb ├── .gitattributes ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── ---bug-report.md │ ├── ---documentation-issue.md │ ├── ---feature-request.md │ └── ---support-question.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CatLib.Core.sln ├── LICENSE ├── README.md ├── bucket.json ├── build.ps1 └── src/ ├── CatLib.Core/ │ ├── CatLib/ │ │ ├── App.cs │ │ ├── Application.cs │ │ ├── DebugLevel.cs │ │ ├── Events/ │ │ │ ├── AfterBootEventArgs.cs │ │ │ ├── AfterInitEventArgs.cs │ │ │ ├── AfterTerminateEventArgs.cs │ │ │ ├── ApplicationEventArgs.cs │ │ │ ├── ApplicationEvents.cs │ │ │ ├── BeforeBootEventArgs.cs │ │ │ ├── BeforeInitEventArgs.cs │ │ │ ├── BeforeTerminateEventArgs.cs │ │ │ ├── BootingEventArgs.cs │ │ │ ├── InitProviderEventArgs.cs │ │ │ ├── RegisterProviderEventArgs.cs │ │ │ └── StartCompletedEventArgs.cs │ │ ├── Facade.cs │ │ ├── IApplication.cs │ │ ├── IBootstrap.cs │ │ ├── IServiceProvider.cs │ │ ├── ServiceProvider.cs │ │ └── StartProcess.cs │ ├── CatLib.Core.csproj │ ├── Container/ │ │ ├── BindData.cs │ │ ├── BindDataExtension.cs │ │ ├── Bindable.cs │ │ ├── Container.cs │ │ ├── ContainerExtension.cs │ │ ├── GivenData.cs │ │ ├── IBindData.cs │ │ ├── IBindable.cs │ │ ├── IContainer.cs │ │ ├── IGivenData.cs │ │ ├── IMethodBind.cs │ │ ├── IParams.cs │ │ ├── InjectAttribute.cs │ │ ├── MethodBind.cs │ │ ├── MethodContainer.cs │ │ ├── ParamsCollection.cs │ │ ├── UnresolvableException.cs │ │ └── VariantAttribute.cs │ ├── EventDispatcher/ │ │ ├── EventDispatcher.cs │ │ ├── IEventDispatcher.cs │ │ └── IStoppableEvent.cs │ ├── Exception/ │ │ ├── AssertException.cs │ │ ├── LogicException.cs │ │ └── RuntimeException.cs │ ├── IO/ │ │ ├── CombineStream.cs │ │ ├── RingBufferStream.cs │ │ ├── SegmentStream.cs │ │ ├── StreamExtension.cs │ │ └── WrapperStream.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ └── Util/ │ ├── Arr.cs │ ├── Guard.cs │ ├── InternalHelper.cs │ ├── SortSet.cs │ └── Str.cs ├── CatLib.Core.Tests/ │ ├── CatLib/ │ │ ├── TestsApplication.cs │ │ └── TestsFacade.cs │ ├── CatLib.Core.Tests.csproj │ ├── Container/ │ │ ├── TestsBindData.cs │ │ ├── TestsContainer.cs │ │ └── TestsExtensionContainer.cs │ ├── EventDispatcher/ │ │ └── TestsEventDispatcher.cs │ ├── Fixture/ │ │ ├── AbstractClass.cs │ │ ├── Bar.cs │ │ ├── Baz.cs │ │ ├── CircularDependency.cs │ │ ├── Foo.cs │ │ ├── FooBar.cs │ │ ├── Fubar.cs │ │ ├── FubarChild.cs │ │ ├── IFoo.cs │ │ ├── Position.cs │ │ ├── Quux.cs │ │ ├── QuuxFoo.cs │ │ ├── TestEventArgs.cs │ │ ├── Variant.cs │ │ └── VariantModel.cs │ ├── Framework/ │ │ ├── ExpectedExceptionAndMessageAttribute.cs │ │ ├── TestException.cs │ │ └── TestMethodIterativeAttribute.cs │ ├── IO/ │ │ ├── TestsCombineStream.cs │ │ ├── TestsRingBuffer.cs │ │ ├── TestsSegmentStream.cs │ │ └── TestsStreamExtension.cs │ └── Util/ │ ├── TestsArr.cs │ ├── TestsGuard.cs │ ├── TestsSortSet.cs │ └── TestsStr.cs ├── Directory.Build.props ├── analysis.ruleset ├── analysis.test.ruleset ├── settings.runsettings └── stylecop.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dist/CHANGELOG.md ================================================ # Release Notes ## [v2.0.0-alpha.1 (2019-12-04)](https://github.com/CatLib/Core/releases/tag/v2.0.0) #### Added - `Inject` allowed to be set to optional.(#253 ) - `Arr.Test` can get matched value of the first match(#280 ) #### Changed - Comments translated from Chinese into English(#133 ) - Defined Container.Build as a virtual function(#210 ) - Optimizes the constructor of `MethodContainer`(#218 ) - The default project uses the .net standard 2.0(#225 ) - Rename Util helper class to Helper class Change access level to internal.(#230 ) - `Application.IsRegisted` changed(rename) to `IsRegistered`(#226 ) - Use `VariantAttribute` to mark variable types instead of `IVariant`(#232 ) - `Guard` Will be expandable with `Guard.That`(#233 ) - Fixed the problem of container exception stack loss(#234 ) - Adjusted the internal file structure to make it clearer(#236 ). - Add code analyzers (#206 ) - Refactoring event system (#177 ) - Refactoring `RingBuffer` make it inherit from `Stream`.(#238 ) - Namespace structure adjustment(optimization).(#241 ) - `App` can be extended by `That` (Handler rename to that) and removed `HasHandler` API (#242 ) - Unnecessary inheritance: WrappedStream(#247 ) - Clean up useless comment(#249 ). - `Guard.Require` can set error messages and internal exceptions(#250). - Exception class implementation support: SerializationInfo build(#252 ). - Refactoring unit test, import moq.(#255 ) - `CodeStandardException` replaces to `LogicException`(#257 ) - Exception move to namespace `CatLib.Exception`(#258 ) - `Facade<>.Instance` changed to `Facade<>.That`(#259 ) - `Application.StartProcess` migrate to `StartProcess`(#260 ) - `Arr` optimization, lifting some unnecessary restrictions (#263) - `Str` optimization, lifting some unnecessary restrictions (#264) - Refactoring `SortSet`(#265 ) - Removed global params in application constructor. use Application.New() instead.(#267 ) - Containers are no longer thread-safe by default(#270 ) #### Fixed - Fixed a bug that caused `Arr.Fill` to not work properly under special circumstances. (#255 ) #### Removed - Removed `ExcludeFromCodeCoverageAttribute` (#229 ) - Removed unnecessary interface design `ISortSet`(#211 ). - Removed `Version` classes and `Application.Compare` method.(#212). - Removed `Template` supported(#213 ). - Removed `FilterChain` supported(#214 ). - Removed `Enum` supported(#215 ). - Removed `IAwait` interface(#217 ). - Removed `Container.Flash` api(#219 ). - Removed `Arr.Flash` method(#220 ). - Removed `Dict` helper class(#221 ). - Removed `ThreadStatic` helper class(#223 ). - Removed `QuickList` supported(#224 ). - Removed `Storage` supported(#228 ) - Removed `SystemTime` class(#235 ). - Removed `ICoroutineInit` feature from core library(#243 ). - Removed the priority attribute, depending on the loading order(#244 ). - Removed `Util.Encoding` (#245 ). - Removed `Str.Encoding`(#246 ) - Removed `IServiceProviderType` feature in core library(#246 ). - Removed unnecessary extension functions(#247 ). - Removed `PipelineStream` stream.(#256 ) - Removed all `Obsolete` method and clean code.(#261 ) - Removed `App.Version`.(#266 ) ================================================ FILE: .dist/LICENSE ================================================ MIT License Copyright (c) 2017 CatLib Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: .dist/README.md ================================================ 

Codecov Downloads

## About CatLib `CatLib` is the lightweight dependency injection container and helper functions library. - [Service Provider](https://catlib.io/lasted/architecture/service-provider.html) - [Application](https://catlib.io/lasted/architecture/application.html) - [IOC Container](https://catlib.io/lasted/architecture/container.html) - [Facades](https://catlib.io/lasted/architecture/facade.html) ## Install CatLib Core **Installed with Nuget** ```PM $ Install-Package CatLib.Core -Version 2.0.0 ``` **Installed with [Bucket](https://github.com/getbucket/bucket)** ```shell $ bucket require catlib/core ``` **Installed with Github Release** [Download the latest version](https://github.com/CatLib/Core/releases)。 ## Learning CatLib CatLib has the most extensive and thorough [documentation](https://catlib.io), making it a breeze to get started with the framework. > 需要中文支持? 请访问[CatLib中文文档](https://zh.catlib.io)。 ## Contribution CatLib is still a young framework, and her growth and your contribution are inseparable. If you want to contribute to the project, please refer to: [CatLib Contribution Guide](https://catlib.io/lasted/contribution.html) Your contribution will be included in the list of contributors,Welcome Pull Request! ## License The open source license used by CatLib is [MIT license](http://opensource.org/licenses/MIT). ## Support * email: support@catlib.io * slack: [catlib.slack](https://catlib.slack.com/messages/internals/) ================================================ FILE: .dist/bucket.json ================================================ { "name": "catlib/core", "version": "2.0.0", "description": "catlib framework is the lightweight dependency injection container and helper functions library.", "keywords": ["framework", "catlib"], "license": "MIT", "homepage": "https://catlib.io", "support": { "issues": "https://github.com/CatLib/Core/issues", "source": "https://github.com/CatLib/Core" }, "authors": [ { "name": "Meng han Yu", "email": "menghanyu1994@gmail.com" } ] } ================================================ FILE: .dist/netstandard2.0/CatLib.Core.deps.json ================================================ { "runtimeTarget": { "name": ".NETStandard,Version=v2.0/", "signature": "" }, "compilationOptions": {}, "targets": { ".NETStandard,Version=v2.0": {}, ".NETStandard,Version=v2.0/": { "CatLib.Core/0.2.0-alpha.1+37a93a5": { "dependencies": { "Microsoft.CodeAnalysis.CSharp": "3.3.1", "Microsoft.CodeAnalysis.FxCopAnalyzers": "2.9.6", "NETStandard.Library": "2.0.3", "SonarAnalyzer.CSharp": "7.17.0.9346", "StyleCop.Analyzers": "1.1.118" }, "runtime": { "CatLib.Core.dll": {} } }, "Microsoft.CodeAnalysis.Analyzers/2.9.4": {}, "Microsoft.CodeAnalysis.Common/3.3.1": { "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "2.9.4", "System.Collections.Immutable": "1.5.0", "System.Memory": "4.5.3", "System.Reflection.Metadata": "1.6.0", "System.Runtime.CompilerServices.Unsafe": "4.5.2", "System.Text.Encoding.CodePages": "4.5.1", "System.Threading.Tasks.Extensions": "4.5.3" } }, "Microsoft.CodeAnalysis.CSharp/3.3.1": { "dependencies": { "Microsoft.CodeAnalysis.Common": "3.3.1" } }, "Microsoft.CodeAnalysis.FxCopAnalyzers/2.9.6": { "dependencies": { "Microsoft.CodeAnalysis.VersionCheckAnalyzer": "2.9.6", "Microsoft.CodeQuality.Analyzers": "2.9.6", "Microsoft.NetCore.Analyzers": "2.9.6", "Microsoft.NetFramework.Analyzers": "2.9.6" } }, "Microsoft.CodeAnalysis.VersionCheckAnalyzer/2.9.6": {}, "Microsoft.CodeQuality.Analyzers/2.9.6": {}, "Microsoft.NetCore.Analyzers/2.9.6": {}, "Microsoft.NETCore.Platforms/1.1.0": {}, "Microsoft.NetFramework.Analyzers/2.9.6": {}, "NETStandard.Library/2.0.3": { "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } }, "SonarAnalyzer.CSharp/7.17.0.9346": {}, "StyleCop.Analyzers/1.1.118": {}, "System.Buffers/4.4.0": {}, "System.Collections.Immutable/1.5.0": {}, "System.Memory/4.5.3": { "dependencies": { "System.Buffers": "4.4.0", "System.Numerics.Vectors": "4.4.0", "System.Runtime.CompilerServices.Unsafe": "4.5.2" } }, "System.Numerics.Vectors/4.4.0": {}, "System.Reflection.Metadata/1.6.0": { "dependencies": { "System.Collections.Immutable": "1.5.0" } }, "System.Runtime.CompilerServices.Unsafe/4.5.2": {}, "System.Text.Encoding.CodePages/4.5.1": { "dependencies": { "System.Runtime.CompilerServices.Unsafe": "4.5.2" } }, "System.Threading.Tasks.Extensions/4.5.3": { "dependencies": { "System.Runtime.CompilerServices.Unsafe": "4.5.2" } } } }, "libraries": { "CatLib.Core/0.2.0-alpha.1+37a93a5": { "type": "project", "serviceable": false, "sha512": "" }, "Microsoft.CodeAnalysis.Analyzers/2.9.4": { "type": "package", "serviceable": true, "sha512": "sha512-alIJhS0VUg/7x5AsHEoovh/wRZ0RfCSS7k5pDSqpRLTyuMTtRgj6OJJPRApRhJHOGYYsLakf1hKeXFoDwKwNkg==", "path": "microsoft.codeanalysis.analyzers/2.9.4", "hashPath": "microsoft.codeanalysis.analyzers.2.9.4.nupkg.sha512" }, "Microsoft.CodeAnalysis.Common/3.3.1": { "type": "package", "serviceable": true, "sha512": "sha512-N5yQdGy+M4kimVG7hwCeGTCfgYjK2o5b/Shumkb/rCC+/SAkvP1HUAYK+vxPFS7dLJNtXLRsmPHKj3fnyNWnrw==", "path": "microsoft.codeanalysis.common/3.3.1", "hashPath": "microsoft.codeanalysis.common.3.3.1.nupkg.sha512" }, "Microsoft.CodeAnalysis.CSharp/3.3.1": { "type": "package", "serviceable": true, "sha512": "sha512-WDUIhTHem38H6VJ98x2Ssq0fweakJHnHYl7vbG8ARnsAwLoJKCQCy78EeY1oRrCKG42j0v6JVljKkeqSDA28UA==", "path": "microsoft.codeanalysis.csharp/3.3.1", "hashPath": "microsoft.codeanalysis.csharp.3.3.1.nupkg.sha512" }, "Microsoft.CodeAnalysis.FxCopAnalyzers/2.9.6": { "type": "package", "serviceable": true, "sha512": "sha512-GBZV6P8u3uIXYrlapvbpaaaB5LpF3hk+I2ThIcoVyENr8krNOovFyJhi6FQHgnxd0XLPj9J9Nt35iuqwVe/XJw==", "path": "microsoft.codeanalysis.fxcopanalyzers/2.9.6", "hashPath": "microsoft.codeanalysis.fxcopanalyzers.2.9.6.nupkg.sha512" }, "Microsoft.CodeAnalysis.VersionCheckAnalyzer/2.9.6": { "type": "package", "serviceable": true, "sha512": "sha512-Q5rRN0+9TJsfsXaGKnzDjGkUZ4Rd8ZKmpSND1j49jsmLacCspp4ZaJ31nJn76B/W0qrb4GkEBxKtj3U9lo2gqg==", "path": "microsoft.codeanalysis.versioncheckanalyzer/2.9.6", "hashPath": "microsoft.codeanalysis.versioncheckanalyzer.2.9.6.nupkg.sha512" }, "Microsoft.CodeQuality.Analyzers/2.9.6": { "type": "package", "serviceable": true, "sha512": "sha512-jND04w/FPgfBYKkLLnosmvgHs8NMpMvQ7LQqyiCjMlKOM+MT6edgvTU0vAAowxGuLGceC6pjJm0kUVkG9qT3CQ==", "path": "microsoft.codequality.analyzers/2.9.6", "hashPath": "microsoft.codequality.analyzers.2.9.6.nupkg.sha512" }, "Microsoft.NetCore.Analyzers/2.9.6": { "type": "package", "serviceable": true, "sha512": "sha512-YPcQ+gCt66btt9jNHNi4vrRK6AAP7EIO17quwUeHLUw7vhc6q18r2RYVTLKQvOQuOXs1UEYHArENpppwFP4MLA==", "path": "microsoft.netcore.analyzers/2.9.6", "hashPath": "microsoft.netcore.analyzers.2.9.6.nupkg.sha512" }, "Microsoft.NETCore.Platforms/1.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", "path": "microsoft.netcore.platforms/1.1.0", "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" }, "Microsoft.NetFramework.Analyzers/2.9.6": { "type": "package", "serviceable": true, "sha512": "sha512-8VyWe53rtUkgJFAkbCZY5KT2t1tEmRrdK1Z+zbyRlzoKVEHyMs0+WNBeJ8d2YB/NjXaYAlaVG2CGZFnQUrwY6A==", "path": "microsoft.netframework.analyzers/2.9.6", "hashPath": "microsoft.netframework.analyzers.2.9.6.nupkg.sha512" }, "NETStandard.Library/2.0.3": { "type": "package", "serviceable": true, "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", "path": "netstandard.library/2.0.3", "hashPath": "netstandard.library.2.0.3.nupkg.sha512" }, "SonarAnalyzer.CSharp/7.17.0.9346": { "type": "package", "serviceable": true, "sha512": "sha512-Fmap0bvZRojVKRSJj8xS3Bqsltmjy6i3bJF2gXMkuKdtFFJeS546RT75G3YN3IDSygrIEyfQdL+s8ep60vzORQ==", "path": "sonaranalyzer.csharp/7.17.0.9346", "hashPath": "sonaranalyzer.csharp.7.17.0.9346.nupkg.sha512" }, "StyleCop.Analyzers/1.1.118": { "type": "package", "serviceable": true, "sha512": "sha512-Onx6ovGSqXSK07n/0eM3ZusiNdB6cIlJdabQhWGgJp3Vooy9AaLS/tigeybOJAobqbtggTamoWndz72JscZBvw==", "path": "stylecop.analyzers/1.1.118", "hashPath": "stylecop.analyzers.1.1.118.nupkg.sha512" }, "System.Buffers/4.4.0": { "type": "package", "serviceable": true, "sha512": "sha512-AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==", "path": "system.buffers/4.4.0", "hashPath": "system.buffers.4.4.0.nupkg.sha512" }, "System.Collections.Immutable/1.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ==", "path": "system.collections.immutable/1.5.0", "hashPath": "system.collections.immutable.1.5.0.nupkg.sha512" }, "System.Memory/4.5.3": { "type": "package", "serviceable": true, "sha512": "sha512-3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", "path": "system.memory/4.5.3", "hashPath": "system.memory.4.5.3.nupkg.sha512" }, "System.Numerics.Vectors/4.4.0": { "type": "package", "serviceable": true, "sha512": "sha512-UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==", "path": "system.numerics.vectors/4.4.0", "hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512" }, "System.Reflection.Metadata/1.6.0": { "type": "package", "serviceable": true, "sha512": "sha512-COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==", "path": "system.reflection.metadata/1.6.0", "hashPath": "system.reflection.metadata.1.6.0.nupkg.sha512" }, "System.Runtime.CompilerServices.Unsafe/4.5.2": { "type": "package", "serviceable": true, "sha512": "sha512-wprSFgext8cwqymChhrBLu62LMg/1u92bU+VOwyfBimSPVFXtsNqEWC92Pf9ofzJFlk4IHmJA75EDJn1b2goAQ==", "path": "system.runtime.compilerservices.unsafe/4.5.2", "hashPath": "system.runtime.compilerservices.unsafe.4.5.2.nupkg.sha512" }, "System.Text.Encoding.CodePages/4.5.1": { "type": "package", "serviceable": true, "sha512": "sha512-4J2JQXbftjPMppIHJ7IC+VXQ9XfEagN92vZZNoG12i+zReYlim5dMoXFC1Zzg7tsnKDM7JPo5bYfFK4Jheq44w==", "path": "system.text.encoding.codepages/4.5.1", "hashPath": "system.text.encoding.codepages.4.5.1.nupkg.sha512" }, "System.Threading.Tasks.Extensions/4.5.3": { "type": "package", "serviceable": true, "sha512": "sha512-+MvhNtcvIbqmhANyKu91jQnvIRVSTiaOiFNfKWwXGHG48YAb4I/TyH8spsySiPYla7gKal5ZnF3teJqZAximyQ==", "path": "system.threading.tasks.extensions/4.5.3", "hashPath": "system.threading.tasks.extensions.4.5.3.nupkg.sha512" } } } ================================================ FILE: .gitattributes ================================================ src/ export-ignore *.sln export-ignore .gitignore export-ignore .gitattributes export-ignore *.ps1 export-ignore .github/ export-ignore .travis.yml export-ignore /bucket.json export-ignore /README.md export-ignore /CHANGELOG.md export-ignore /LICENSE export-ignore ================================================ FILE: .github/ISSUE_TEMPLATE/---bug-report.md ================================================ --- name: "\U0001F41B Bug Report" about: Report a general framework issue. Please ensure your CatLib version is still supported. title: "[BUG] Add a placeholder for issues title, ex." labels: Need Review, bug assignees: yb199478 --- **CatLib version(s) affected**: x.y.z #### Description #### How to reproduce #### Possible Solution #### Additional context ================================================ FILE: .github/ISSUE_TEMPLATE/---documentation-issue.md ================================================ --- name: "\U0001F4DA Documentation Issue" about: For documentation issues, open a pull request at https://github.com/catlib/catlib-en.io title: "[Document] Add a placeholder for issues title, ex." labels: Document, Need Review assignees: yb199478 --- The CatLib documentation has its own dedicated repository. Please open a pull request at https://github.com/catlib/catlib-en to correct the issue you have found. Thanks! ================================================ FILE: .github/ISSUE_TEMPLATE/---feature-request.md ================================================ --- name: "\U0001F4A1 Feature Request" about: Suggest an idea for this project title: "[Feature] Suggested as the issues title, ex." labels: Feature, Need Review assignees: yb199478 --- If you would like to propose new CatLib features, please make a pull request, or open an issue. ================================================ FILE: .github/ISSUE_TEMPLATE/---support-question.md ================================================ --- name: "\U0001F680 Support Question" about: This repository is only for for questions about using CatLib and its components. title: "[Question] Add a placeholder for issues title, ex." labels: Need Review, question assignees: yb199478 --- > If you need support, please use the document first: http://catlib.io **CatLib version(s) affected**: x.y.z #### Description #### What's need help ================================================ FILE: .gitignore ================================================ .vs/ src/*/bin/ src/*/obj/ TestResults/ coverage.json coverage.opencover.xml .DS_Store .idea/ *.sln.DotSettings.user ================================================ FILE: .travis.yml ================================================ language: csharp mono: none dotnet: 3.0.100 stages: - compile - test jobs: include: - stage: compile script: - dotnet build -c Release - stage: test script: - dotnet test -c Release -s src/settings.runsettings /p:CollectCoverage=true /p:CoverletOutputFormat=\"json,teamcity,opencover\" /p:CoverletOutput=\"../../coverage/\" /p:Exclude=[*.Tests]* /p:Threshold=90 /p:ThresholdType=line after_success: - bash <(curl -s https://codecov.io/bash) -f coverage/coverage.opencover.xml -t $CODE_COV_TOKEN ================================================ FILE: CHANGELOG.md ================================================ # Release Notes ## [v2.0.0-alpha.1 (2019-12-04)](https://github.com/CatLib/Core/releases/tag/v2.0.0) #### Added - `Inject` allowed to be set to optional.(#253 ) - `Arr.Test` can get matched value of the first match(#280 ) #### Changed - Comments translated from Chinese into English(#133 ) - Defined Container.Build as a virtual function(#210 ) - Optimizes the constructor of `MethodContainer`(#218 ) - The default project uses the .net standard 2.0(#225 ) - Rename Util helper class to Helper class Change access level to internal.(#230 ) - `Application.IsRegisted` changed(rename) to `IsRegistered`(#226 ) - Use `VariantAttribute` to mark variable types instead of `IVariant`(#232 ) - `Guard` Will be expandable with `Guard.That`(#233 ) - Fixed the problem of container exception stack loss(#234 ) - Adjusted the internal file structure to make it clearer(#236 ). - Add code analyzers (#206 ) - Refactoring event system (#177 ) - Refactoring `RingBuffer` make it inherit from `Stream`.(#238 ) - Namespace structure adjustment(optimization).(#241 ) - `App` can be extended by `That` (Handler rename to that) and removed `HasHandler` API (#242 ) - Unnecessary inheritance: WrappedStream(#247 ) - Clean up useless comment(#249 ). - `Guard.Require` can set error messages and internal exceptions(#250). - Exception class implementation support: SerializationInfo build(#252 ). - Refactoring unit test, import moq.(#255 ) - `CodeStandardException` replaces to `LogicException`(#257 ) - Exception move to namespace `CatLib.Exception`(#258 ) - `Facade<>.Instance` changed to `Facade<>.That`(#259 ) - `Application.StartProcess` migrate to `StartProcess`(#260 ) - `Arr` optimization, lifting some unnecessary restrictions (#263) - `Str` optimization, lifting some unnecessary restrictions (#264) - Refactoring `SortSet`(#265 ) - Removed global params in application constructor. use Application.New() instead.(#267 ) - Containers are no longer thread-safe by default(#270 ) #### Fixed - Fixed a bug that caused `Arr.Fill` to not work properly under special circumstances. (#255 ) #### Removed - Removed `ExcludeFromCodeCoverageAttribute` (#229 ) - Removed unnecessary interface design `ISortSet`(#211 ). - Removed `Version` classes and `Application.Compare` method.(#212). - Removed `Template` supported(#213 ). - Removed `FilterChain` supported(#214 ). - Removed `Enum` supported(#215 ). - Removed `IAwait` interface(#217 ). - Removed `Container.Flash` api(#219 ). - Removed `Arr.Flash` method(#220 ). - Removed `Dict` helper class(#221 ). - Removed `ThreadStatic` helper class(#223 ). - Removed `QuickList` supported(#224 ). - Removed `Storage` supported(#228 ) - Removed `SystemTime` class(#235 ). - Removed `ICoroutineInit` feature from core library(#243 ). - Removed the priority attribute, depending on the loading order(#244 ). - Removed `Util.Encoding` (#245 ). - Removed `Str.Encoding`(#246 ) - Removed `IServiceProviderType` feature in core library(#246 ). - Removed unnecessary extension functions(#247 ). - Removed `PipelineStream` stream.(#256 ) - Removed all `Obsolete` method and clean code.(#261 ) - Removed `App.Version`.(#266 ) ================================================ FILE: CatLib.Core.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28803.202 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CatLib.Core", "src\CatLib.Core\CatLib.Core.csproj", "{C99EA95A-3D04-4588-97E5-D81078EE85BD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CatLib.Core.Tests", "src\CatLib.Core.Tests\CatLib.Core.Tests.csproj", "{2E89486C-7A92-4C94-8F3E-D8DC1F5C883B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C99EA95A-3D04-4588-97E5-D81078EE85BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C99EA95A-3D04-4588-97E5-D81078EE85BD}.Debug|Any CPU.Build.0 = Debug|Any CPU {C99EA95A-3D04-4588-97E5-D81078EE85BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {C99EA95A-3D04-4588-97E5-D81078EE85BD}.Release|Any CPU.Build.0 = Release|Any CPU {2E89486C-7A92-4C94-8F3E-D8DC1F5C883B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E89486C-7A92-4C94-8F3E-D8DC1F5C883B}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E89486C-7A92-4C94-8F3E-D8DC1F5C883B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E89486C-7A92-4C94-8F3E-D8DC1F5C883B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BD9525ED-3010-4A67-B726-6FB2960FEC30} EndGlobalSection EndGlobal ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 CatLib Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ 

Codecov Downloads

## About CatLib `CatLib` is the lightweight dependency injection container and helper functions library. - [Service Provider](https://catlib.io/lasted/architecture/service-provider.html) - [Application](https://catlib.io/lasted/architecture/application.html) - [IOC Container](https://catlib.io/lasted/architecture/container.html) - [Facades](https://catlib.io/lasted/architecture/facade.html) ## Install CatLib Core **Installed with Nuget** ```PM $ Install-Package CatLib.Core -Version 2.0.0 ``` **Installed with [Bucket](https://github.com/getbucket/bucket)** ```shell $ bucket require catlib/core ``` **Installed with Github Release** [Download the latest version](https://github.com/CatLib/Core/releases)。 ## Learning CatLib CatLib has the most extensive and thorough [documentation](https://catlib.io), making it a breeze to get started with the framework. > 需要中文支持? 请访问[CatLib中文文档](https://cn.catlib.io)。 ## Contribution CatLib is still a young framework, and her growth and your contribution are inseparable. If you want to contribute to the project, please refer to: [CatLib Contribution Guide](https://catlib.io/lasted/contribution.html) Your contribution will be included in the list of contributors,Welcome Pull Request! ## License The open source license used by CatLib is [MIT license](http://opensource.org/licenses/MIT). ## Support * email: support@catlib.io * slack: [catlib.slack](https://catlib.slack.com/messages/internals/) ================================================ FILE: bucket.json ================================================ { "name": "catlib/core", "version": "2.0.0", "description": "catlib framework is the lightweight dependency injection container and helper functions library.", "keywords": ["framework", "catlib"], "license": "MIT", "homepage": "https://catlib.io", "support": { "issues": "https://github.com/CatLib/Core/issues", "source": "https://github.com/CatLib/Core" }, "authors": [ { "name": "Meng han Yu", "email": "menghanyu1994@gmail.com" } ] } ================================================ FILE: build.ps1 ================================================ param( [string]$version="master-dev" ) $dist = ".dist" $root = (Get-Item -Path ".\").FullName $publishDir = $root + "\src\CatLib.Core\bin\Release\netstandard2.0\publish" $version = $version.Trim() $versionParttern = "^v?(?(?\d{1,5})(?\.\d+)?(?\.\d+)?(?\.\d+)?|master)(?\-(?:stable|beta|b|RC|alpha|a|patch|pl|p|dev)(?:(?:[.-]?\d+)+)?)?(?\+[0-9A-Za-z\-\.]+)?$" if(!($version -match $versionParttern)) { throw ("Invalid version, must conform to the semver version: " + $version) } if($matches["stability"] -eq $null) { $matches["stability"] = "-stable" } if($matches["master"] -eq "master") { $matches["major"] = "0" } if($matches["minor"] -eq $null) { $matches["minor"] = ".0" } if($matches["patch"] -eq $null) { $matches["patch"] = ".0" } if($matches["build"] -match "^[0-9a-f]{40}$") { $env:CATLIB_COMMIT_SHA = $matches["build"].substring(1).trim() $matches["build"] = $matches["build"].substring(0, 8) } elseif($env:CI_COMMIT_SHA -ne $null) { $env:CATLIB_COMMIT_SHA = ($env:CI_COMMIT_SHA).trim() $matches["build"] = "+" + $env:CATLIB_COMMIT_SHA.substring(0, 7) } else { $env:CATLIB_COMMIT_SHA = (git rev-parse HEAD).trim() $matches["build"] = "+" + $env:CATLIB_COMMIT_SHA.substring(0, 7) } $major = $matches["major"] $minor = $matches["minor"] $assemblyVersion = $major + ".0.0.0" $versionNormalized = $major + $minor + $matches["patch"] + $matches["revision"] + $matches["stability"] + $matches["build"] $beginDateTime = ([DateTime] "01/01/2000"); $midnightDateTime = (Get-Date -Hour 0 -Minute 0 -Second 0); $elapseDay = -1 * (New-TimeSpan -end $beginDateTime).Days $elapseMidnight = [math]::floor((New-TimeSpan $midnightDateTime -End (Get-Date)).TotalSeconds * 0.5) $fileVersion = $major + $minor + "." + $elapseDay + "." + $elapseMidnight dotnet publish src\CatLib.Core\CatLib.Core.csproj -c Release /p:Version=$versionNormalized /p:AssemblyVersion=$assemblyVersion /p:FileVersion=$fileVersion --self-contained false if($LastExitCode){ echo "Abnormal. build failded." exit(1) } if(Test-Path -Path $dist) { Remove-Item $dist -Recurse } mkdir $dist mkdir $dist\netstandard2.0 cp $publishDir\* $dist\netstandard2.0 -Recurse cp LICENSE $dist cp CHANGELOG.md $dist cp README.md $dist cp bucket.json $dist $env:CATLIB_PROJECT_VERSION=$versionNormalized.trim() $env:CATLIB_FILE_VERSION=$fileVersion.trim() $env:CATLIB_STABILITY=$matches["stability"].substring(1).trim() $env:CATLIB_MIN_DOTNET_CORE="3.0.0".trim() $env:CATLIB_PUBLISH_DIR=$publishDir.trim() echo "CATLIB_COMMIT_SHA $env:CATLIB_COMMIT_SHA" echo "CATLIB_FILE_VERSION $env:CATLIB_FILE_VERSION" echo "CATLIB_PROJECT_VERSION $env:CATLIB_PROJECT_VERSION" echo "CATLIB_STABILITY $env:CATLIB_STABILITY" echo "CATLIB_MIN_DOTNET_CORE $env:CATLIB_MIN_DOTNET_CORE" echo "CATLIB_PUBLISH_DIR $env:CATLIB_PUBLISH_DIR" ================================================ FILE: src/CatLib.Core/CatLib/App.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; #pragma warning disable CA1030 namespace CatLib { /// /// The static facade. /// [ExcludeFromCodeCoverage] #pragma warning disable S1118 public abstract class App #pragma warning restore S1118 { /// /// The instance. /// private static IApplication that; /// /// Callback when a new instance is created. /// public static event Action OnNewApplication { add { RaiseOnNewApplication += value; if (That != null) { value?.Invoke(That); } } remove => RaiseOnNewApplication -= value; } /// /// Callback when a new instance is created. /// private static event Action RaiseOnNewApplication; /// /// Gets or Sets the instance. /// public static IApplication That { get { return that; } set { that = value; RaiseOnNewApplication?.Invoke(that); } } /// public static bool IsMainThread => That.IsMainThread; /// public static DebugLevel DebugLevel { get => That.DebugLevel; set => That.DebugLevel = value; } /// public static void Terminate() { That.Terminate(); } /// public static void Register(IServiceProvider provider, bool force = false) { That.Register(provider, force); } /// public static bool IsRegistered(IServiceProvider provider) { return That.IsRegistered(provider); } /// public static long GetRuntimeId() { return That.GetRuntimeId(); } /// public static void UnbindMethod(object target) { That.UnbindMethod(target); } /// public static object Invoke(string method, params object[] userParams) { return That.Invoke(method, userParams); } /// public static IContainer OnFindType(Func func, int priority = int.MaxValue) { return That.OnFindType(func, priority); } /// public static IContainer OnRebound(string service, Action callback) { return That.OnRebound(service, callback); } /// public static IBindData GetBind(string service) { return That.GetBind(service); } /// public static IBindData GetBind() { return That.GetBind(); } /// public static bool HasInstance(string service) { return That.HasInstance(service); } /// public static bool HasInstance() { #if CATLIB_PERFORMANCE return Facade.HasInstance || Handler.HasInstance(); #else return That.HasInstance(); #endif } /// public static bool IsResolved(string service) { return That.IsResolved(service); } /// public static bool IsResolved() { return That.IsResolved(); } /// public static bool HasBind(string service) { return That.HasBind(service); } /// public static bool HasBind() { return That.HasBind(); } /// public static bool CanMake(string service) { return That.CanMake(service); } /// public static bool CanMake() { return That.CanMake(); } /// public static bool IsStatic(string service) { return That.IsStatic(service); } /// public static bool IsStatic() { return That.IsStatic(); } /// public static bool IsAlias(string name) { return That.IsAlias(name); } /// public static bool IsAlias() { return That.IsAlias(); } /// public static IContainer Alias(string alias, string service) { return That.Alias(alias, service); } /// public static IContainer Alias() { return That.Alias(); } /// public static void Extend(string service, Func closure) { That.Extend(service, closure); } /// public static void Extend(string service, Func closure) { That.Extend(service, closure); } /// public static void Extend(Func closure) { That.Extend(closure); } /// public static void Extend(Func closure) { That.Extend(closure); } /// public static void Extend(Func closure) { That.Extend(closure); } /// public static void Extend(Func closure) { That.Extend(closure); } /// public static IBindData Bind() { return That.Bind(); } /// public static IBindData Bind() { return That.Bind(); } /// public static IBindData Bind(string service, Type concrete, bool isStatic) { return That.Bind(service, concrete, isStatic); } /// public static IBindData Bind(string service, Func concrete, bool isStatic) { return That.Bind(service, concrete, isStatic); } /// public static IBindData Bind(Func concrete) { return That.Bind(concrete); } /// public static IBindData Bind(Func concrete) { return That.Bind(concrete); } /// public static IBindData Bind(Func concrete) { return That.Bind(concrete); } /// public static IBindData Bind(string service, Func concrete) { return That.Bind(service, concrete); } /// public static bool BindIf(string service, Func concrete, bool isStatic, out IBindData bindData) { return That.BindIf(service, concrete, isStatic, out bindData); } /// public static bool BindIf(string service, Type concrete, bool isStatic, out IBindData bindData) { return That.BindIf(service, concrete, isStatic, out bindData); } /// public static bool BindIf(out IBindData bindData) { return That.BindIf(out bindData); } /// public static bool BindIf(out IBindData bindData) { return That.BindIf(out bindData); } /// public static bool BindIf(Func concrete, out IBindData bindData) { return That.BindIf(concrete, out bindData); } /// public static bool BindIf(Func concrete, out IBindData bindData) { return That.BindIf(concrete, out bindData); } /// public static bool BindIf(Func concrete, out IBindData bindData) { return That.BindIf(concrete, out bindData); } /// public static bool BindIf(string service, Func concrete, out IBindData bindData) { return That.BindIf(service, concrete, out bindData); } /// public static IBindData Singleton() { return That.Singleton(); } /// public static IBindData Singleton() { return That.Singleton(); } /// public static IBindData Singleton(Func concrete) { return That.Singleton(concrete); } /// public static IBindData Singleton(Func concrete) { return That.Singleton(concrete); } /// public static IBindData Singleton(Func concrete) { return That.Singleton(concrete); } /// public static IBindData Singleton(string service, Func concrete) { return That.Singleton(service, concrete); } /// public static bool SingletonIf(out IBindData bindData) { return That.SingletonIf(out bindData); } /// public static bool SingletonIf(out IBindData bindData) { return That.SingletonIf(out bindData); } /// public static bool SingletonIf(Func concrete, out IBindData bindData) { return That.SingletonIf(concrete, out bindData); } /// public static bool SingletonIf(Func concrete, out IBindData bindData) { return That.SingletonIf(concrete, out bindData); } /// public static bool SingletonIf(Func concrete, out IBindData bindData) { return That.SingletonIf(concrete, out bindData); } /// public static bool SingletonIf(string service, Func concrete, out IBindData bindData) { return That.SingletonIf(service, concrete, out bindData); } /// public static IMethodBind BindMethod(string method, object target, MethodInfo call) { return That.BindMethod(method, target, call); } /// public static IMethodBind BindMethod(string method, object target, string call = null) { return That.BindMethod(method, target, call); } /// public static IMethodBind BindMethod(string method, Func callback) { return That.BindMethod(method, callback); } /// public static IMethodBind BindMethod(string method, Func callback) { return That.BindMethod(method, callback); } /// public static IMethodBind BindMethod(string method, Func callback) { return That.BindMethod(method, callback); } /// public static IMethodBind BindMethod(string method, Func callback) { return That.BindMethod(method, callback); } /// public static IMethodBind BindMethod(string method, Func callback) { return That.BindMethod(method, callback); } /// public static void Unbind(string service) { That.Unbind(service); } /// public static void Unbind() { That.Unbind(); } /// public static object[] Tagged(string tag) { return That.Tagged(tag); } /// public static void Tag(string tag, params string[] service) { That.Tag(tag, service); } /// public static void Tag(string tag) { That.Tag(tag); } /// public static object Instance(string service, object instance) { return That.Instance(service, instance); } /// public static void Instance(object instance) { That.Instance(instance); } /// public static bool Release(string service) { return That.Release(service); } /// public static bool Release() { return That.Release(); } /// public static bool Release(ref object[] instances, bool reverse = true) { return That.Release(ref instances, reverse); } /// public static object Call(object instance, MethodInfo methodInfo, params object[] userParams) { return That.Call(instance, methodInfo, userParams); } /// public static object Call(object instance, string method, params object[] userParams) { return That.Call(instance, method, userParams); } /// public static void Call(Action method, params object[] userParams) { That.Call(method, userParams); } /// public static void Call(Action method, params object[] userParams) { That.Call(method, userParams); } /// public static void Call(Action method, params object[] userParams) { That.Call(method, userParams); } /// public static void Call(Action method, params object[] userParams) { That.Call(method, userParams); } /// public static Action Wrap(Action method, params object[] userParams) { return That.Wrap(method, userParams); } /// public static Action Wrap(Action method, params object[] userParams) { return That.Wrap(method, userParams); } /// public static Action Wrap(Action method, params object[] userParams) { return That.Wrap(method, userParams); } /// public static Action Wrap(Action method, params object[] userParams) { return That.Wrap(method, userParams); } /// public static object Make(string service, params object[] userParams) { return That.Make(service, userParams); } /// public static TService Make(params object[] userParams) { #if CATLIB_PERFORMANCE return Facade.Make(userParams); #else return That.Make(userParams); #endif } /// public static object Make(Type type, params object[] userParams) { return That.Make(type, userParams); } /// public static Func Factory(string service, params object[] userParams) { return That.Factory(service, userParams); } /// public static Func Factory(params object[] userParams) { return That.Factory(userParams); } /// public static IContainer OnRelease(Action action) { return That.OnRelease(action); } /// public static IContainer OnRelease(Action callback) { return That.OnRelease(callback); } /// public static IContainer OnRelease(Action closure) { return That.OnRelease(closure); } /// public static IContainer OnRelease(Action closure) { return That.OnRelease(closure); } /// public static IContainer OnResolving(Action closure) { return That.OnResolving(closure); } /// public static IContainer OnResolving(Action callback) { return That.OnResolving(callback); } /// public static IContainer OnResolving(Action closure) { return That.OnResolving(closure); } /// public static IContainer OnResolving(Action closure) { return That.OnResolving(closure); } /// public static IContainer OnAfterResolving(Action closure) { return That.OnAfterResolving(closure); } /// public static IContainer OnAfterResolving(Action closure) { return That.OnAfterResolving(closure); } /// public static IContainer OnAfterResolving(Action closure) { return That.OnAfterResolving(closure); } /// public static IContainer OnAfterResolving(Action closure) { return That.OnAfterResolving(closure); } /// public static void Watch(Action method) { That.Watch(method); } /// public static void Watch(Action method) { That.Watch(method); } /// public static string Type2Service(Type type) { return That.Type2Service(type); } /// public static string Type2Service() { return That.Type2Service(); } } } ================================================ FILE: src/CatLib.Core/CatLib/Application.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using CatLib.EventDispatcher; using CatLib.Exception; using CatLib.Util; using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Threading; namespace CatLib { /// /// The CatLib instance. /// public class Application : Container.Container, IApplication { private static string version; private readonly IList loadedProviders; private readonly int mainThreadId; private readonly IDictionary dispatchMapping; private bool bootstrapped; private bool inited; private bool registering; private long incrementId; private DebugLevel debugLevel; private IEventDispatcher dispatcher; /// /// Initializes a new instance of the class. /// /// True if sets the instance to facade. public Application() { loadedProviders = new List(); mainThreadId = Thread.CurrentThread.ManagedThreadId; RegisterBaseBindings(); dispatchMapping = new Dictionary() { { typeof(AfterBootEventArgs), ApplicationEvents.OnAfterBoot }, { typeof(AfterInitEventArgs), ApplicationEvents.OnAfterInit }, { typeof(AfterTerminateEventArgs), ApplicationEvents.OnAfterTerminate }, { typeof(BeforeBootEventArgs), ApplicationEvents.OnBeforeBoot }, { typeof(BeforeInitEventArgs), ApplicationEvents.OnBeforeInit }, { typeof(BeforeTerminateEventArgs), ApplicationEvents.OnBeforeTerminate }, { typeof(BootingEventArgs), ApplicationEvents.OnBooting }, { typeof(InitProviderEventArgs), ApplicationEvents.OnInitProvider }, { typeof(RegisterProviderEventArgs), ApplicationEvents.OnRegisterProvider }, { typeof(StartCompletedEventArgs), ApplicationEvents.OnStartCompleted }, }; // We use closures to save the current context state // Do not change to: OnFindType(Type.GetType) This // causes the active assembly to be not the expected scope. OnFindType(finder => { return Type.GetType(finder); }); DebugLevel = DebugLevel.Production; Process = StartProcess.Construct; } /// /// Gets the CatLib version. /// public static string Version => version ?? (version = FileVersionInfo .GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion); /// /// Gets indicates the application startup process. /// public StartProcess Process { get; private set; } /// public bool IsMainThread => mainThreadId == Thread.CurrentThread.ManagedThreadId; /// public DebugLevel DebugLevel { get => debugLevel; set { debugLevel = value; this.Instance(debugLevel); } } /// /// The CatLib instance. public static Application New(bool global = true) { var application = new Application(); if (global) { App.That = application; } return application; } /// /// Sets the event dispatcher. /// /// The event dispatcher instance. public void SetDispatcher(IEventDispatcher dispatcher) { this.dispatcher = dispatcher; this.Instance(dispatcher); } /// public IEventDispatcher GetDispatcher() { return dispatcher; } /// public virtual void Terminate() { Process = StartProcess.Terminate; Raise(new BeforeTerminateEventArgs(this)); Process = StartProcess.Terminating; Flush(); if (App.That == this) { App.That = null; } Process = StartProcess.Terminated; Raise(new AfterTerminateEventArgs(this)); } /// /// Bootstrap the given array of bootstrap classes. /// /// The given bootstrap classes. public virtual void Bootstrap(params IBootstrap[] bootstraps) { Guard.Requires(bootstraps != null); if (bootstrapped || Process != StartProcess.Construct) { throw new LogicException($"Cannot repeatedly trigger the {nameof(Bootstrap)}()"); } Process = StartProcess.Bootstrap; bootstraps = Raise(new BeforeBootEventArgs(bootstraps, this)) .GetBootstraps(); Process = StartProcess.Bootstrapping; var existed = new HashSet(); foreach (var bootstrap in bootstraps) { if (bootstrap == null) { continue; } if (existed.Contains(bootstrap)) { throw new LogicException($"The bootstrap already exists : {bootstrap}"); } existed.Add(bootstrap); var skipped = Raise(new BootingEventArgs(bootstrap, this)) .IsSkip; if (!skipped) { bootstrap.Bootstrap(); } } Process = StartProcess.Bootstraped; bootstrapped = true; Raise(new AfterBootEventArgs(this)); } /// /// Init all of the registered service provider. /// public virtual void Init() { if (!bootstrapped) { throw new LogicException($"You must call {nameof(Bootstrap)}() first."); } if (inited || Process != StartProcess.Bootstraped) { throw new LogicException($"Cannot repeatedly trigger the {nameof(Init)}()"); } Process = StartProcess.Init; Raise(new BeforeInitEventArgs(this)); Process = StartProcess.Initing; foreach (var provider in loadedProviders) { InitProvider(provider); } inited = true; Process = StartProcess.Inited; Raise(new AfterInitEventArgs(this)); Process = StartProcess.Running; Raise(new StartCompletedEventArgs(this)); } /// public virtual void Register(IServiceProvider provider, bool force = false) { Guard.Requires(provider != null, $"Parameter \"{nameof(provider)}\" can not be null."); if (IsRegistered(provider)) { if (!force) { throw new LogicException($"Provider [{provider.GetType()}] is already register."); } loadedProviders.Remove(provider); } if (Process == StartProcess.Initing) { throw new LogicException($"Unable to add service provider during {nameof(StartProcess.Initing)}"); } if (Process > StartProcess.Running) { throw new LogicException($"Unable to {nameof(Terminate)} in-process registration service provider"); } if (provider is ServiceProvider baseProvider) { baseProvider.SetApplication(this); } var skipped = Raise(new RegisterProviderEventArgs(provider, this)) .IsSkip; if (skipped) { return; } try { registering = true; provider.Register(); } finally { registering = false; } loadedProviders.Add(provider); if (inited) { InitProvider(provider); } } /// public bool IsRegistered(IServiceProvider provider) { Guard.Requires(provider != null); return loadedProviders.Contains(provider); } /// public long GetRuntimeId() { return Interlocked.Increment(ref incrementId); } /// /// Initialize the specified service provider. /// /// The specified service provider. protected virtual void InitProvider(IServiceProvider provider) { Raise(new InitProviderEventArgs(provider, this)); provider.Init(); } /// protected override void GuardConstruct(string method) { if (registering) { throw new LogicException( $"It is not allowed to make services or dependency injection in the {nameof(Register)} process, method:{method}"); } base.GuardConstruct(method); } private void RegisterBaseBindings() { this.Singleton(() => this).Alias().Alias(); SetDispatcher(new EventDispatcher.EventDispatcher()); } private T Raise(T args) where T : EventArgs { if (!dispatchMapping.TryGetValue(args.GetType(), out string eventName)) { throw new AssertException($"Assertion error: Undefined event {args}"); } if (dispatcher == null) { return args; } dispatcher.Raise(eventName, this, args); return args; } } } ================================================ FILE: src/CatLib.Core/CatLib/DebugLevel.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Indicates the framework debug level. /// public enum DebugLevel { /// /// Production environment /// Production, /// /// Between the production environment and the developer environment /// Staging, /// /// Development environment /// Development, } } ================================================ FILE: src/CatLib.Core/CatLib/Events/AfterBootEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Indicates that the bootstrap has been booted. /// public class AfterBootEventArgs : ApplicationEventArgs { /// /// Initializes a new instance of the class. /// /// The application instance. public AfterBootEventArgs(IApplication application) : base(application) { } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/AfterInitEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Indicates that all the has been called. /// public class AfterInitEventArgs : ApplicationEventArgs { /// /// Initializes a new instance of the class. /// /// The application instance. public AfterInitEventArgs(IApplication application) : base(application) { } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/AfterTerminateEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Indicates that the framework will terminate. /// public class AfterTerminateEventArgs : ApplicationEventArgs { /// /// Initializes a new instance of the class. /// /// The terminate application instance. public AfterTerminateEventArgs(IApplication application) : base(application) { } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/ApplicationEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; namespace CatLib { /// /// Represents an application event. /// public class ApplicationEventArgs : EventArgs { /// /// Initializes a new instance of the class. /// /// The application instance. public ApplicationEventArgs(IApplication application) { Application = application; } /// /// Gets the application instance. /// public IApplication Application { get; private set; } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/ApplicationEvents.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Contains all events dispatched by an Application. /// public static class ApplicationEvents { /// /// Before the call. /// public static readonly string OnBeforeBoot = $"{BaseEventArgs}.OnBeforeBoot"; /// /// When the call is in progress. /// public static readonly string OnBooting = $"{BaseEventArgs}.BootingEventArgs"; /// /// After the called. /// public static readonly string OnAfterBoot = $"{BaseEventArgs}.AfterBootEventArgs"; /// /// When registering for a service provider. /// public static readonly string OnRegisterProvider = $"{BaseEventArgs}.RegisterProviderEventArgs"; /// /// Before the call. /// public static readonly string OnBeforeInit = $"{BaseEventArgs}.BeforeInitEventArgs"; /// /// Before the call. /// public static readonly string OnInitProvider = $"{BaseEventArgs}.InitProviderEventArgs"; /// /// After the called. /// public static readonly string OnAfterInit = $"{BaseEventArgs}.AfterInitEventArgs"; /// /// When the framework is started. /// public static readonly string OnStartCompleted = $"{BaseEventArgs}.StartCompletedEventArgs"; /// /// Before the call. /// public static readonly string OnBeforeTerminate = $"{BaseEventArgs}.BeforeTerminateEventArgs"; /// /// After the called. /// public static readonly string OnAfterTerminate = $"{BaseEventArgs}.AfterTerminateEventArgs"; private const string BaseEventArgs = "EventArgs.ApplicationEventArgs"; } } ================================================ FILE: src/CatLib.Core/CatLib/Events/BeforeBootEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// It indicates that the bootstrap will be bootstrapped. /// public class BeforeBootEventArgs : ApplicationEventArgs { private IBootstrap[] bootstraps; /// /// Initializes a new instance of the class. /// /// An array of the bootstrap list. /// The application instance. public BeforeBootEventArgs(IBootstrap[] bootstraps, IApplication application) : base(application) { this.bootstraps = bootstraps; } /// /// Gets an array of bootstrap will be bootstrapped. /// /// Returns an array of bootstraps. public IBootstrap[] GetBootstraps() { return bootstraps; } /// /// Sets the bootstrap will replace the old boot list. /// /// New bootstrap list. public void SetBootstraps(IBootstrap[] bootstraps) { this.bootstraps = bootstraps; } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/BeforeInitEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// It indicates that the method will be called. /// public class BeforeInitEventArgs : ApplicationEventArgs { /// /// Initializes a new instance of the class. /// /// An array of the bootstrap list. /// The application instance. public BeforeInitEventArgs(IApplication application) : base(application) { } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/BeforeTerminateEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Indicates that the framework will terminate. /// public class BeforeTerminateEventArgs : ApplicationEventArgs { /// /// Initializes a new instance of the class. /// /// The terminate application instance. public BeforeTerminateEventArgs(IApplication application) : base(application) { } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/BootingEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.EventDispatcher; namespace CatLib { /// /// Indicates a boot class that is booting. /// public class BootingEventArgs : ApplicationEventArgs, IStoppableEvent { private readonly IBootstrap bootstrap; /// /// Initializes a new instance of the class. /// /// The boot class that is booting. /// The application instance. public BootingEventArgs(IBootstrap bootstrap, IApplication application) : base(application) { IsSkip = false; this.bootstrap = bootstrap; } /// /// Gets a value indicating whether the boot class is skip booting. /// public bool IsSkip { get; private set; } /// public bool IsPropagationStopped => IsSkip; /// /// Gets the a boot class that is booting. /// /// Return the boot class. public IBootstrap GetBootstrap() { return bootstrap; } /// /// Disable the boot class. /// public void Skip() { IsSkip = true; } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/InitProviderEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Indicates a service provider that will inited. /// public class InitProviderEventArgs : ApplicationEventArgs { private readonly IServiceProvider provider; /// /// Initializes a new instance of the class. /// /// The service provider class that will inited. /// The application instance. public InitProviderEventArgs(IServiceProvider provider, IApplication application) : base(application) { this.provider = provider; } /// /// Gets the a service provider class that will inited. /// /// Return the service provider class. public IServiceProvider GetServiceProvider() { return provider; } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/RegisterProviderEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.EventDispatcher; namespace CatLib { /// /// Indicates a service provider that will register. /// public class RegisterProviderEventArgs : ApplicationEventArgs, IStoppableEvent { private readonly IServiceProvider provider; /// /// Initializes a new instance of the class. /// /// The service provider class that will register. /// The application instance. public RegisterProviderEventArgs(IServiceProvider provider, IApplication application) : base(application) { IsSkip = false; this.provider = provider; } /// /// Gets a value indicating whether the service provider is skip register. /// public bool IsSkip { get; private set; } /// public bool IsPropagationStopped => IsSkip; /// /// Gets the a service provider class that will register. /// /// Return the service provider class. public IServiceProvider GetServiceProvider() { return provider; } /// /// Skip the register service provider. /// public void Skip() { IsSkip = true; } } } ================================================ FILE: src/CatLib.Core/CatLib/Events/StartCompletedEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// Indicates that the framework ready. /// public class StartCompletedEventArgs : ApplicationEventArgs { /// /// Initializes a new instance of the class. /// /// The application instance. public StartCompletedEventArgs(IApplication application) : base(application) { } } } ================================================ FILE: src/CatLib.Core/CatLib/Facade.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable CA1000 #pragma warning disable S2743 #pragma warning disable S1118 using CatLib.Container; namespace CatLib { /// /// is the abstract implemented by all facade classes. /// /// The service type. /// /// public class FileSystem : Facade>IFileSystem<{ } /// public abstract class Facade { private static readonly string Service; private static TService that; private static IBindData binder; private static bool inited; private static bool released; /// /// Initializes static members of the class. /// #pragma warning disable S3963 static Facade() #pragma warning restore S3963 { Service = App.Type2Service(typeof(TService)); App.OnNewApplication += app => { that = default; binder = null; inited = false; released = false; }; } /// public static TService That => HasInstance ? that : Resolve(); /// /// Gets a value indicating whether the resolved instance is exists in the facade. /// If it is a non-static binding then return forever false. /// internal static bool HasInstance => binder != null && binder.IsStatic && !released && that != null; /// /// Resolve the object instance. /// /// The user parameters. /// The resolved object. internal static TService Make(params object[] userParams) { return HasInstance ? that : Resolve(userParams); } /// private static TService Resolve(params object[] userParams) { released = false; if (!inited && (App.IsResolved(Service) || App.CanMake(Service))) { App.Watch(ServiceRebound); inited = true; } else if (binder != null && !binder.IsStatic) { // If it has been initialized, the binder has been initialized. // Then judging in advance can optimize performance without // going through a hash lookup. return Build(userParams); } var newBinder = App.GetBind(Service); if (newBinder == null || !newBinder.IsStatic) { binder = newBinder; return Build(userParams); } Rebind(newBinder); return that = Build(userParams); } /// /// When the resolved object is released. /// /// The old bind data with resolved object. /// The ignored parameter. private static void OnRelease(IBindData oldBinder, object instance) { if (oldBinder != binder) { return; } that = default; released = true; } /// /// When the resolved object is rebound. /// /// The new resolved object. private static void ServiceRebound(TService newService) { var newBinder = App.GetBind(Service); Rebind(newBinder); that = (newBinder == null || !newBinder.IsStatic) ? default : newService; } /// /// Rebinding the bound data to given binder. /// /// The new binder. private static void Rebind(IBindData newBinder) { if (newBinder != null && binder != newBinder && newBinder.IsStatic) { newBinder.OnRelease(OnRelease); } binder = newBinder; } /// /// Resolve facade object from the container. /// /// The user parameters. /// The resolved object. private static TService Build(params object[] userParams) { return (TService)App.Make(Service, userParams); } } } ================================================ FILE: src/CatLib.Core/CatLib/IApplication.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this sender code. * * Document: https://catlib.io/ */ using CatLib.Container; using CatLib.EventDispatcher; namespace CatLib { /// /// is the interface implemented by all application classes. /// public interface IApplication : IContainer { /// /// Gets a value indicating whether true if we're on the main thread. /// bool IsMainThread { get; } /// /// Gets or sets the debug level. /// DebugLevel DebugLevel { get; set; } /// /// Gets the event dispatcher. /// /// Returns event dispatcher instance, null if the dispatcher not found. IEventDispatcher GetDispatcher(); /// /// Register a service provider with the application. /// /// The service provider. /// True if the force register. void Register(IServiceProvider provider, bool force = false); /// /// Checks whether the given service provider is registered. /// /// The service provider. /// True if the service provider is registered. bool IsRegistered(IServiceProvider provider); /// /// Gets the unique runtime id. /// /// The unique runtime id. long GetRuntimeId(); /// /// Terminates the . /// void Terminate(); } } ================================================ FILE: src/CatLib.Core/CatLib/IBootstrap.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// is the interface implemented by all bootstrap classes. /// public interface IBootstrap { /// /// Bootstrap the framework. /// void Bootstrap(); } } ================================================ FILE: src/CatLib.Core/CatLib/IServiceProvider.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// is the interface implemented by all service provider classes. /// public interface IServiceProvider { /// /// Initialize the application's service providers. /// void Init(); /// /// Register any application services. /// void Register(); } } ================================================ FILE: src/CatLib.Core/CatLib/ServiceProvider.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Diagnostics.CodeAnalysis; namespace CatLib { /// /// is default service provider class /// for all concrete ServiceProvider classes. /// [ExcludeFromCodeCoverage] public abstract class ServiceProvider : IServiceProvider { /// /// Gets application instance. /// protected IApplication App { get; private set; } /// public virtual void Init() { } /// public virtual void Register() { } internal void SetApplication(IApplication application) { App = application; } } } ================================================ FILE: src/CatLib.Core/CatLib/StartProcess.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib { /// /// The framework start process type. /// public enum StartProcess { /// /// When you create a new , /// you are in the phase. /// Construct = 0, /// /// Before the call. /// Bootstrap = 1, /// /// When during execution, /// you are in the phase. /// Bootstrapping = 2, /// /// After the called. /// Bootstraped = 3, /// /// Before the call. /// Init = 4, /// /// When during execution, /// you are in the phase. /// Initing = 5, /// /// After the called. /// Inited = 6, /// /// When the framework running. /// Running = 7, /// /// Before the call. /// Terminate = 8, /// /// When during execution, /// you are in the phase. /// Terminating = 9, /// /// After the called. /// All resources are destroyed. /// Terminated = 10, } } ================================================ FILE: src/CatLib.Core/CatLib.Core.csproj ================================================  netstandard2.0 2.0.0 true CatLib CatLib CatLib lightweight dependency injection container MIT https://github.com/CatLib/Core https://github.com/CatLib/Core github catlib, ioc, inject, framework ================================================ FILE: src/CatLib.Core/Container/BindData.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.Collections.Generic; using CatLibContainer = CatLib.Container.Container; namespace CatLib.Container { /// public sealed class BindData : Bindable, IBindData { /// /// The local resolving callbacks. /// private List> resolving; /// /// The local after resolving callbacks. /// private List> afterResolving; /// /// The local release callbacks. /// private List> release; /// /// Initializes a new instance of the class. /// /// The container instance. /// The service name. /// The service concrete. /// Whether the service is singleton(static). public BindData(CatLibContainer container, string service, Func concrete, bool isStatic) : base(container, service) { Concrete = concrete; IsStatic = isStatic; } /// public Func Concrete { get; } /// public bool IsStatic { get; } /// public IBindData Alias(string alias) { AssertDestroyed(); Guard.ParameterNotNull(alias, nameof(alias)); Container.Alias(alias, Service); return this; } /// public IBindData Tag(string tag) { AssertDestroyed(); Guard.ParameterNotNull(tag, nameof(tag)); Container.Tag(tag, Service); return this; } /// public IBindData OnResolving(Action closure) { AddClosure(closure, ref resolving); return this; } /// public IBindData OnAfterResolving(Action closure) { AddClosure(closure, ref afterResolving); return this; } /// public IBindData OnRelease(Action closure) { if (!IsStatic) { throw new LogicException( $"Service [{Service}] is not Singleton(Static) Bind , Can not call {nameof(OnRelease)}()."); } AddClosure(closure, ref release); return this; } internal object TriggerResolving(object instance) { return CatLibContainer.Trigger(this, instance, resolving); } internal object TriggerAfterResolving(object instance) { return CatLibContainer.Trigger(this, instance, afterResolving); } internal object TriggerRelease(object instance) { return CatLibContainer.Trigger(this, instance, release); } /// protected override void ReleaseBind() { ((CatLibContainer)Container).Unbind(this); } private void AddClosure(Action closure, ref List> collection) { Guard.Requires(closure != null); AssertDestroyed(); if (collection == null) { collection = new List>(); } collection.Add(closure); } } } ================================================ FILE: src/CatLib.Core/Container/BindDataExtension.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Util; using System; namespace CatLib.Container { /// /// An extension function for . /// public static class BindDataExtension { /// /// The type convert to alias name. public static IBindData Alias(this IBindData bindData) { return bindData.Alias(bindData.Container.Type2Service(typeof(TAlias))); } /// public static IBindData OnResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnResolving((_, instance) => { closure(); }); } /// public static IBindData OnResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnResolving((_, instance) => { closure(instance); }); } /// /// The type of resolve instance. public static IBindData OnResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnResolving((_, instance) => { if (instance is T) { closure((T)instance); } }); } /// public static IBindData OnResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnResolving((bind, instance) => { if (instance is T) { closure(bind, (T)instance); } }); } /// public static IBindData OnAfterResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnAfterResolving((_, instance) => { closure(); }); } /// public static IBindData OnAfterResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnAfterResolving((_, instance) => { closure(instance); }); } /// /// The type of resolve instance. public static IBindData OnAfterResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnAfterResolving((_, instance) => { if (instance is T) { closure((T)instance); } }); } /// public static IBindData OnAfterResolving(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnAfterResolving((bind, instance) => { if (instance is T) { closure(bind, (T)instance); } }); } /// public static IBindData OnRelease(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnRelease((_, __) => { closure(); }); } /// public static IBindData OnRelease(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnRelease((_, instance) => { closure(instance); }); } /// /// The type of release instance. public static IBindData OnRelease(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnRelease((_, instance) => { if (instance is T) { closure((T)instance); } }); } /// public static IBindData OnRelease(this IBindData bindData, Action closure) { Guard.Requires(closure != null); return bindData.OnRelease((bind, instance) => { if (instance is T) { closure(bind, (T)instance); } }); } } } ================================================ FILE: src/CatLib.Core/Container/Bindable.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.Collections.Generic; namespace CatLib.Container { /// /// The bindable data indicates relational data related to the specified service. /// public abstract class Bindable : IBindable { private readonly Container container; private Dictionary contextual; private Dictionary> contextualClosure; private bool isDestroy; /// /// Initializes a new instance of the class. /// /// The container instance. /// The service name. protected Bindable(Container container, string service) { this.container = container; Service = service; isDestroy = false; } /// public string Service { get; } /// /// Gets the container to which the service belongs. /// public IContainer Container => container; /// public void Unbind() { isDestroy = true; ReleaseBind(); } /// /// Add the context with service. /// /// Demand specified service. /// Given speified service or alias. internal void AddContextual(string needs, string given) { AssertDestroyed(); if (contextual == null) { contextual = new Dictionary(); } if (contextual.ContainsKey(needs) || (contextualClosure != null && contextualClosure.ContainsKey(needs))) { throw new LogicException($"Needs [{needs}] is already exist."); } contextual.Add(needs, given); } /// /// The closure return the given service instance. internal void AddContextual(string needs, Func given) { AssertDestroyed(); if (contextualClosure == null) { contextualClosure = new Dictionary>(); } if (contextualClosure.ContainsKey(needs) || (contextual != null && contextual.ContainsKey(needs))) { throw new LogicException($"Needs [{needs}] is already exist."); } contextualClosure.Add(needs, given); } /// /// Get the demand context of the service. /// /// The demand service. /// The given service or alias. internal string GetContextual(string needs) { if (contextual == null) { return null; } return contextual.TryGetValue(needs, out string contextualNeeds) ? contextualNeeds : null; } /// /// The closure return the given service instance. internal Func GetContextualClosure(string needs) { if (contextualClosure == null) { return null; } return contextualClosure.TryGetValue(needs, out Func closure) ? closure : null; } /// protected abstract void ReleaseBind(); /// /// Verify that the current instance i has been released. /// protected void AssertDestroyed() { if (isDestroy) { throw new LogicException("The current instance is destroyed."); } } } #pragma warning disable SA1402 /// public abstract class Bindable : Bindable, IBindable where TReturn : class, IBindable { /// /// Indicates the given relationship in the context. /// private GivenData given; /// /// Initializes a new instance of the class. /// /// The container instance. /// The service name. protected Bindable(Container container, string service) : base(container, service) { } /// public IGivenData Needs(string service) { Guard.ParameterNotNull(service, nameof(service)); AssertDestroyed(); if (given == null) { given = new GivenData((Container)Container, this); } given.Needs(service); return given; } /// public IGivenData Needs() { return Needs(Container.Type2Service(typeof(TService))); } } } ================================================ FILE: src/CatLib.Core/Container/Container.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.ExceptionServices; using System.Text; using SException = System.Exception; namespace CatLib.Container { /// /// The catlib ioc container implemented. /// public class Container : IContainer { /// /// Characters not allowed in the service name. /// private static readonly char[] ServiceBanChars = { '@', ':', '$' }; /// /// The container's bindings. /// private readonly Dictionary bindings; /// /// The container's singleton(static) instances. /// private readonly Dictionary instances; /// /// The container's singleton instances inverse index mapping. /// private readonly Dictionary instancesReverse; /// /// The registered aliases with service. /// private readonly Dictionary aliases; /// /// The registered aliases with service. inverse index mapping. /// private readonly Dictionary> aliasesReverse; /// /// All of the registered tags. /// private readonly Dictionary> tags; /// /// All of the global resolving callbacks. /// private readonly List> resolving; /// /// All of the global after resolving callbacks. /// private readonly List> afterResloving; /// /// All of the global release callbacks. /// private readonly List> release; /// /// The extension closures for services. /// private readonly Dictionary>> extenders; /// /// The type finder convert a string to a service type. /// private readonly SortSet, int> findType; /// /// The cache that has been type found. /// private readonly Dictionary findTypeCache; /// /// An hash set of the service that have been resolved. /// private readonly HashSet resolved; /// /// The singleton service build timing. /// private readonly SortSet instanceTiming; /// /// All of the registered rebound callbacks. /// private readonly Dictionary>> rebound; /// /// The method ioc container. /// private readonly MethodContainer methodContainer; /// /// Represents a skipped object to skip some dependency injection. /// private readonly object skipped; /// /// Whether the container is flushing. /// private bool flushing; /// /// The unique Id is used to mark the global build order. /// private int instanceId; /// /// Initializes a new instance of the class. /// /// The estimated number of services. public Container(int prime = 64) { prime = Math.Max(8, prime); tags = new Dictionary>((int)(prime * 0.25)); aliases = new Dictionary(prime * 4); aliasesReverse = new Dictionary>(prime * 4); instances = new Dictionary(prime * 4); instancesReverse = new Dictionary(prime * 4); bindings = new Dictionary(prime * 4); resolving = new List>((int)(prime * 0.25)); afterResloving = new List>((int)(prime * 0.25)); release = new List>((int)(prime * 0.25)); extenders = new Dictionary>>((int)(prime * 0.25)); resolved = new HashSet(); findType = new SortSet, int>(); findTypeCache = new Dictionary(prime * 4); rebound = new Dictionary>>(prime); instanceTiming = new SortSet(); BuildStack = new Stack(32); UserParamsStack = new Stack(32); skipped = new object(); methodContainer = new MethodContainer(this); flushing = false; instanceId = 0; } /// /// Gets the stack of concretions currently being built. /// protected Stack BuildStack { get; } /// /// Gets the stack of the user params being built. /// protected Stack UserParamsStack { get; } /// public object this[string service] { get => Make(service); set { GetBind(service)?.Unbind(); Bind(service, (container, args) => value, false); } } /// public void Tag(string tag, params string[] services) { Guard.ParameterNotNull(tag, nameof(tag)); GuardFlushing(); if (!tags.TryGetValue(tag, out List collection)) { tags[tag] = collection = new List(); } foreach (var service in services ?? Array.Empty()) { if (string.IsNullOrEmpty(service)) { continue; } collection.Add(service); } } /// public object[] Tagged(string tag) { Guard.ParameterNotNull(tag, nameof(tag)); if (!tags.TryGetValue(tag, out List services)) { throw new LogicException($"Tag \"{tag}\" is not exist."); } return Arr.Map(services, (service) => Make(service)); } /// public IBindData GetBind(string service) { if (string.IsNullOrEmpty(service)) { return null; } service = AliasToService(service); return bindings.TryGetValue(service, out BindData bindData) ? bindData : null; } /// public bool HasBind(string service) { return GetBind(service) != null; } /// public bool HasInstance(string service) { Guard.ParameterNotNull(service, nameof(service)); service = AliasToService(service); return instances.ContainsKey(service); } /// public bool IsResolved(string service) { Guard.ParameterNotNull(service, nameof(service)); service = AliasToService(service); return resolved.Contains(service) || instances.ContainsKey(service); } /// public bool CanMake(string service) { Guard.ParameterNotNull(service, nameof(service)); service = AliasToService(service); if (HasBind(service) || HasInstance(service)) { return true; } var type = SpeculatedServiceType(service); return !IsBasicType(type) && !IsUnableType(type); } /// public bool IsStatic(string service) { var bind = GetBind(service); return bind != null && bind.IsStatic; } /// public bool IsAlias(string name) { name = FormatService(name); return aliases.ContainsKey(name); } /// public IContainer Alias(string alias, string service) { Guard.ParameterNotNull(alias, nameof(alias)); Guard.ParameterNotNull(service, nameof(service)); if (alias == service) { throw new LogicException($"Alias is same as service: \"{alias}\"."); } GuardFlushing(); alias = FormatService(alias); service = AliasToService(service); if (aliases.ContainsKey(alias)) { throw new LogicException($"Alias \"{alias}\" is already exists."); } if (bindings.ContainsKey(alias)) { throw new LogicException($"Alias \"{alias}\" has been used for service name."); } if (!bindings.ContainsKey(service) && !instances.ContainsKey(service)) { throw new LogicException( $"You must {nameof(Bind)}() or {nameof(Instance)}() serivce before and you be able to called {nameof(Alias)}()."); } aliases.Add(alias, service); if (!aliasesReverse.TryGetValue(service, out List collection)) { aliasesReverse[service] = collection = new List(); } collection.Add(alias); return this; } /// public bool BindIf(string service, Func concrete, bool isStatic, out IBindData bindData) { var bind = GetBind(service); if (bind == null && (HasInstance(service) || IsAlias(service))) { bindData = null; return false; } bindData = bind ?? Bind(service, concrete, isStatic); return bind == null; } /// public bool BindIf(string service, Type concrete, bool isStatic, out IBindData bindData) { if (!IsUnableType(concrete)) { service = FormatService(service); return BindIf(service, WrapperTypeBuilder(service, concrete), isStatic, out bindData); } bindData = null; return false; } /// public IBindData Bind(string service, Type concrete, bool isStatic) { Guard.Requires(concrete != null, $"Parameter {nameof(concrete)} can not be null."); if (IsUnableType(concrete)) { throw new LogicException($"Type \"{concrete}\" can not bind. please check if there is a list of types that cannot be built."); } service = FormatService(service); return Bind(service, WrapperTypeBuilder(service, concrete), isStatic); } /// public IBindData Bind(string service, Func concrete, bool isStatic) { Guard.ParameterNotNull(service, nameof(service)); Guard.ParameterNotNull(concrete, nameof(concrete)); GuardServiceName(service); GuardFlushing(); service = FormatService(service); if (bindings.ContainsKey(service)) { throw new LogicException($"Bind [{service}] already exists."); } if (instances.ContainsKey(service)) { throw new LogicException($"Instances [{service}] is already exists."); } if (aliases.ContainsKey(service)) { throw new LogicException($"Aliase [{service}] is already exists."); } var bindData = new BindData(this, service, concrete, isStatic); bindings.Add(service, bindData); if (!IsResolved(service)) { return bindData; } if (isStatic) { // If it is "static" then solve this service directly // The process of staticizing the service triggers TriggerOnRebound Make(service); } else { TriggerOnRebound(service); } return bindData; } /// public IMethodBind BindMethod(string method, object target, MethodInfo called) { GuardFlushing(); GuardMethodName(method); return methodContainer.Bind(method, target, called); } /// public void UnbindMethod(object target) { methodContainer.Unbind(target); } /// public object Invoke(string method, params object[] userParams) { GuardConstruct(nameof(Invoke)); return methodContainer.Invoke(method, userParams); } /// public object Call(object target, MethodInfo methodInfo, params object[] userParams) { Guard.Requires(methodInfo != null); if (!methodInfo.IsStatic) { Guard.Requires(target != null); } GuardConstruct(nameof(Call)); var parameter = methodInfo.GetParameters(); var bindData = GetBindFillable(target != null ? Type2Service(target.GetType()) : null); userParams = GetDependencies(bindData, parameter, userParams) ?? Array.Empty(); return methodInfo.Invoke(target, userParams); } /// public object Make(string service, params object[] userParams) { GuardConstruct(nameof(Make)); return Resolve(service, userParams); } /// public void Extend(string service, Func closure) { Guard.Requires(closure != null); GuardFlushing(); service = string.IsNullOrEmpty(service) ? string.Empty : AliasToService(service); if (!string.IsNullOrEmpty(service) && instances.TryGetValue(service, out object instance)) { // If the instance already exists then apply the extension. // Extensions will no longer be added to the permanent extension list. var old = instance; instances[service] = instance = closure(instance, this); if (!old.Equals(instance)) { instancesReverse.Remove(old); instancesReverse.Add(instance, service); } TriggerOnRebound(service, instance); return; } if (!extenders.TryGetValue(service, out List> extender)) { extenders[service] = extender = new List>(); } extender.Add(closure); if (!string.IsNullOrEmpty(service) && IsResolved(service)) { TriggerOnRebound(service); } } /// /// Remove all extensions for the specified service. /// /// The service name or alias. public void ClearExtenders(string service) { GuardFlushing(); service = AliasToService(service); extenders.Remove(service); if (!IsResolved(service)) { return; } Release(service); TriggerOnRebound(service); } /// public object Instance(string service, object instance) { Guard.ParameterNotNull(service, nameof(service)); GuardFlushing(); GuardServiceName(service); service = AliasToService(service); var bindData = GetBind(service); if (bindData != null) { if (!bindData.IsStatic) { throw new LogicException($"Service [{service}] is not Singleton(Static) Bind."); } } else { bindData = MakeEmptyBindData(service); } instance = TriggerOnResolving((BindData)bindData, instance); if (instance != null && instancesReverse.TryGetValue(instance, out string realService) && realService != service) { throw new LogicException($"The instance has been registered as a singleton in {realService}"); } var isResolved = IsResolved(service); Release(service); instances.Add(service, instance); if (instance != null) { instancesReverse.Add(instance, service); } if (!instanceTiming.Contains(service)) { instanceTiming.Add(service, instanceId++); } if (isResolved) { TriggerOnRebound(service, instance); } return instance; } /// public bool Release(object mixed) { if (mixed == null) { return false; } string service; object instance = null; if (!(mixed is string)) { service = GetServiceWithInstanceObject(mixed); } else { service = AliasToService(mixed.ToString()); if (!instances.TryGetValue(service, out instance)) { // Prevent the use of a string as a service name. service = GetServiceWithInstanceObject(mixed); } } if (instance == null && (string.IsNullOrEmpty(service) || !instances.TryGetValue(service, out instance))) { return false; } var bindData = GetBindFillable(service); bindData.TriggerRelease(instance); TriggerOnRelease(bindData, instance); if (instance != null) { DisposeInstance(instance); instancesReverse.Remove(instance); } instances.Remove(service); if (!HasOnReboundCallbacks(service)) { instanceTiming.Remove(service); } return true; } /// public IContainer OnFindType(Func func, int priority = int.MaxValue) { Guard.Requires(func != null); GuardFlushing(); findType.Add(func, priority); return this; } /// public IContainer OnRelease(Action closure) { AddClosure(closure, release); return this; } /// public IContainer OnResolving(Action closure) { AddClosure(closure, resolving); return this; } /// public IContainer OnAfterResolving(Action closure) { AddClosure(closure, afterResloving); return this; } /// public IContainer OnRebound(string service, Action callback) { Guard.Requires(callback != null); GuardFlushing(); service = AliasToService(service); if (!IsResolved(service) && !CanMake(service)) { throw new LogicException( $"If you want use Rebound(Watch) , please {nameof(Bind)} or {nameof(Instance)} service first."); } if (!rebound.TryGetValue(service, out List> list)) { rebound[service] = list = new List>(); } list.Add(callback); return this; } /// public void Unbind(string service) { service = AliasToService(service); var bind = GetBind(service); bind?.Unbind(); } /// public virtual void Flush() { try { flushing = true; foreach (var service in instanceTiming.GetIterator(false)) { Release(service); } Guard.Requires(instances.Count <= 0); tags.Clear(); aliases.Clear(); aliasesReverse.Clear(); instances.Clear(); bindings.Clear(); resolving.Clear(); release.Clear(); extenders.Clear(); resolved.Clear(); findType.Clear(); findTypeCache.Clear(); BuildStack.Clear(); UserParamsStack.Clear(); rebound.Clear(); methodContainer.Flush(); instanceTiming.Clear(); instanceId = 0; } finally { flushing = false; } } /// public string Type2Service(Type type) { return type.ToString(); } /// /// Trigger all callbacks in specified list. /// /// The bind data for service. /// The service instance. /// The specified list. /// The decorated service instance. internal static object Trigger(IBindData bindData, object instance, List> list) { if (list == null) { return instance; } foreach (var closure in list) { closure(bindData, instance); } return instance; } /// /// Unbind the service from the container. /// /// The bindable instance. internal void Unbind(IBindable bindable) { GuardFlushing(); Release(bindable.Service); if (aliasesReverse.TryGetValue(bindable.Service, out List serviceList)) { foreach (var alias in serviceList) { aliases.Remove(alias); } aliasesReverse.Remove(bindable.Service); } bindings.Remove(bindable.Service); } /// /// Gets an array of resolved instances for dependent parameters. /// /// The bind data for service. /// The dependent parameters array for service. /// An array for the user parameter. /// An array of resolved instances for dependent parameters. protected internal virtual object[] GetDependencies(Bindable makeServiceBindData, ParameterInfo[] baseParams, object[] userParams) { if (baseParams.Length <= 0) { return Array.Empty(); } var results = new object[baseParams.Length]; // Gets a parameter matcher for filtering parameters var matcher = GetParamsMatcher(ref userParams); for (var i = 0; i < baseParams.Length; i++) { var baseParam = baseParams[i]; // Parameter matching is used to match the parameters. // The parameter matchers are the first to perform because their // matching accuracy is the most accurate. var param = matcher?.Invoke(baseParam); // When the container finds that the developer uses object or object[] as // the dependency parameter type, we try to compact inject the user parameters. param = param ?? GetCompactInjectUserParams(baseParam, ref userParams); // Select the appropriate parameters from the user parameters and inject // them in the relative order. param = param ?? GetDependenciesFromUserParams(baseParam, ref userParams); string needService = null; if (param == null) { // Try to generate the required parameters through the dependency // injection container. needService = GetParamNeedsService(baseParam); if (baseParam.ParameterType.IsClass || baseParam.ParameterType.IsInterface) { param = ResloveClass(makeServiceBindData, needService, baseParam); } else { param = ResolvePrimitive(makeServiceBindData, needService, baseParam); } } // Perform dependency injection checking on the obtained injection instance. if (!CanInject(baseParam.ParameterType, param)) { var error = $"[{makeServiceBindData.Service}] Params inject type must be [{baseParam.ParameterType}] , But instance is [{param?.GetType()}]"; if (needService == null) { error += " Inject params from user incoming parameters."; } else { error += $" Make service is [{needService}]."; } throw new UnresolvableException(error); } results[i] = param; } return results; } /// /// Determine if specified type is the default base type of the container. /// /// The specified type. /// True if the specified type is the default base type. otherwise false. protected virtual bool IsBasicType(Type type) { return type == null || type.IsPrimitive || type == typeof(string); } /// /// Determine the specified type is cannot built. /// /// The specified type. /// True if the specified type is cannot built. otherwise false. protected virtual bool IsUnableType(Type type) { return type == null || type.IsAbstract || type.IsInterface || type.IsArray || type.IsEnum || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)); } /// /// Wrap a specified type. /// /// The service name. /// The service concrete type. /// Return a closure, call it to get the service instance. protected virtual Func WrapperTypeBuilder(string service, Type concrete) { return (container, userParams) => ((Container)container).CreateInstance(GetBindFillable(service), concrete, userParams); } /// /// Get dependencies from the user parameters. /// /// The depend type. /// The user parameters. /// The instance that match the type of dependency. protected virtual object GetDependenciesFromUserParams(ParameterInfo baseParam, ref object[] userParams) { if (userParams == null) { return null; } GuardUserParamsCount(userParams.Length); for (var n = 0; n < userParams.Length; n++) { var userParam = userParams[n]; if (!ChangeType(ref userParam, baseParam.ParameterType)) { continue; } Arr.RemoveAt(ref userParams, n); return userParam; } return null; } /// /// Convert instance to specified type. /// /// The instance. /// The specified type. /// True if the conversion was successful, otherwise false. protected virtual bool ChangeType(ref object result, Type conversionType) { try { if (result == null || conversionType.IsInstanceOfType(result)) { return true; } if (IsBasicType(result.GetType()) && conversionType.IsDefined(typeof(VariantAttribute), false)) { try { result = Make(Type2Service(conversionType), result); return true; } #pragma warning disable CA1031 catch (SException) #pragma warning restore CA1031 { // ignored // when throw exception then stop inject } } if (result is IConvertible && typeof(IConvertible).IsAssignableFrom(conversionType)) { result = Convert.ChangeType(result, conversionType); return true; } } #pragma warning disable CA1031 catch (SException) #pragma warning restore CA1031 { // ignored // when throw exception then stop inject } return false; } /// /// Convert to the service name. /// /// The property. /// The service name. protected virtual string GetPropertyNeedsService(PropertyInfo propertyInfo) { return Type2Service(propertyInfo.PropertyType); } /// /// Convert to the service name. /// /// The parameter. /// The service name. protected virtual string GetParamNeedsService(ParameterInfo baseParam) { return Type2Service(baseParam.ParameterType); } /// /// Gets build closures based on context. /// /// The bind data for the service. /// The service name for dependent. /// The parameter name for dependent. /// The closure, call returned the dependency instance. protected virtual Func GetContextualClosure(Bindable makeServiceBindData, string service, string paramName) { return makeServiceBindData.GetContextualClosure(service) ?? makeServiceBindData.GetContextualClosure($"{GetVariableTag()}{paramName}"); } /// /// /// Get build service based on context. /// /// The dependency service name. protected virtual string GetContextualService(Bindable makeServiceBindData, string service, string paramName) { return makeServiceBindData.GetContextual(service) ?? makeServiceBindData.GetContextual($"{GetVariableTag()}{paramName}") ?? service; } /// /// Gets the instance from closure. /// /// The closure. /// The expected type. /// The instance. /// True if the build is successful and matches the expected type, otherwise false. protected virtual bool MakeFromContextualClosure(Func closure, Type needType, out object ouput) { ouput = null; if (closure == null) { return false; } ouput = closure(); return ChangeType(ref ouput, needType); } /// /// /// Gets the instance from service name. /// /// The service name. protected virtual bool MakeFromContextualService(string service, Type needType, out object output) { output = null; if (!CanMake(service)) { return false; } output = Make(service); return ChangeType(ref output, needType); } /// /// Resolve the specified service based on context. /// /// The bind data for the service. /// The service name for dependent. /// The parameter or property name for dependent. /// The parameter or property type for dependent. /// The dependency instance. /// True if build the dependency instance successful. otherwise false. protected virtual bool ResloveFromContextual(Bindable makeServiceBindData, string service, string paramName, Type paramType, out object output) { if (MakeFromContextualClosure( GetContextualClosure(makeServiceBindData, service, paramName), paramType, out output)) { return true; } return MakeFromContextualService( GetContextualService(makeServiceBindData, service, paramName), paramType, out output); } /// /// Resolved the attribute selector's primitive type. /// /// The bind data for the service. /// The name or alias of the service dependent needs to resolved. /// The property for dependent. /// The dependency instance. protected virtual object ResolveAttrPrimitive(Bindable makeServiceBindData, string service, PropertyInfo baseParam) { if (ResloveFromContextual(makeServiceBindData, service, baseParam.Name, baseParam.PropertyType, out object instance)) { return instance; } if (baseParam.PropertyType.IsGenericType && baseParam.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { return null; } var inject = (InjectAttribute)baseParam.GetCustomAttribute(typeof(InjectAttribute)); if (inject != null && !inject.Required) { return skipped; } throw MakeUnresolvableException(baseParam.Name, baseParam.DeclaringType); } /// /// /// Resolved the attribute selector's reference type. /// protected virtual object ResloveAttrClass(Bindable makeServiceBindData, string service, PropertyInfo baseParam) { if (ResloveFromContextual(makeServiceBindData, service, baseParam.Name, baseParam.PropertyType, out object instance)) { return instance; } var inject = (InjectAttribute)baseParam.GetCustomAttribute(typeof(InjectAttribute)); if (inject != null && !inject.Required) { return skipped; } throw MakeUnresolvableException(baseParam.Name, baseParam.DeclaringType); } /// /// Resolved the constructor's primitive type. /// /// The bind data for the service. /// The name or alias of the service dependent needs to resolved. /// The parameter for dependent. /// The dependency instance. protected virtual object ResolvePrimitive(Bindable makeServiceBindData, string service, ParameterInfo baseParam) { if (ResloveFromContextual(makeServiceBindData, service, baseParam.Name, baseParam.ParameterType, out object instance)) { return instance; } if (baseParam.IsOptional) { return baseParam.DefaultValue; } if (baseParam.ParameterType.IsGenericType && baseParam.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>)) { return null; } throw MakeUnresolvableException( baseParam.Name, baseParam.Member?.DeclaringType); } /// /// /// Resolved the constructor's reference type. /// protected virtual object ResloveClass(Bindable makeServiceBindData, string service, ParameterInfo baseParam) { if (ResloveFromContextual(makeServiceBindData, service, baseParam.Name, baseParam.ParameterType, out object instance)) { return instance; } if (baseParam.IsOptional) { return baseParam.DefaultValue; } // baseParam.Member maybe empty and may occur when some underlying // development overwrites ParameterInfo class. throw MakeUnresolvableException( baseParam.Name, baseParam.Member?.DeclaringType); } /// /// Gets variable characters. /// /// The variable characters. protected virtual char GetVariableTag() { return '$'; } /// /// Gets the debug message of the build stack. /// /// The debug message of the build stack. protected virtual string GetBuildStackDebugMessage() { var previous = string.Join(", ", BuildStack.ToArray()); return $" While building stack [{previous}]."; } /// /// Build a reslove failure exception. /// /// The service name. /// The service type. /// The inner exception. /// The resolve failure exception instance. protected virtual UnresolvableException MakeBuildFaildException(string makeService, Type makeServiceType, SException innerException) { var message = makeServiceType != null ? $"Class [{makeServiceType}] build faild. Service is [{makeService}]." : $"Service [{makeService}] is not exists."; message += GetBuildStackDebugMessage(); message += GetInnerExceptionMessage(innerException); return new UnresolvableException(message, innerException); } /// /// Gets the inner exception debug message. /// /// The inner exception. /// The debug message. protected virtual string GetInnerExceptionMessage(SException innerException) { if (innerException == null) { return string.Empty; } var stack = new StringBuilder(); do { if (stack.Length > 0) { stack.Append(", "); } stack.Append(innerException); } while ((innerException = innerException.InnerException) != null); return $" InnerException message stack: [{stack}]"; } /// /// Build a unresolved exception. /// /// The parameter or property name for dependent. /// Declaring class type for parameter or property. /// The unresolved exception instance. protected virtual UnresolvableException MakeUnresolvableException(string name, Type declaringClass) { return new UnresolvableException( $"Unresolvable dependency , resolving [{name ?? "Unknow"}] in class [{declaringClass?.ToString() ?? "Unknow"}]"); } /// /// Build a circular dependency exception. /// /// The name of the service that throws the exception. /// The circular dependency exception. protected virtual LogicException MakeCircularDependencyException(string service) { var message = $"Circular dependency detected while for [{service}]."; message += GetBuildStackDebugMessage(); return new LogicException(message); } /// /// Format the service name. /// /// The service name. /// The formatted service name. protected virtual string FormatService(string service) { return service.Trim(); } /// /// Check if the specified instance can be injected. /// /// The expected type. /// The specified instance. /// True if the instance can be injected. otherwise false. protected virtual bool CanInject(Type type, object instance) { return instance == null || type.IsInstanceOfType(instance); } /// /// Ensure that the number of parameters passed in by the user must be less than the specified value. /// /// The specified count. protected virtual void GuardUserParamsCount(int count) { if (count > 255) { throw new LogicException($"Too many parameters , must be less or equal than 255 or override the {nameof(GuardUserParamsCount)} method."); } } /// /// Ensure that the specified instance is valid. /// /// The specified instance. /// The service name. protected virtual void GuardResolveInstance(object instance, string makeService) { if (instance == null) { throw MakeBuildFaildException(makeService, SpeculatedServiceType(makeService), null); } } /// /// Speculative service type based on specified service name. /// /// The specified service name. /// The speculative service type. protected virtual Type SpeculatedServiceType(string service) { if (findTypeCache.TryGetValue(service, out Type result)) { return result; } foreach (var finder in findType) { var type = finder.Invoke(service); if (type != null) { return findTypeCache[service] = type; } } return findTypeCache[service] = null; } /// /// Dependency injection on the property selector. /// /// The bind data for service. /// The instance for service. protected virtual void AttributeInject(Bindable makeServiceBindData, object makeServiceInstance) { if (makeServiceInstance == null) { return; } foreach (var property in makeServiceInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (!property.CanWrite || !property.IsDefined(typeof(InjectAttribute), false)) { continue; } var needService = GetPropertyNeedsService(property); object instance; if (property.PropertyType.IsClass || property.PropertyType.IsInterface) { instance = ResloveAttrClass(makeServiceBindData, needService, property); } else { instance = ResolveAttrPrimitive(makeServiceBindData, needService, property); } if (ReferenceEquals(instance, skipped)) { continue; } if (!CanInject(property.PropertyType, instance)) { throw new UnresolvableException( $"[{makeServiceBindData.Service}]({makeServiceInstance.GetType()}) Attr inject type must be [{property.PropertyType}] , But instance is [{instance?.GetType()}] , Make service is [{needService}]."); } property.SetValue(makeServiceInstance, instance, null); } } /// /// Check if the parameter passed in by the user can be injected compact. /// /// The parameter for dependent. /// An array for the user parameter. /// True if the parameter can be injected tightly. otherwise false. protected virtual bool CheckCompactInjectUserParams(ParameterInfo baseParam, object[] userParams) { if (userParams == null || userParams.Length <= 0) { return false; } return baseParam.ParameterType == typeof(object[]) || baseParam.ParameterType == typeof(object); } /// /// Gets the parameters injected through the compact. /// /// The parameter for dependent. /// An array for the user parameter. /// Parameters that can be injected in a compact. protected virtual object GetCompactInjectUserParams(ParameterInfo baseParam, ref object[] userParams) { if (!CheckCompactInjectUserParams(baseParam, userParams)) { return null; } try { if (baseParam.ParameterType == typeof(object) && userParams != null && userParams.Length == 1) { return userParams[0]; } return userParams; } finally { userParams = null; } } /// /// Gets the parameter matcher. /// /// An array for the user parameter. /// Returns the parameter matcher, if it is null, there is no matcher. protected virtual Func GetParamsMatcher(ref object[] userParams) { if (userParams == null || userParams.Length <= 0) { return null; } var tables = GetParamsTypeInUserParams(ref userParams); return tables.Length <= 0 ? null : MakeParamsMatcher(tables); } /// /// Select the appropriate constructor and get the corresponding array of parameter instances. /// /// The bind data for service. /// The type for service. /// An array for the user parameter. /// An array of resolved instances for dependent parameters. protected virtual object[] GetConstructorsInjectParams(Bindable makeServiceBindData, Type makeServiceType, object[] userParams) { var constructors = makeServiceType.GetConstructors(); if (constructors.Length <= 0) { return Array.Empty(); } ExceptionDispatchInfo exceptionDispatchInfo = null; foreach (var constructor in constructors) { try { return GetDependencies(makeServiceBindData, constructor.GetParameters(), userParams); } #pragma warning disable CA1031 catch (SException ex) { if (exceptionDispatchInfo == null) { exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex); } } #pragma warning restore CA1031 } exceptionDispatchInfo?.Throw(); throw new AssertException("Exception dispatch info is null."); } /// /// Get the service name of the specified instance. /// /// The specified instance. /// Returns the service name, or null if not found. protected string GetServiceWithInstanceObject(object instance) { return instancesReverse.TryGetValue(instance, out string origin) ? origin : null; } /// /// Verify that the current construct is valid. /// /// Called function name. protected virtual void GuardConstruct(string method) { } /// /// Verify service name validity. /// /// The service name. protected virtual void GuardServiceName(string service) { foreach (var c in ServiceBanChars) { if (service.IndexOf(c) >= 0) { throw new LogicException( $"Service name {service} contains disabled characters : {c}. please use Alias replacement"); } } } /// /// Verify function name validity. /// /// The method name. protected virtual void GuardMethodName(string method) { } /// /// Build an empty bound data. /// /// The service name. /// The bound data. protected virtual BindData MakeEmptyBindData(string service) { return new BindData(this, service, null, false); } /// /// Resolve the specified service(Will not perform check). /// /// The service name or alias. /// An array for the user parameter. /// The service instance. protected object Resolve(string service, params object[] userParams) { Guard.ParameterNotNull(service, nameof(service)); service = AliasToService(service); if (instances.TryGetValue(service, out object instance)) { return instance; } if (BuildStack.Contains(service)) { throw MakeCircularDependencyException(service); } BuildStack.Push(service); UserParamsStack.Push(userParams); try { var bindData = GetBindFillable(service); // We will start building a service instance, // For the built service we will try to do dependency injection。 instance = Build(bindData, userParams); // If we define an extender for the specified service, then we need // to execute the expander in turn,And allow the extender to modify // or overwrite the original service。 instance = Extend(service, instance); instance = bindData.IsStatic ? Instance(bindData.Service, instance) : TriggerOnResolving(bindData, instance); resolved.Add(bindData.Service); return instance; } finally { UserParamsStack.Pop(); BuildStack.Pop(); } } /// /// Build the specified service. /// /// The bind data for the service. /// An array for the user parameter. /// The service instance. protected virtual object Build(BindData makeServiceBindData, object[] userParams) { var instance = makeServiceBindData.Concrete != null ? makeServiceBindData.Concrete(this, userParams) : CreateInstance(makeServiceBindData, SpeculatedServiceType(makeServiceBindData.Service), userParams); return Inject(makeServiceBindData, instance); } /// /// Create the specified service instance. /// /// The bind data for the service. /// The type for the service. /// An array for the user parameter. /// The service instance. protected virtual object CreateInstance(Bindable makeServiceBindData, Type makeServiceType, object[] userParams) { if (IsUnableType(makeServiceType)) { return null; } userParams = GetConstructorsInjectParams(makeServiceBindData, makeServiceType, userParams); try { return CreateInstance(makeServiceType, userParams); } #pragma warning disable CA1031 catch (SException ex) #pragma warning restore CA1031 { throw MakeBuildFaildException(makeServiceBindData.Service, makeServiceType, ex); } } /// protected virtual object CreateInstance(Type makeServiceType, object[] userParams) { // If the parameter does not exist then you can get better // performance without writing parameters when reflecting. if (userParams == null || userParams.Length <= 0) { return Activator.CreateInstance(makeServiceType); } return Activator.CreateInstance(makeServiceType, userParams); } /// /// Get the service binding data, fill the data if the data is null. /// /// The service name. /// The bind data for the service. protected BindData GetBindFillable(string service) { return service != null && bindings.TryGetValue(service, out BindData bindData) ? bindData : MakeEmptyBindData(service); } /// /// Guaranteed not to be flushing. /// private void GuardFlushing() { if (flushing) { throw new LogicException("Container is flushing can not do it"); } } /// /// Convert an alias to a service name. /// /// The service name or alias. /// The service name. private string AliasToService(string name) { name = FormatService(name); return aliases.TryGetValue(name, out string alias) ? alias : name; } /// /// Trigger all of the resolving callbacks. /// /// The bind data for service. /// The service instance. /// The decorated service instance. private object TriggerOnResolving(BindData bindData, object instance) { instance = bindData.TriggerResolving(instance); instance = Trigger(bindData, instance, resolving); return TriggerOnAfterResolving(bindData, instance); } /// /// /// Trigger all of the after resolving callbacks. /// private object TriggerOnAfterResolving(BindData bindData, object instance) { instance = bindData.TriggerAfterResolving(instance); return Trigger(bindData, instance, afterResloving); } /// /// /// Trigger all of the release callbacks. /// private void TriggerOnRelease(IBindData bindData, object instance) { Trigger(bindData, instance, release); } /// /// Trigger the rebound callbacks for specified service instance. /// /// The specified service name. /// /// The specified service instance. /// Build from the container by service name if a null value is passed in. /// private void TriggerOnRebound(string service, object instance = null) { var callbacks = GetOnReboundCallbacks(service); if (callbacks == null || callbacks.Count <= 0) { return; } var bind = GetBind(service); instance = instance ?? Make(service); for (var index = 0; index < callbacks.Count; index++) { callbacks[index](instance); // If it is a not singleton(static) binding then each callback is given a separate instance. if (index + 1 < callbacks.Count && (bind == null || !bind.IsStatic)) { instance = Make(service); } } } /// /// Release the specified instance via . /// /// The specified instance. private void DisposeInstance(object instance) { if (instance is IDisposable disposable) { disposable.Dispose(); } } /// /// Gets the specified service all of the rebound callbacks. /// /// The service name. /// The rebound callbacks list. private IList> GetOnReboundCallbacks(string service) { return !rebound.TryGetValue(service, out List> result) ? null : result; } /// /// Check if there is a callback for the rebound service. /// /// The service name. /// True if the rebound callback exists. otherwise false. private bool HasOnReboundCallbacks(string service) { var result = GetOnReboundCallbacks(service); return result != null && result.Count > 0; } /// /// Trigger all of the extend callbacks. /// /// The service name. /// The service instance. /// The decorated instance. private object Extend(string service, object instance) { if (extenders.TryGetValue(service, out List> list)) { foreach (var extender in list) { instance = extender(instance, this); } } if (!extenders.TryGetValue(string.Empty, out list)) { return instance; } foreach (var extender in list) { instance = extender(instance, this); } return instance; } /// /// Dependency injection for specified instance. /// /// The bindable for the instance. /// The instance. /// An instance of injection has been completed. private object Inject(Bindable bindable, object instance) { GuardResolveInstance(instance, bindable.Service); AttributeInject(bindable, instance); return instance; } /// /// Get the variable of type from . /// /// An array for the user parameter. /// An array of parameters. private IParams[] GetParamsTypeInUserParams(ref object[] userParams) { // Filter is used here without using Remove because // the IParams is also one of the types that you might want to inject. var elements = Arr.Filter(userParams, value => value is IParams); var results = new IParams[elements.Length]; for (var i = 0; i < elements.Length; i++) { results[i] = (IParams)elements[i]; } return results; } /// /// Generate a default parameter matcher. /// /// An array of parameters. /// The default parameter matcher. private Func MakeParamsMatcher(IParams[] tables) { // The default matcher policy will match the parameter name // with the parameter name of the parameter table. // The first valid valid parameter value will be returned // as the return value return parameterInfo => { foreach (var table in tables) { if (!table.TryGetValue(parameterInfo.Name, out object result)) { continue; } if (ChangeType(ref result, parameterInfo.ParameterType)) { return result; } } return null; }; } /// /// Register a new callback in specified list. /// /// The callback. /// The specified list. private void AddClosure(Action closure, List> list) { Guard.Requires(closure != null); GuardFlushing(); list.Add(closure); } } } ================================================ FILE: src/CatLib.Core/Container/ContainerExtension.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable SA1618 using CatLib.Exception; using CatLib.Util; using System; namespace CatLib.Container { /// /// An extension function for . /// public static class ContainerExtension { /// /// Gets the binding data of the given service. /// /// The service name. /// The instance. /// Return null If there is no binding data. public static IBindData GetBind(this IContainer container) { return container.GetBind(container.Type2Service(typeof(TService))); } /// /// Whether the given service has been bound. /// /// The service name. /// The instance. /// True if the service has been bound. public static bool HasBind(this IContainer container) { return container.HasBind(container.Type2Service(typeof(TService))); } /// /// Whether the existing instance is exists in the container. /// /// The service name. /// The instance. /// True if the instance existed. public static bool HasInstance(this IContainer container) { return container.HasInstance(container.Type2Service(typeof(TService))); } /// /// Whether the service has been resolved. /// /// The service name. /// The instance. /// True if the service has been resolved. public static bool IsResolved(this IContainer container) { return container.IsResolved(container.Type2Service(typeof(TService))); } /// /// Whether the given service can be made. /// /// The service name. /// The instance. /// True if the given service can be made. public static bool CanMake(this IContainer container) { return container.CanMake(container.Type2Service(typeof(TService))); } /// /// Whether the given service is singleton bind. false if the service not exists. /// /// The service name. /// The instance. /// True if the service is singleton bind. public static bool IsStatic(this IContainer container) { return container.IsStatic(container.Type2Service(typeof(TService))); } /// /// Whether the given name is an alias. /// /// The service name. /// The instance. /// True if the given name is an alias. public static bool IsAlias(this IContainer container) { return container.IsAlias(container.Type2Service(typeof(TService))); } /// /// Alias a service to a different name. /// /// The alias name. /// The service name. /// The container instance. /// Returns the container instance. public static IContainer Alias(this IContainer container) { return container.Alias(container.Type2Service(typeof(TAlias)), container.Type2Service(typeof(TService))); } /// /// Register a binding with the container. /// /// The service name (also indicates specific implementation). /// The instance. /// The service binding data. public static IBindData Bind(this IContainer container) { return container.Bind(container.Type2Service(typeof(TService)), typeof(TService), false); } /// /// Register a binding with the container. /// /// The service name. /// The service concrete. /// The instance. /// The service binding data. public static IBindData Bind(this IContainer container) { return container.Bind(container.Type2Service(typeof(TService)), typeof(TConcrete), false); } /// /// Register a binding with the container. /// /// The service name. /// The instance. /// The service concrete. /// The service binding data. public static IBindData Bind(this IContainer container, Func concrete) { Guard.Requires(concrete != null); return container.Bind(container.Type2Service(typeof(TService)), concrete, false); } /// /// Register a binding with the container. /// /// The service name. /// The instance. /// The service concrete. /// The service binding data. public static IBindData Bind(this IContainer container, Func concrete) { Guard.Requires(concrete != null); return container.Bind(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(p), false); } /// /// Register a binding with the container. /// /// The service name. /// The instance. /// The service concrete. /// The service binding data. public static IBindData Bind(this IContainer container, Func concrete) { Guard.Requires(concrete != null); return container.Bind(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), false); } /// /// Register a binding with the container. /// /// The instance. /// The service name. /// The service concrete. /// The service binding data. public static IBindData Bind(this IContainer container, string service, Func concrete) { Guard.Requires(concrete != null); return container.Bind(service, concrete, false); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The service concrete. /// The instance. /// The binding data. /// True if register a binding with the container. public static bool BindIf(this IContainer container, out IBindData bindData) { return container.BindIf(container.Type2Service(typeof(TService)), typeof(TConcrete), false, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name (also indicates specific implementation). /// The instance. /// The binding data. /// True if register a binding with the container. public static bool BindIf(this IContainer container, out IBindData bindData) { return container.BindIf(container.Type2Service(typeof(TService)), typeof(TService), false, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The instance. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool BindIf(this IContainer container, Func concrete, out IBindData bindData) { Guard.Requires(concrete != null); return container.BindIf(container.Type2Service(typeof(TService)), concrete, false, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The instance. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool BindIf(this IContainer container, Func concrete, out IBindData bindData) { Guard.Requires(concrete != null); return container.BindIf(container.Type2Service(typeof(TService)), (c, @params) => concrete(@params), false, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The instance. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool BindIf(this IContainer container, Func concrete, out IBindData bindData) { Guard.Requires(concrete != null); return container.BindIf(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), false, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The instance. /// The service name. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool BindIf(this IContainer container, string service, Func concrete, out IBindData bindData) { return container.BindIf(service, concrete, false, out bindData); } /// /// Register a singleton binding with the container. /// /// The instance. /// The service name. /// The service concrete. /// The service binding data. public static IBindData Singleton(this IContainer container, string service, Func concrete) { return container.Bind(service, concrete, true); } /// /// Register a singleton binding with the container. /// /// The service name. /// The service concrete. /// The instance. /// The service binding data. public static IBindData Singleton(this IContainer container) { return container.Bind(container.Type2Service(typeof(TService)), typeof(TConcrete), true); } /// /// Register a singleton binding with the container. /// /// The service name (also indicates specific implementation). /// The instance. /// The service binding data. public static IBindData Singleton(this IContainer container) { return container.Bind(container.Type2Service(typeof(TService)), typeof(TService), true); } /// /// Register a singleton binding with the container. /// /// The service name. /// The instance. /// The service concrete. /// The service binding data. public static IBindData Singleton( this IContainer container, Func concrete) { Guard.Requires(concrete != null); return container.Bind(container.Type2Service(typeof(TService)), concrete, true); } /// /// Register a singleton binding with the container. /// /// The service name. /// The instance. /// The service concrete. /// The service binding data. public static IBindData Singleton(this IContainer container, Func concrete) { Guard.Requires(concrete != null); return container.Bind(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(p), true); } /// /// Register a singleton binding with the container. /// /// The service name. /// The instance. /// The service concrete. /// The service binding data. public static IBindData Singleton( this IContainer container, Func concrete) { Guard.Requires(concrete != null); return container.Bind(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), true); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The service concrete. /// The instance. /// The binding data. /// True if register a binding with the container. public static bool SingletonIf(this IContainer container, out IBindData bindData) { return container.BindIf(container.Type2Service(typeof(TService)), typeof(TConcrete), true, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name (also indicates specific implementation). /// The instance. /// The binding data. /// True if register a binding with the container. public static bool SingletonIf(this IContainer container, out IBindData bindData) { return container.BindIf(container.Type2Service(typeof(TService)), typeof(TService), true, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The instance. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool SingletonIf(this IContainer container, Func concrete, out IBindData bindData) { return container.BindIf(container.Type2Service(typeof(TService)), concrete, true, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The instance. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool SingletonIf(this IContainer container, Func concrete, out IBindData bindData) { Guard.Requires(concrete != null); return container.BindIf(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), true, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The instance. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool SingletonIf(this IContainer container, Func concrete, out IBindData bindData) { Guard.Requires(concrete != null); return container.BindIf(container.Type2Service(typeof(TService)), (c, @params) => concrete(@params), true, out bindData); } /// /// Register a binding with the container if the service not exists. /// /// The instance. /// The service name. /// The service concrete. /// The binding data. /// True if register a binding with the container. public static bool SingletonIf(this IContainer container, string service, Func concrete, out IBindData bindData) { return container.BindIf(service, concrete, true, out bindData); } /// /// Register a method with the container. /// /// The instance. /// The method name. /// The invoking target. /// The method info to invoke. /// Returns the method bind instance. public static IMethodBind BindMethod(this IContainer container, string method, object target, string call = null) { Guard.ParameterNotNull(method, nameof(method)); Guard.ParameterNotNull(target, nameof(target)); return container.BindMethod(method, target, target.GetType().GetMethod(call ?? Str.Method(method))); } /// /// Register a method with the container. /// /// The instance. /// The method name. /// The method to invoke. /// Returns the method bind instance. public static IMethodBind BindMethod(this IContainer container, string method, Func callback) { Guard.Requires(method != null); Guard.Requires(callback != null); return container.BindMethod(method, callback.Target, callback.Method); } /// /// Register a method with the container. /// /// The instance. /// The method name. /// The method to invoke. /// Returns the method bind instance. public static IMethodBind BindMethod(this IContainer container, string method, Func callback) { Guard.Requires(method != null); Guard.Requires(callback != null); return container.BindMethod(method, callback.Target, callback.Method); } /// /// Register a method with the container. /// /// The instance. /// The method name. /// The method to invoke. /// Returns the method bind instance. public static IMethodBind BindMethod(this IContainer container, string method, Func callback) { Guard.Requires(method != null); Guard.Requires(callback != null); return container.BindMethod(method, callback.Target, callback.Method); } /// /// Register a method with the container. /// /// The instance. /// The method name. /// The method to invoke. /// Returns the method bind instance. public static IMethodBind BindMethod(this IContainer container, string method, Func callback) { Guard.Requires(method != null); Guard.Requires(callback != null); return container.BindMethod(method, callback.Target, callback.Method); } /// /// Register a method with the container. /// /// The instance. /// The method name. /// The method to invoke. /// Returns the method bind instance. public static IMethodBind BindMethod(this IContainer container, string method, Func callback) { Guard.Requires(method != null); Guard.Requires(callback != null); return container.BindMethod(method, callback.Target, callback.Method); } /// /// Unbinds a service from the container. /// /// The service name. /// The instance. public static void Unbind(this IContainer container) { container.Unbind(container.Type2Service(typeof(TService))); } /// /// Assign a set of tags to a given binding. /// /// The service name. /// The instance. /// The tag name. public static void Tag(this IContainer container, string tag) { container.Tag(tag, container.Type2Service(typeof(TService))); } /// /// Register an existing instance as shared in the container. /// /// The service name. /// The instance. /// The service instance. /// Object processed by the decorator. public static object Instance(this IContainer container, object instance) { return container.Instance(container.Type2Service(typeof(TService)), instance); } /// /// Release an existing instance in the container. /// /// The service name. /// The instance. /// True if the instance is released. otherwise if instance not exits return false. public static bool Release(this IContainer container) { return container.Release(container.Type2Service(typeof(TService))); } /// /// Release an existing instance in the container. /// /// The instance. /// Service instance that needs to be released. /// Whether to reverse the release order. /// Returns false if one has not been successfully released, is an instance that has not been released. public static bool Release(this IContainer container, ref object[] instances, bool reverse = true) { if (instances == null || instances.Length <= 0) { return true; } if (reverse) { Array.Reverse(instances); } var errorIndex = 0; for (var index = 0; index < instances.Length; index++) { if (instances[index] == null) { continue; } if (!container.Release(instances[index])) { instances[errorIndex++] = instances[index]; } } Array.Resize(ref instances, errorIndex); if (reverse && errorIndex > 0) { Array.Reverse(instances); } return errorIndex <= 0; } /// /// Call the given method and inject its dependencies. /// /// The instance. /// The method to called. /// The user parameters. public static void Call(this IContainer container, Action method, params object[] userParams) { Guard.Requires(method != null); container.Call(method.Target, method.Method, userParams); } /// /// Call the given method and inject its dependencies. /// /// The instance. /// The method to called. /// The user parameters. public static void Call(this IContainer container, Action method, params object[] userParams) { Guard.Requires(method != null); container.Call(method.Target, method.Method, userParams); } /// /// Call the given method and inject its dependencies. /// /// The instance. /// The method to called. /// The user parameters. public static void Call(this IContainer container, Action method, params object[] userParams) { Guard.Requires(method != null); container.Call(method.Target, method.Method, userParams); } /// /// Call the given method and inject its dependencies. /// /// The instance. /// The method to called. /// The user parameters. public static void Call(this IContainer container, Action method, params object[] userParams) { Guard.Requires(method != null); container.Call(method.Target, method.Method, userParams); } /// /// Call the given method and inject its dependencies. /// /// The instance. /// The instance on which to call the method. /// The method name. /// The user parameters. /// The return value of method. public static object Call(this IContainer container, object target, string method, params object[] userParams) { Guard.ParameterNotNull(method, nameof(method)); Guard.ParameterNotNull(target, nameof(target)); var methodInfo = target.GetType().GetMethod(method); if (methodInfo == null) { throw new LogicException($"Function \"{method}\" not found."); } return container.Call(target, methodInfo, userParams); } /// /// Wrap a method called in a dependency injection form. /// /// The instance. /// The method to called. /// The user parameters. /// Return the wrapper method, which can be called to trigger another method in the form of dependency injection. public static Action Wrap(this IContainer container, Action method, params object[] userParams) { return () => { if (method != null) { container.Call(method.Target, method.Method, userParams); } }; } /// /// Wrap a method called in a dependency injection form. /// /// The instance. /// The method to called. /// The user parameters. /// Return the wrapper method, which can be called to trigger another method in the form of dependency injection. public static Action Wrap(this IContainer container, Action method, params object[] userParams) { return () => { if (method != null) { container.Call(method.Target, method.Method, userParams); } }; } /// /// Wrap a method called in a dependency injection form. /// /// The instance. /// The method to called. /// The user parameters. /// Return the wrapper method, which can be called to trigger another method in the form of dependency injection. public static Action Wrap(this IContainer container, Action method, params object[] userParams) { return () => { if (method != null) { container.Call(method.Target, method.Method, userParams); } }; } /// /// Wrap a method called in a dependency injection form. /// /// The instance. /// The method to called. /// The user parameters. /// Return the wrapper method, which can be called to trigger another method in the form of dependency injection. public static Action Wrap(this IContainer container, Action method, params object[] userParams) { return () => { if (method != null) { container.Call(method.Target, method.Method, userParams); } }; } /// /// Resolve the given service or alias from the container. /// /// The service name. /// The instance. /// The user parameters. /// The serivce instance. Throw exception if the service can not resolved. public static TService Make(this IContainer container, params object[] userParams) { return (TService)container.Make(container.Type2Service(typeof(TService)), userParams); } /// /// Resolve the given service or alias from the container. /// /// The instance. /// The service type. /// The user parameters. /// The serivce instance. Throw exception if the service can not resolved. public static object Make(this IContainer container, Type type, params object[] userParams) { var service = container.Type2Service(type); container.BindIf(service, type, false, out _); return container.Make(service, userParams); } /// /// an abstract type in the container. /// Allow configuration or replacement of services during service resolving. /// /// The instance. /// The service name. /// The closure. public static void Extend(this IContainer container, string service, Func closure) { container.Extend(service, (instance, c) => closure(instance)); } /// /// an abstract type in the container. /// Allow configuration or replacement of services during service resolving. /// /// The instance. /// The closure. public static void Extend(this IContainer container, Func closure) { container.Extend(container.Type2Service(typeof(TService)), (instance, c) => closure((TConcrete)instance)); } /// /// an abstract type in the container. /// Allow configuration or replacement of services during service resolving. /// /// The instance. /// The closure. public static void Extend(this IContainer container, Func closure) { container.Extend( container.Type2Service(typeof(TService)), (instance, c) => closure((TConcrete)instance, c)); } /// /// an abstract type in the container. /// Allow configuration or replacement of services during service resolving. /// /// Expected type or interface. /// The instance. /// The closure. public static void Extend(this IContainer container, Func closure) { container.Extend(null, (instance, c) => { if (instance is TConcrete) { return closure((TConcrete)instance, c); } return instance; }); } /// /// an abstract type in the container. /// Allow configuration or replacement of services during service resolving. /// /// Expected type or interface. /// The instance. /// The closure. public static void Extend(this IContainer container, Func closure) { container.Extend(null, (instance, _) => { if (instance is TConcrete) { return closure((TConcrete)instance); } return instance; }); } /// /// Register a new release callback. /// /// The instance. /// The callback. /// Returns the instance. public static IContainer OnRelease(this IContainer container, Action callback) { Guard.Requires(callback != null); return container.OnRelease((_, instance) => callback(instance)); } /// /// Register a new release callback. /// /// The instance. /// The callback. /// Returns the instance. public static IContainer OnRelease(this IContainer container, Action closure) { Guard.Requires(closure != null); return container.OnRelease((_, instance) => { if (instance is T) { closure((T)instance); } }); } /// /// Register a new release callback. /// /// The instance. /// The callback. /// Returns the instance. public static IContainer OnRelease(this IContainer container, Action closure) { Guard.Requires(closure != null); return container.OnRelease((bindData, instance) => { if (instance is T) { closure(bindData, (T)instance); } }); } /// /// Register a new resolving callback. /// /// The instance. /// The callback. /// Returns the instance. public static IContainer OnResolving(this IContainer container, Action callback) { Guard.Requires(callback != null); return container.OnResolving((_, instance) => { callback(instance); }); } /// /// Register a new resolving callback. /// Only the type matches the given type will be called back. /// /// The specified type. /// The instance. /// The closure. /// Returns the instance. public static IContainer OnResolving(this IContainer container, Action closure) { Guard.Requires(closure != null); return container.OnResolving((_, instance) => { if (instance is T) { closure((T)instance); } }); } /// /// Register a new resolving callback. /// Only the type matches the given type will be called back. /// /// The specified type. /// The instance. /// The closure. /// Returns the instance. public static IContainer OnResolving(this IContainer container, Action closure) { Guard.Requires(closure != null); return container.OnResolving((bindData, instance) => { if (instance is T) { closure(bindData, (T)instance); } }); } /// /// Register a new after resolving callback. /// /// The instance. /// The callback. /// Returns the instance. public static IContainer OnAfterResolving(this IContainer container, Action callback) { Guard.Requires(callback != null); return container.OnAfterResolving((_, instance) => { callback(instance); }); } /// /// Register a new after resolving callback. /// Only the type matches the given type will be called back. /// /// The specified type. /// The instance. /// The closure. /// Returns the instance. public static IContainer OnAfterResolving(this IContainer container, Action closure) { Guard.Requires(closure != null); return container.OnAfterResolving((_, instance) => { if (instance is T) { closure((T)instance); } }); } /// /// Register a new after resolving callback. /// Only the type matches the given type will be called back. /// /// The specified type. /// The instance. /// The closure. /// Returns the instance. public static IContainer OnAfterResolving(this IContainer container, Action closure) { Guard.Requires(closure != null); return container.OnAfterResolving((bindData, instance) => { if (instance is T) { closure(bindData, (T)instance); } }); } /// /// Watch the specified service, trigger callback when rebinding the service. /// /// The service name. /// The instance. /// The callback. public static void Watch(this IContainer container, Action method) { Guard.Requires(method != null); container.OnRebound(container.Type2Service(typeof(TService)), (instance) => method()); } /// /// Watch the specified service, trigger callback when rebinding the service. /// /// The service name. /// The instance. /// The callback. public static void Watch(this IContainer container, Action method) { Guard.Requires(method != null); container.OnRebound(container.Type2Service(typeof(TService)), (instance) => method((TService)instance)); } /// /// Converts the given type to the service name. /// /// The given type. /// The instance. /// The service name. public static string Type2Service(this IContainer container) { return container.Type2Service(typeof(TService)); } /// /// Lazially resolve a service that is built when the call returns a callback. /// /// The service name. /// The instance. /// The user parameters. /// The callback. public static Func Factory(this IContainer container, params object[] userParams) { return () => (TService)container.Make(container.Type2Service(typeof(TService)), userParams); } /// /// Lazially resolve a service that is built when the call returns a callback. /// /// The instance. /// The service name or alias. /// The user parameters. /// The callback. public static Func Factory(this IContainer container, string service, params object[] userParams) { return () => container.Make(service, userParams); } } } ================================================ FILE: src/CatLib.Core/Container/GivenData.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Util; using System; namespace CatLib.Container { /// internal sealed class GivenData : IGivenData where TReturn : class, IBindable { private readonly Bindable bindable; private readonly Container container; private string needs; /// /// Initializes a new instance of the class. /// /// The container instance. /// The bindable data. internal GivenData(Container container, Bindable bindable) { this.container = container; this.bindable = bindable; } /// public TReturn Given(string service) { Guard.ParameterNotNull(service, nameof(service)); bindable.AddContextual(needs, service); return bindable as TReturn; } /// public TReturn Given() { return Given(container.Type2Service(typeof(TService))); } /// public TReturn Given(Func closure) { Guard.Requires(closure != null); bindable.AddContextual(needs, closure); return bindable as TReturn; } /// internal IGivenData Needs(string needs) { this.needs = needs; return this; } } } ================================================ FILE: src/CatLib.Core/Container/IBindData.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; namespace CatLib.Container { /// /// The bind data indicates relational data related to the specified service. /// public interface IBindData : IBindable { /// /// Gets the delegate return service concrete. /// Func Concrete { get; } /// /// Gets a value indicating whether true if the service is singleton(static). /// bool IsStatic { get; } /// /// Alias service to a different name. /// /// The alias. /// The current instance. #pragma warning disable CA1716 IBindData Alias(string alias); #pragma warning restore CA1716 /// /// Assign a tag to a given service. /// /// The tag name. /// The current instance. IBindData Tag(string tag); /// /// Register a new resolving callback. /// /// The resolving callback. /// The current instance. IBindData OnResolving(Action closure); /// /// Register a new after resolving callback. /// /// The after resolving callback. /// The current instance. IBindData OnAfterResolving(Action closure); /// /// Register a new release callback. /// /// The release callback. /// The current instance. IBindData OnRelease(Action closure); } } ================================================ FILE: src/CatLib.Core/Container/IBindable.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable SA1402 namespace CatLib.Container { /// /// is the interface implemented by all bindable data classes. /// public interface IBindable { /// /// Gets the service name. /// string Service { get; } /// /// Gets the container to which the service belongs. /// IContainer Container { get; } /// /// Unbind the service from the container. /// /// /// If the service is a singletoned instance, then the singleton instance /// that has been built will be automatically released. /// void Unbind(); } /// public interface IBindable : IBindable where TReturn : IBindable { /// /// When the service specified by the demand. /// /// The specified service name. /// The given relationship in the context. IGivenData Needs(string service); /// /// The type convert to service name. IGivenData Needs(); } } ================================================ FILE: src/CatLib.Core/Container/IContainer.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; using System.Reflection; #pragma warning disable CA1716 namespace CatLib.Container { /// /// is the interface implemented by all IOC Container classes. /// public interface IContainer { /// /// Resolve the given type from the container. /// /// The service name or alias. /// The serivce instance. Throw exception if the service can not resolved. object this[string service] { get; set; } /// /// Gets the binding data of the given service. /// /// The service name or alias. /// Return null If there is no binding data. IBindData GetBind(string service); /// /// Whether the given service has been bound. /// /// The service name or alias. /// True if the service has been bound. bool HasBind(string service); /// /// Whether the existing instance is exists in the container. /// /// The service name or alias. /// True if the instance existed. bool HasInstance(string service); /// /// Whether the service has been resolved. /// /// The service name or alias. /// True if the service has been resolved. bool IsResolved(string service); /// /// Whether the given service can be made. /// /// The service name or alias. /// True if the given service can be made. bool CanMake(string service); /// /// Whether the given service is singleton bind. false if the service not exists. /// /// The service name or alias. /// True if the service is singleton bind. bool IsStatic(string service); /// /// Whether the given name is an alias. /// /// The given name. /// True if the given name is an alias. bool IsAlias(string name); /// /// Register a binding with the container. /// /// The service name. /// The service type. /// Whether the service is singleton bind. /// The binding data. IBindData Bind(string service, Type concrete, bool isStatic); /// /// Register a binding with the container. /// /// The service name. /// Closure return service instance. /// Whether the service is singleton bind. /// The binding data. IBindData Bind(string service, Func concrete, bool isStatic); /// /// Register a binding with the container if the service not exists. /// /// The service name. /// Closure return service instance. /// Whether the service is singleton bind. /// The binding data. /// True if register a binding with the container. bool BindIf(string service, Func concrete, bool isStatic, out IBindData bindData); /// /// Register a binding with the container if the service not exists. /// /// The service name. /// The service type. /// Whether the service is singleton bind. /// The binding data. /// True if register a binding with the container. bool BindIf(string service, Type concrete, bool isStatic, out IBindData bindData); /// /// Register a method with the container. /// /// The method name. /// The invoking target. /// The method info to invoke. /// The method binding data. IMethodBind BindMethod(string method, object target, MethodInfo called); /// /// Unbinds a method from the container. /// /// /// The target. /// string will be taken as the method name. /// IMethodBind will be taken as a given method. /// Other object will be taken as the invoking target. /// void UnbindMethod(object target); /// /// Unbinds a service from the container. /// /// The service name or alias. void Unbind(string service); /// /// Assign a set of tags to a given binding. /// /// The tag name. /// The array of service name or alias. void Tag(string tag, params string[] services); /// /// Resolve all of the bindings for a given tag. /// /// The tag name. /// All the services tagged with the given tag name. object[] Tagged(string tag); /// /// Register an existing instance as shared in the container. /// /// The service name or alias. /// The service instance. /// New instance after being processed by the decorator. object Instance(string service, object instance); /// /// Release an existing instance in the container. /// /// The service name or alias or instance. /// True if the existing instance has been released. bool Release(object mixed); /// /// Flush the container of all bindings and resolved instances. /// void Flush(); /// /// Call the method in bonded container and inject its dependencies. /// /// The method name. /// The user parameters. /// The return value of method. object Invoke(string method, params object[] userParams); /// /// Call the given method and inject its dependencies. /// /// The instance on which to call the method. /// The method info. /// The user parameters. /// The return value of method. object Call(object target, MethodInfo methodInfo, params object[] userParams); /// /// Resolve the given service or alias from the container. /// /// The service name or alias. /// The user parameters. /// The serivce instance. Throw exception if the service can not resolved. object Make(string service, params object[] userParams); /// /// Alias a service to a different name. /// /// The alias name to service. /// The service name. /// The container instance. IContainer Alias(string alias, string service); /// /// an abstract type in the container. /// Allow configuration or replacement of services during service resolving. /// /// The service name or alias, null if the apply to gloabl. /// The closure replacement instance. void Extend(string service, Func closure); /// /// Register a new resolving callback. /// /// The callback. /// The container instance. IContainer OnResolving(Action closure); /// /// Register a new after resolving callback. /// /// The callback. /// The container instance. IContainer OnAfterResolving(Action closure); /// /// Register a new release callback. /// /// The callback. /// The container instance. IContainer OnRelease(Action closure); /// /// Register a callback for when type finding fails. /// /// The callback. /// The priority. /// The container instance. IContainer OnFindType(Func func, int priority = int.MaxValue); /// /// Register a new callback to an abstract's rebind event. /// /// The service name or alias. /// The callback. /// The container instance. IContainer OnRebound(string service, Action callback); /// /// Converts the given type to the service name. /// /// The given type. /// The service name. string Type2Service(Type type); } } ================================================ FILE: src/CatLib.Core/Container/IGivenData.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; namespace CatLib.Container { /// /// Indicates the given relationship in the context. /// /// The type of the . public interface IGivenData where TReturn : IBindable { /// /// Give the specified service. /// /// The service name or alias. /// The instance of the . TReturn Given(string service); /// TReturn Given(); /// /// The closure returns the given instance. TReturn Given(Func closure); } } ================================================ FILE: src/CatLib.Core/Container/IMethodBind.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.Container { /// /// The method binding data. /// public interface IMethodBind : IBindable { } } ================================================ FILE: src/CatLib.Core/Container/IParams.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.Container { /// /// All parameter tables must implement this interface. /// public interface IParams { /// /// Get parameters by name. /// /// The parameter name. /// The parameter value. /// True if the parameter exist. bool TryGetValue(string key, out object value); } } ================================================ FILE: src/CatLib.Core/Container/InjectAttribute.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; namespace CatLib.Container { /// /// The injection attribute. /// [AttributeUsage(AttributeTargets.Property)] public class InjectAttribute : Attribute { /// /// Gets or sets a value indicating whether the property is required. /// public bool Required { get; set; } = true; } } ================================================ FILE: src/CatLib.Core/Container/MethodBind.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Reflection; namespace CatLib.Container { /// /// The default method binding data implementation. /// internal sealed class MethodBind : Bindable, IMethodBind { private readonly MethodContainer methodContainer; /// /// Initializes a new instance of the class. /// /// The instance. /// The instance. /// The service name. /// The instance on which to call the method. /// The method to called. public MethodBind(MethodContainer methodContainer, Container container, string service, object target, MethodInfo call) : base(container, service) { this.methodContainer = methodContainer; Target = target; MethodInfo = call; ParameterInfos = call.GetParameters(); } /// /// Gets the method info. /// public MethodInfo MethodInfo { get; } /// /// Gets the instance on which to call the method. /// public object Target { get; } /// /// Gets an array of the method parameters. /// public ParameterInfo[] ParameterInfos { get; } /// /// Unbinds a method from the container. /// protected override void ReleaseBind() { methodContainer.Unbind(this); } } } ================================================ FILE: src/CatLib.Core/Container/MethodContainer.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.Collections.Generic; using System.Reflection; namespace CatLib.Container { /// /// The catlib ioc method container implemented. /// internal sealed class MethodContainer { private readonly Dictionary> targetToMethodsMappings; private readonly Dictionary methodMappings; private readonly Container container; /// /// Initializes a new instance of the class. /// /// The instance. internal MethodContainer(Container container) { this.container = container; targetToMethodsMappings = new Dictionary>(); methodMappings = new Dictionary(); } /// /// Register a method with the container. /// /// The method name. /// The invoking target. /// The method info to invoke. /// The method binding data. public IMethodBind Bind(string method, object target, MethodInfo methodInfo) { Guard.ParameterNotNull(method, nameof(method)); Guard.ParameterNotNull(methodInfo, nameof(methodInfo)); if (!methodInfo.IsStatic) { Guard.Requires(target != null); } if (methodMappings.ContainsKey(method)) { throw new LogicException($"Method [{method}] is already {nameof(Bind)}"); } var methodBind = new MethodBind(this, container, method, target, methodInfo); methodMappings[method] = methodBind; if (target == null) { return methodBind; } if (!targetToMethodsMappings.TryGetValue(target, out List targetMappings)) { targetToMethodsMappings[target] = targetMappings = new List(); } targetMappings.Add(method); return methodBind; } /// /// Call the method in bonded container and inject its dependencies. /// /// The method name. /// The user parameters. /// The return value of method. public object Invoke(string method, params object[] userParams) { Guard.ParameterNotNull(method, nameof(method)); if (!methodMappings.TryGetValue(method, out MethodBind methodBind)) { throw MakeMethodNotFoundException(method); } var injectParams = container.GetDependencies(methodBind, methodBind.ParameterInfos, userParams) ?? Array.Empty(); return methodBind.MethodInfo.Invoke(methodBind.Target, injectParams); } /// /// Unbinds a method from the container. /// /// /// The target. /// string will be taken as the method name. /// IMethodBind will be taken as a given method. /// Other object will be taken as the invoking target. /// public void Unbind(object target) { Guard.Requires(target != null); if (target is MethodBind methodBind) { methodBind.Unbind(); return; } if (target is string) { if (!methodMappings.TryGetValue(target.ToString(), out methodBind)) { return; } methodBind.Unbind(); return; } UnbindWithObject(target); } /// /// Flush the container of all method bindings. /// public void Flush() { targetToMethodsMappings.Clear(); methodMappings.Clear(); } /// /// Unbinds a method from the container. /// /// The method binding data. internal void Unbind(MethodBind methodBind) { methodMappings.Remove(methodBind.Service); if (methodBind.Target == null) { return; } if (!targetToMethodsMappings.TryGetValue(methodBind.Target, out List methods)) { return; } methods.Remove(methodBind.Service); if (methods.Count <= 0) { targetToMethodsMappings.Remove(methodBind.Target); } } /// /// Create a method without not found exception. /// private static LogicException MakeMethodNotFoundException(string method) { return new LogicException($"Method [{method}] is not found."); } /// /// Remove all methods bound to the object. /// private void UnbindWithObject(object target) { if (!targetToMethodsMappings.TryGetValue(target, out List methods)) { return; } foreach (var method in methods.ToArray()) { Unbind(method); } } } } ================================================ FILE: src/CatLib.Core/Container/ParamsCollection.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace CatLib.Container { /// /// Default parameter table implementation. /// [ExcludeFromCodeCoverage] public sealed class ParamsCollection : IParams, IEnumerable> { private readonly IDictionary collection; /// /// Initializes a new instance of the class. /// public ParamsCollection() { collection = new Dictionary(); } /// /// Initializes a new instance of the class. /// /// The parameters mapping. public ParamsCollection(IDictionary mapping) { collection = mapping; } /// /// Get or set a parameter. /// /// The parameter name. /// The parameter value. public object this[string key] { get => collection[key]; set => collection[key] = value; } /// /// Gets the iterator. /// /// Return the iterator. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return collection.GetEnumerator(); } /// /// Gets the iterator. /// /// Return the iterator. IEnumerator> IEnumerable>.GetEnumerator() { return collection.GetEnumerator(); } /// /// Add an parameter. /// /// The parameter name. /// The parameter value. public void Add(string key, object value) { collection.Add(key, value); } /// /// Remove an parameter. /// /// The parameter name. /// True if the removed. public bool Remove(string key) { return collection.Remove(key); } /// /// Gets an parameter value. /// /// The parameter name. /// The parameter value. /// True if the parameter is exist. public bool TryGetValue(string key, out object value) { return collection.TryGetValue(key, out value); } } } ================================================ FILE: src/CatLib.Core/Container/UnresolvableException.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using System.Diagnostics.CodeAnalysis; using SException = System.Exception; namespace CatLib.Container { /// /// Failed to resolve the service exception. /// [ExcludeFromCodeCoverage] public class UnresolvableException : RuntimeException { /// /// Initializes a new instance of the class. /// public UnresolvableException() { } /// /// Initializes a new instance of the class. /// /// The exception message. public UnresolvableException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// The exception message. /// The inner exception. public UnresolvableException(string message, SException innerException) : base(message, innerException) { } } } ================================================ FILE: src/CatLib.Core/Container/VariantAttribute.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; namespace CatLib.Container { /// /// A constructor that represents the class allows a primitive type(Include string) to be passed in to be converted to the current class. /// [AttributeUsage(AttributeTargets.Class)] public class VariantAttribute : Attribute { } } ================================================ FILE: src/CatLib.Core/EventDispatcher/EventDispatcher.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.Collections.Generic; using System.Linq; namespace CatLib.EventDispatcher { /// public class EventDispatcher : IEventDispatcher { private readonly IDictionary> listeners; /// /// Initializes a new instance of the class. /// public EventDispatcher() { listeners = new Dictionary>(); } /// public virtual bool AddListener(string eventName, EventHandler handler) { if (string.IsNullOrEmpty(eventName) || handler == null) { return false; } if (!listeners.TryGetValue(eventName, out IList handlers)) { listeners[eventName] = handlers = new List(); } else if (handlers.Contains(handler)) { return false; } handlers.Add(handler); return true; } /// public virtual void Raise(string eventName, object sender, EventArgs e = null) { Guard.Requires(!(sender is EventArgs), $"Passed event args for the parameter {sender}, Did you make a wrong method call?"); e = e ?? EventArgs.Empty; if (!listeners.TryGetValue(eventName, out IList handlers)) { return; } foreach (var listener in handlers) { if (e is IStoppableEvent stoppableEvent && stoppableEvent.IsPropagationStopped) { break; } listener(sender, e); } } /// public virtual EventHandler[] GetListeners(string eventName) { if (!listeners.TryGetValue(eventName, out IList handlers)) { return Array.Empty(); } return handlers.ToArray(); } /// public virtual bool HasListener(string eventName) { return listeners.ContainsKey(eventName); } /// public virtual bool RemoveListener(string eventName, EventHandler handler = null) { if (handler == null) { return listeners.Remove(eventName); } if (!listeners.TryGetValue(eventName, out IList handlers)) { return false; } var status = handlers.Remove(handler); if (handlers.Count <= 0) { listeners.Remove(eventName); } return status; } } } ================================================ FILE: src/CatLib.Core/EventDispatcher/IEventDispatcher.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; namespace CatLib.EventDispatcher { /// /// Represents an event dispatcher. /// public interface IEventDispatcher { /// /// Adds an event listener that listens on the specified events. /// /// The event name. /// The event listener. /// True if the handler added. otherwise false if handler already exists. bool AddListener(string eventName, EventHandler handler); /// /// Removes an event listener from the specified events. /// /// The event name. /// Remove the specified event listener, otherwise remove all listeners under the event. /// True if removed the listener. bool RemoveListener(string eventName, EventHandler handler = null); /// /// Gets the listeners of a specific event or all listeners sorted by descending priority. Will not return listeners in the inheritance chain. /// /// The specified events type. /// The event name. /// The event listeners for the specified event. Never return null. EventHandler[] GetListeners(string eventName); /// /// Whether an event has any registered event listener. /// If has inheritance, will not return handler in the inheritance chain. /// /// The event name. /// True if the event has any registered listeners. bool HasListener(string eventName); /// /// Provide all relevant listeners with an event to process. /// /// The event name. /// The source of the event. /// The event object to process. #pragma warning disable CA1030 void Raise(string eventName, object sender, EventArgs e = null); #pragma warning restore CA1030 } } ================================================ FILE: src/CatLib.Core/EventDispatcher/IStoppableEvent.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.EventDispatcher { /// /// An event whose processing may be interrupted when the event has been handled. /// public interface IStoppableEvent { /// /// Gets a value indicating whether propagation stopped. /// /// True if the propagation stopped. bool IsPropagationStopped { get; } } } ================================================ FILE: src/CatLib.Core/Exception/AssertException.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using SException = System.Exception; namespace CatLib.Exception { /// /// Represents an assertion exception. /// [ExcludeFromCodeCoverage] public class AssertException : RuntimeException { /// /// Initializes a new instance of the class. /// public AssertException() { } /// /// Initializes a new instance of the class. /// /// The exception message. public AssertException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// The exception message. /// The inner exception. public AssertException(string message, SException innerException) : base(message, innerException) { } /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected AssertException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } } } ================================================ FILE: src/CatLib.Core/Exception/LogicException.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using SException = System.Exception; namespace CatLib.Exception { /// /// Represents a logical exception encountered during execution. /// /// Logical exceptions are caused by logical errors during runtime. [ExcludeFromCodeCoverage] public class LogicException : RuntimeException { /// /// Initializes a new instance of the class. /// public LogicException() { } /// /// Initializes a new instance of the class. /// /// The exception message. public LogicException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// The exception message. /// The inner exception. public LogicException(string message, SException innerException) : base(message, innerException) { } /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected LogicException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } } } ================================================ FILE: src/CatLib.Core/Exception/RuntimeException.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Runtime.Serialization; using SException = System.Exception; namespace CatLib.Exception { /// /// Represents a generic runtime exception. /// public class RuntimeException : SException { /// /// Initializes a new instance of the class. /// public RuntimeException() { } /// /// Initializes a new instance of the class. /// /// The exception message. public RuntimeException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// The exception message. /// The inner exception. public RuntimeException(string message, SException innerException) : base(message, innerException) { } /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected RuntimeException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } } } ================================================ FILE: src/CatLib.Core/IO/CombineStream.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.IO; namespace CatLib.IO { /// /// allow multiple different streams to be combined into one stream. /// public class CombineStream : Stream { /// /// An array of multiple streams. /// private readonly Stream[] streams; /// /// Whether to close the sub stream when closing the combined stream. /// private readonly bool autoClosed; /// /// Indicates the position of the current cursor. /// private long globalPosition; /// /// Indicates the index of the current stream. /// private int index; /// /// The length of the . /// private long length; /// /// Initializes a new instance of the class. /// /// The left stream. /// The right stream. /// Whether to close the sub stream when closing the combined stream. public CombineStream(Stream left, Stream right, bool closed = false) : this(new[] { left, right }, closed) { } /// /// Initializes a new instance of the class. /// /// An array of the sub stream. /// Whether to close the sub stream when closing the combined stream. public CombineStream(Stream[] source, bool closed = false) { index = 0; streams = source; length = -1; autoClosed = closed; } /// /// Gets the length of the . /// public override long Length { get { if (length >= 0) { return length; } length = 0; foreach (var stream in streams) { length += stream.Length; } return length; } } /// public override bool CanSeek { get { foreach (var stream in streams) { if (!stream.CanSeek) { return false; } } return true; } } /// public override long Position { get => globalPosition; set => Seek(value, SeekOrigin.Begin); } /// public override bool CanRead { get { foreach (var stream in streams) { if (!stream.CanRead) { return false; } } return true; } } /// public override bool CanWrite => false; /// public override long Seek(long offset, SeekOrigin origin) { if (!CanSeek) { throw new NotSupportedException($"{nameof(CombineStream)} not supported {nameof(Seek)}."); } long newGloablPosition; switch (origin) { case SeekOrigin.Begin: newGloablPosition = offset; break; case SeekOrigin.Current: newGloablPosition = globalPosition + offset; break; case SeekOrigin.End: newGloablPosition = Length + offset; break; default: throw new NotSupportedException($"Not support {nameof(SeekOrigin)}: {origin}"); } if (newGloablPosition < 0 || newGloablPosition > Length) { throw new ArgumentOutOfRangeException($"{nameof(offset)} must large than zero or small then {nameof(Length)}"); } long localPosition = 0; var newIndex = index = CalculatedIndex(newGloablPosition, ref localPosition); streams[newIndex].Seek(localPosition, SeekOrigin.Begin); while (++newIndex < streams.Length) { streams[newIndex].Seek(0, SeekOrigin.Begin); } return globalPosition = newGloablPosition; } /// public override int Read(byte[] buffer, int offset, int count) { Guard.Requires(buffer != null); Guard.Requires(offset >= 0); Guard.Requires(count >= 0); Guard.Requires(buffer.Length - offset >= count); var result = 0; do { var read = streams[index].Read(buffer, offset, count); if (read <= 0 && index < streams.Length - 1) { index++; continue; } if (read <= 0) { break; } count -= read; offset += read; globalPosition += read; result += read; } while (count > 0); return result; } /// public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException($"{nameof(CombineStream)} not supported {nameof(Write)}."); } /// public override void SetLength(long value) { throw new NotSupportedException($"{nameof(CombineStream)} not supported {nameof(SetLength)}."); } /// public override void Flush() { throw new NotSupportedException($"{nameof(CombineStream)} not supported {nameof(Flush)}."); } /// /// Calculate sub stream index and relative positions. /// /// The position of the current cursor. /// The relative position. /// The index of the stream array. protected int CalculatedIndex(long globalPosition, ref long localPosition) { long len = 0; for (var i = 0; i < streams.Length; i++) { len += streams[i].Length; if (globalPosition > len) { continue; } localPosition = streams[i].Length - (len - globalPosition); return i; } throw new AssertException($"Failed to determine {nameof(localPosition)}"); } /// protected override void Dispose(bool disposing) { try { if (!autoClosed) { return; } foreach (var stream in streams) { stream?.Dispose(); } } finally { base.Dispose(disposing); } } } } ================================================ FILE: src/CatLib.Core/IO/RingBufferStream.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.IO; namespace CatLib.IO { /// /// Represents a ring buffer. /// public class RingBufferStream : Stream { private readonly long capacity; private readonly byte[] buffer; private readonly bool exposable; private readonly long mask; private long write; private long read; /// /// Initializes a new instance of the class. /// /// Capacity, need to be the power of 2 (upward). /// Whether possible to access internal arrays. public RingBufferStream(int capacity = 8192, bool exposable = true) { Guard.Requires(capacity > 0); this.capacity = ToPrime(capacity); buffer = new byte[this.capacity]; mask = this.capacity - 1; write = 0; read = 0; this.exposable = exposable; } /// /// Gets the capacity of the ring buffer. /// public int Capacity => (int)capacity; /// /// Gets writeable count. /// public int WriteableCount => (int)GetCanWriteSize(); /// /// Gets readable count. /// public int ReadableCount => (int)GetCanReadSize(); /// public override bool CanRead => true; /// public override bool CanSeek => false; /// public override bool CanWrite => true; /// public override long Length => write; /// public override long Position { get => read; set => Seek(value, SeekOrigin.Begin); } /// /// Gets the original array of ring buffer. /// /// Returns the original array of ring buffer. public byte[] GetBuffer() { if (!exposable) { throw new UnauthorizedAccessException("Unable to access original array"); } return buffer; } /// public override int Read(byte[] buffer, int offset, int count) { var readSize = Peek(buffer, offset, count); read += readSize; return readSize; } /// public override void Write(byte[] buffer, int offset, int count) { Guard.Requires(buffer != null); Guard.Requires(offset >= 0); Guard.Requires(count >= 0); Guard.Requires((buffer.Length - offset) >= count); var writeSize = GetCanWriteSize(); if (writeSize < count) { throw new RuntimeException("The memory is no longer writable because the buffer area is full."); } if (writeSize > count) { writeSize = count; } if (writeSize <= 0) { return; } var nextWritePos = write + writeSize; var realWritePos = write & mask; var realNextWritePos = nextWritePos & mask; if (realNextWritePos >= realWritePos) { Buffer.BlockCopy(buffer, offset, this.buffer, (int)realWritePos, (int)writeSize); } else { var tail = (int)(capacity - realWritePos); Buffer.BlockCopy(buffer, offset, this.buffer, (int)realWritePos, tail); if ((writeSize - tail) > 0) { Buffer.BlockCopy(buffer, offset + tail, this.buffer, 0, (int)writeSize - tail); } } write = nextWritePos; } /// /// Read the data of the ring buffer into , /// but do not advance the read position. /// /// The read data is filled into the current buffer. /// The starting offset of the buffer array. /// How many lengths of data expected to read. /// Actual read length. public int Peek(byte[] buffer, int offset, int count) { Guard.Requires(buffer != null); Guard.Requires(offset >= 0); Guard.Requires(count >= 0); Guard.Requires((buffer.Length - offset) >= count); var readSize = GetCanReadSize(); if (readSize > count) { readSize = count; } if (readSize <= 0) { return 0; } var nextReadPos = read + readSize; var realReadPos = read & mask; var realNextReadPos = nextReadPos & mask; if (realNextReadPos >= realReadPos) { Buffer.BlockCopy(this.buffer, (int)realReadPos, buffer, offset, (int)readSize); } else { var tail = (int)(capacity - realReadPos); Buffer.BlockCopy(this.buffer, (int)realReadPos, buffer, offset, tail); if (readSize - tail > 0) { Buffer.BlockCopy(this.buffer, 0, buffer, offset + tail, (int)readSize - tail); } } return (int)readSize; } /// /// Clear the ring buffer. /// public void Clear() { write = 0; read = 0; Array.Clear(buffer, 0, buffer.Length); } /// public override void Flush() { // ignore. } /// public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } /// public override void SetLength(long value) { throw new NotSupportedException(); } /// protected override void Dispose(bool disposing) { if (disposing) { Clear(); } base.Dispose(disposing); } /// /// Get the size of the byte stream that can be read. /// /// todo:1. private long GetCanReadSize() { return write - read; } /// /// Get the size of the byte stream that can be written. /// private long GetCanWriteSize() { return Math.Max(0, capacity - GetCanReadSize()); } /// /// Calculate the power of the nearest two. /// /// The starting number. /// The power of the nearest two. private int ToPrime(int min) { min = Math.Max(0, min); var result = 0; for (var i = 2; i < int.MaxValue; i <<= 1) { if (i < min) { continue; } result = i; break; } return result; } } } ================================================ FILE: src/CatLib.Core/IO/SegmentStream.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; using System.IO; namespace CatLib.IO { /// /// A can be used to wrap a stream of a specified slice。 /// Make the stream of the specified shard access like the traditional /// stream from the beginning to the end。. /// public class SegmentStream : WrapperStream { /// /// The initial position of the base stream. /// private readonly long initialPosition; /// /// The slice size. /// private readonly long partSize; /// /// Initializes a new instance of the class. /// /// The base stream. /// The slice size. public SegmentStream(Stream stream, long partSize = 0) : base(stream) { if (!stream.CanSeek) { throw new InvalidOperationException($"Base stream of {nameof(SegmentStream)} must be seekable"); } initialPosition = stream.Position; var remainingSize = stream.Length - stream.Position; if (partSize == 0 || remainingSize < partSize) { this.partSize = remainingSize; } else { this.partSize = partSize; } } /// public override long Length { get { var length = base.Length - initialPosition; if (length > partSize) { length = partSize; } return length; } } /// public override long Position { get => base.Position - initialPosition; set => base.Position = value; } /// /// Gets the remaining size. /// private long RemainingSize => partSize - Position; /// public override long Seek(long offset, SeekOrigin origin) { long position; switch (origin) { case SeekOrigin.Begin: position = initialPosition + offset; break; case SeekOrigin.Current: position = base.Position + offset; break; case SeekOrigin.End: position = base.Position + partSize + offset; break; default: throw new ArgumentOutOfRangeException(nameof(origin), origin, null); } if (position < initialPosition) { position = initialPosition; } else if (position > initialPosition + partSize) { position = initialPosition + partSize; } base.Seek(position, SeekOrigin.Begin); return Position; } /// public override int Read(byte[] buffer, int offset, int count) { var bytesToRead = count < RemainingSize ? count : (int)RemainingSize; return bytesToRead < 0 ? 0 : base.Read(buffer, offset, bytesToRead); } /// public override void SetLength(long value) { throw new NotSupportedException(); } /// public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } } ================================================ FILE: src/CatLib.Core/IO/StreamExtension.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.Util; using System; using System.IO; using System.Text; namespace CatLib.IO { /// /// The stream extension function. /// public static class StreamExtension { [ThreadStatic] private static byte[] buffer; private static byte[] Buffer { get { if (buffer == null) { buffer = new byte[4096]; } return buffer; } } /// /// Append the source stream to the destination stream. /// /// The source stream. /// The destination stream. /// Byte length of transmitted data. public static long AppendTo(this Stream source, Stream destination) { return source.AppendTo(destination, Buffer); } /// /// Append the source stream to the destination stream. /// /// The source stream. /// The destination stream. /// The buffer to use. /// Byte length of transmitted data. public static long AppendTo(this Stream source, Stream destination, byte[] buffer) { Guard.Requires(source != null); Guard.Requires(destination != null); long result = 0; int read; while ((read = source.Read(buffer, 0, buffer.Length)) > 0) { destination.Write(buffer, 0, read); result += read; } return result; } /// /// Read the stream's data and convert it to a string. /// /// The source stream. /// The encoding for data. /// Whether auto closed stream. /// The string. public static string ToText(this Stream source, Encoding encoding = null, bool closed = true) { Guard.Requires(source != null); try { if (!source.CanRead) { throw new LogicException($"Can not read stream, {nameof(source.CanRead)} == false"); } encoding = encoding ?? Encoding.Default; if (source is MemoryStream memoryStream) { byte[] internalBuffer; try { internalBuffer = memoryStream.GetBuffer(); } catch (UnauthorizedAccessException) { internalBuffer = memoryStream.ToArray(); } return encoding.GetString(internalBuffer, 0, (int)memoryStream.Length); } var length = 0; try { length = (int)source.Length; } catch (NotSupportedException) { // ignore } MemoryStream targetStream; if (length > 0 && length <= Buffer.Length) { targetStream = new MemoryStream(Buffer, 0, Buffer.Length, true, true); } else { targetStream = new MemoryStream(length); } using (targetStream) { var read = source.AppendTo(targetStream); return encoding.GetString(targetStream.GetBuffer(), 0, (int)read); } } finally { if (closed) { source.Dispose(); } } } /// /// Convert the specified string to a stream. /// /// The specified string. /// The string encoding. /// The stream instance. public static Stream ToStream(this string str, Encoding encoding = null) { return new MemoryStream((encoding ?? Encoding.Default).GetBytes(str)); } } } ================================================ FILE: src/CatLib.Core/IO/WrapperStream.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Util; using System; using System.Diagnostics.CodeAnalysis; using System.IO; namespace CatLib.IO { /// /// The wrapper stream. /// [ExcludeFromCodeCoverage] public class WrapperStream : Stream { /// /// Initializes a new instance of the class. /// public WrapperStream() { BaseStream = this; } /// /// Initializes a new instance of the class. /// /// The base stream. public WrapperStream(Stream stream) { Guard.Requires(stream != null); BaseStream = stream; } /// /// Gets the base stream. /// public Stream BaseStream { get; } /// public override bool CanRead => BaseStream.CanRead; /// public override bool CanSeek => BaseStream.CanSeek; /// public override bool CanWrite => BaseStream.CanWrite; /// public override long Position { get => BaseStream.Position; set => Seek(value, SeekOrigin.Begin); } /// public override long Length => BaseStream.Length; /// public override long Seek(long offset, SeekOrigin origin) { return BaseStream.Seek(offset, origin); } /// public override void Flush() { BaseStream.Flush(); } /// public override void Write(byte[] buffer, int offset, int count) { BaseStream.Write(buffer, offset, count); } /// public override void SetLength(long value) { BaseStream.SetLength(value); } /// public override int Read(byte[] buffer, int offset, int count) { return BaseStream.Read(buffer, offset, count); } } } ================================================ FILE: src/CatLib.Core/Properties/AssemblyInfo.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("CatLib.Core.Tests")] ================================================ FILE: src/CatLib.Core/Util/Arr.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using System; using System.Collections.Generic; using System.Linq; namespace CatLib.Util { /// /// Array helper. /// public static class Arr { /// /// Combine multiple specified arrays into one array. /// /// The type of array. /// The specified array. /// Returns an merged array. #pragma warning disable S2368 public static T[] Merge(params T[][] sources) #pragma warning restore S2368 { if (sources == null || sources.Length <= 0) { return Array.Empty(); } var totalSize = 0; foreach (var source in sources) { if (source == null || source.Length <= 0) { continue; } totalSize += source.Length; } if (totalSize <= 0) { return Array.Empty(); } var merged = new T[totalSize]; var length = 0; foreach (var source in sources) { if (source == null || source.Length <= 0) { continue; } Array.Copy(source, 0, merged, length, source.Length); length += source.Length; } return merged; } /// /// Get a specified number of random values from a specified array. /// /// The type of array. /// The specified array. /// The specified number. /// An array of the random value. public static T[] Rand(T[] sources, int number = 1) { if (sources == null || sources.Length <= 0) { return new T[number]; } return Slice(Shuffle(sources), 0, Math.Max(number, 1)); } /// /// Shuffle the elements in the specified array. /// /// The type of array. /// The specified array. /// The random seed. /// Return the disrupted array. public static T[] Shuffle(T[] sources, int? seed = null) { if (sources == null || sources.Length <= 0) { return Array.Empty(); } var requested = new T[sources.Length]; Array.Copy(sources, requested, sources.Length); var random = InternalHelper.MakeRandom(seed); for (var i = 0; i < requested.Length; i++) { var index = random.Next(0, requested.Length - 1); if (index == i) { continue; } var temporary = requested[i]; requested[i] = requested[index]; requested[index] = temporary; } return requested; } /// /// Removes an element of the specified length from the array. If /// the parameter is given, the new /// element is inserted from the position. /// /// The type of array. /// The specified array. /// /// Delete the start position of the element. /// If the value is set to a positive number, delete it from the beginning of the trip. /// If the value is set to a negative number, the absolute value is taken from the back. /// /// /// Number of deleted elements. /// If the value is set to a positive number, then the number of elements is returned。. /// If the value is set to a negative number, then remove the absolute position from the back to the front to delete. /// If the value is not set, then all elements from the position set by the parameter to the end of the array are returned. /// /// An array inserted at the start position. /// An removed array. public static T[] Splice(ref T[] sources, int start, int? length = null, T[] replaceSource = null) { if (sources == null || sources.Length <= 0) { return Array.Empty(); } InternalHelper.NormalizationPosition(sources.Length, ref start, ref length); var candidates = new T[length.Value]; if (length.Value == sources.Length) { Array.Copy(sources, candidates, sources.Length); sources = replaceSource ?? Array.Empty(); return candidates; } Array.Copy(sources, start, candidates, 0, length.Value); if (replaceSource == null || replaceSource.Length == 0) { var newSource = new T[sources.Length - length.Value]; if (start > 0) { Array.Copy(sources, 0, newSource, 0, start); } Array.Copy(sources, start + length.Value, newSource, start, sources.Length - (start + length.Value)); sources = newSource; } else { var newSource = new T[sources.Length - length.Value + replaceSource.Length]; if (start > 0) { Array.Copy(sources, 0, newSource, 0, start); } Array.Copy(replaceSource, 0, newSource, start, replaceSource.Length); Array.Copy(sources, start + length.Value, newSource, start + replaceSource.Length, sources.Length - (start + length.Value)); sources = newSource; } return candidates; } /// /// Crop the array to the desired position. /// /// The type of array. /// The source array. /// Crop range, negative numbers are trimmed from back to front. public static void Cut(ref T[] source, int position) { if (source == null || source.Length <= 0 || position == 0) { return; } if (Math.Abs(position) >= source.Length) { if (source.Length > 0) { Array.Resize(ref source, 0); } return; } if (position > 0) { var size = source.Length - position; Array.Copy(source, position, source, 0, size); Array.Resize(ref source, size); } else { Array.Resize(ref source, source.Length - Math.Abs(position)); } } /// /// Divide an array into new array blocks. /// The number of cells in each array is determined by /// the parameter. The number of cells in the last array may be a few. /// /// The type of array. /// The specified array. /// The size of the block. /// Return an array of the block. public static T[][] Chunk(T[] sources, int size) { if (sources == null || sources.Length <= 0) { return Array.Empty(); } size = Math.Max(1, size); var requested = new T[(sources.Length / size) + (sources.Length % size == 0 ? 0 : 1)][]; T[] chunks = null; for (var i = 0; i < sources.Length; i++) { var position = i / size; if (i % size == 0) { if (chunks != null) { requested[position - 1] = chunks; } chunks = new T[(i + size) <= sources.Length ? size : sources.Length - i]; } if (chunks == null) { throw new AssertException("Unexpected exception"); } chunks[i - (position * size)] = sources[i]; } requested[requested.Length - 1] = chunks; return requested; } /// /// Fill the array, if the specified array is passed in, it will be filled based on the specified array. /// /// The type of array. /// The starting index. /// The filling length. /// The filling value. /// The specified array. /// Returns an filled array. public static T[] Fill(int start, int length, T value, T[] sources = null) { Guard.Requires(start >= 0); Guard.Requires(length > 0); var count = start + length; var requested = new T[Math.Max(sources?.Length + length ?? count, count)]; if (start > 0 && sources != null) { Array.Copy(sources, requested, Math.Min(sources.Length, start)); } for (var i = start; i < count; i++) { requested[i] = value; } if (sources != null && start < sources.Length) { Array.Copy(sources, start, requested, count, sources.Length - start); } return requested; } /// /// Pass each value of the array to the callback function, if the callback /// function returns true, remove the corresponding element in the array /// and return the removed element. /// /// The type of array. /// The specified array. /// The callback. /// The predicate expected to removed. /// Returns an removed array. public static T[] Remove(ref T[] sources, Predicate predicate, bool expected = true) { Guard.Requires(predicate != null, $"Must set a {predicate}."); if (sources == null || sources.Length <= 0) { return Array.Empty(); } var candidateIndex = 0; var candidates = new T[sources.Length]; for (var i = sources.Length - 1; i >= 0; i--) { if (predicate.Invoke(sources[i]) != expected) { continue; } candidates[candidateIndex++] = sources[i]; RemoveAt(ref sources, i); } Array.Reverse(candidates, 0, candidateIndex); Array.Resize(ref candidates, candidateIndex); return candidates; } /// /// Each value in the source array is passed to the callback function. /// If the callback function is equal to the /// value, the current value in the input array is added to the result array. /// /// The type of array. /// The specified array. /// The callback. /// The expected value. /// Returns an filtered array. public static T[] Filter(IEnumerable sources, Predicate predicate, bool expected = true) { Guard.Requires(predicate != null, $"Must set a {predicate}."); if (sources == null) { return Array.Empty(); } var candidates = new LinkedList(); foreach (var result in sources) { if (predicate.Invoke(result) == expected) { candidates.AddLast(result); } } return candidates.ToArray(); } /// /// Pass the value of the iterator into the callback function, and the /// value returned by the custom function as the new array value. /// /// The type of array. /// The type of return value. /// The source iterator. /// The closure to process. /// Returns an new array. public static TReturn[] Map(IEnumerable source, Func closure) { Guard.Requires(closure != null, $"Must set a {closure}."); if (source == null) { return Array.Empty(); } var requested = new List(); foreach (var value in source) { requested.Add(closure.Invoke(value)); } return requested.ToArray(); } /// /// Delete the last element in the array and return the deleted element /// as the return value. /// /// The type of the array. /// The specified array. /// Returns removed element. public static T Pop(ref T[] sources) { Guard.Requires(sources != null, $"{nameof(sources)} should not be null."); Guard.Requires(sources.Length > 0, $"The number of elements needs to be greater than 0."); var candidate = sources[sources.Length - 1]; Array.Resize(ref sources, sources.Length - 1); return candidate; } /// /// Add one or more elements to the end of the array. /// /// The type of array. /// The specified array. /// The added elements. /// Returns the length of the new array. public static int Push(ref T[] sources, params T[] elements) { sources = sources ?? Array.Empty(); if (elements == null || elements.Length <= 0) { return sources.Length; } Array.Resize(ref sources, sources.Length + elements.Length); Array.Copy(elements, 0, sources, sources.Length - elements.Length, elements.Length); return sources.Length; } /// /// Pass a value from an array to a callback function and return a string. /// The function returns null if the array is empty and the parameter is not passed. /// If the parameter is specified, the parameter will be treated as the first value /// in the array, and if the array is empty, it will be the final return value (string). /// /// The type of array. /// The specified array. /// The closure process. /// The initial value. /// Returnd the processed string. public static string Reduce(IEnumerable sources, Func closure, object initial = null) { Guard.Requires(closure != null, $"Must set a {closure}."); if (sources == null) { return initial?.ToString(); } var requested = initial; foreach (var segments in sources) { requested = closure.Invoke(requested, segments); } return requested?.ToString(); } /// /// Take a value from the array according to the condition and return. /// /// The type of array. /// The specified array. /// /// Remove the starting position of the element. /// If the value is set to a positive number, it will be taken from the beginning of the trip. /// If the value is set to a negative number, the absolute value is taken from the back. /// /// /// Returns the length of the array. /// If the value is set to a positive number, then the number of elements is returned。. /// If the value is set to a negative number, then remove the absolute position from the back to the front to delete. /// If the value is not set, then all elements from the position set by the parameter to the end of the array are returned. /// /// Returns an new array. public static T[] Slice(T[] sources, int start, int? length = null) { if (sources == null || sources.Length <= 0) { return Array.Empty(); } InternalHelper.NormalizationPosition(sources.Length, ref start, ref length); var requested = new T[length.Value]; Array.Copy(sources, start, requested, 0, length.Value); return requested; } /// /// Removed the first element in the array and return the value of the removed element. /// /// The type of array. /// The specified array. /// Returns the removed value. public static T Shift(ref T[] sources) { Guard.Requires(sources != null, $"{nameof(sources)} should not be null."); Guard.Requires(sources.Length > 0, $"The number of elements needs to be greater than 0."); var candidate = sources[0]; var newSource = new T[sources.Length - 1]; Array.Copy(sources, 1, newSource, 0, sources.Length - 1); sources = newSource; return candidate; } /// /// Add a new element at the beginning of the array. /// /// The type of array. /// The specified array. /// The added element. /// Returns the length of the new array. public static int Unshift(ref T[] sources, params T[] elements) { sources = sources ?? Array.Empty(); if (elements == null || elements.Length <= 0) { return sources.Length; } var newSources = new T[sources.Length + elements.Length]; Array.Copy(elements, newSources, elements.Length); Array.Copy(sources, 0, newSources, elements.Length, sources.Length); sources = newSources; return sources.Length; } /// /// Return arrays in reverse order. /// /// The type of array. /// The specified array. /// /// The starting position of the starting element. /// If the value is set to a positive number, it will be taken from the beginning of the trip. /// If the value is set to a negative number, the absolute value is taken from the back. /// /// Returns the length of the array. /// If the value is set to a positive number, then the number of elements is returned。. /// If the value is set to a negative number, then remove the absolute position from the back to the front to delete. /// If the value is not set, then all elements from the position set by the parameter to the end of the array are returned. /// /// Returns inverted array. public static T[] Reverse(T[] sources, int start = 0, int? length = null) { if (sources == null || sources.Length <= 0) { return Array.Empty(); } if (sources.Length == 1) { return sources; } InternalHelper.NormalizationPosition(sources.Length, ref start, ref length); var temporarySource = new T[sources.Length]; Array.Copy(sources, temporarySource, sources.Length); Array.Reverse(temporarySource, start, length.Value); var resquested = new T[length.Value]; Array.Copy(temporarySource, start, resquested, 0, length.Value); return resquested; } /// /// Retrieve the initial element index that matches all matching values from the array. /// If it returns -1, it means that it does not appear. /// /// The type of array. /// The specified array. /// The value to match, if there are more than one, only all matches will match. /// Returning -1 means that the specified value was not retrieved. public static int IndexOf(T[] source, params T[] match) { if (match == null || match.Length <= 0 || source == null || source.Length <= 0) { return -1; } for (var sourceIndex = 0; sourceIndex < source.Length; sourceIndex++) { if (!source[sourceIndex].Equals(match[0])) { continue; } var isFinded = true; for (var matchIndex = 0; matchIndex < match.Length; matchIndex++) { if ((sourceIndex + matchIndex) < source.Length && source[sourceIndex + matchIndex].Equals(match[matchIndex])) { continue; } isFinded = false; break; } if (isFinded) { return sourceIndex; } } return -1; } /// /// Retrieve the subscript of the specified arbitrary matching value from the array. /// If it returns -1, it means that it does not appear. /// /// The type of array. /// The specified array. /// The value to match.Match as long as there is an element match. /// Returning -1 means that the specified value was not retrieved. public static int IndexOfAny(T[] source, params T[] match) { if (match == null || match.Length <= 0 || source == null || source.Length <= 0) { return -1; } for (var sourceIndex = 0; sourceIndex < source.Length; sourceIndex++) { for (var matchIndex = 0; matchIndex < match.Length; matchIndex++) { if (source[sourceIndex].Equals(match[matchIndex])) { return sourceIndex; } } } return -1; } /// /// Exclude the specified value in the array. /// /// The type of array. /// The source array. /// An array of exclude value. /// Returns an array of processed. public static T[] Difference(T[] sources, params T[] matches) { if (sources == null || sources.Length <= 0 || matches == null || matches.Length <= 0) { return sources; } return Filter(sources, (source) => { foreach (var match in matches) { if (source.Equals(match)) { return false; } } return true; }); } /// /// Remove and return the array element of the specified index. /// If the index is passed a negative number then it will be removed from the end. /// /// The type of array. /// The specified array. /// The index of array. /// Default value if index not found. /// Returns removed element. public static T RemoveAt(ref T[] sources, int index, T defaultValue = default) { if (sources == null || sources.Length <= 0 || index >= sources.Length) { return defaultValue; } var candidates = Splice(ref sources, index, 1); return candidates.Length > 0 ? candidates[0] : defaultValue; } /// /// Pass the specified array to the callback test. /// The function returns false only if all elements pass the checker are false. /// /// The type of array. /// The specified array. /// The callback. /// True if pass the test. public static bool Test(IEnumerable sources, Predicate predicate) { return Test(sources, predicate, out _); } /// /// Pass the specified array to the callback test. /// The function returns false only if all elements pass the checker are false. /// /// The type of array. /// The specified array. /// The callback. /// Test passed element. /// True if pass the test. public static bool Test(IEnumerable sources, Predicate predicate, out T match) { Guard.Requires(predicate != null, $"Must set a {predicate}."); match = default; if (sources == null) { return false; } foreach (var source in sources) { if (predicate(source)) { match = source; return true; } } return false; } /// /// Finds the specified element in the specified array, and replaces it with a substitute value if found, /// otherwise adds a replacement value at the end of the specified array. /// /// The type of array. /// The specified array. /// The callback to find element. /// The replacement value. public static void Set(ref T[] sources, Predicate predicate, T value) { Guard.Requires(predicate != null, $"Must set a {predicate}."); sources = sources ?? Array.Empty(); for (var index = 0; index < sources.Length; index++) { if (!predicate(sources[index])) { continue; } sources[index] = value; return; } Push(ref sources, value); } } } ================================================ FILE: src/CatLib.Core/Util/Guard.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; using System.Collections.Generic; using System.Reflection; using SException = System.Exception; namespace CatLib.Util { /// /// Guard the code. /// public sealed class Guard { private static Guard that; private static IDictionary exceptionFactory; /// /// Indicates an extended exception factory. /// /// The exception message. /// The inner exception instance. /// The user object. /// An exception instance. public delegate SException ExtendException(string message, SException innerException, object state); /// /// Gets the singleton instance of the Guard functionality. /// /// Users can use this to plug-in custom assertions through csharp extension methods. public static Guard That { get { if (that == null) { that = new Guard(); } return that; } } /// /// Verifies a condition and throws an exception if the condition of the contract fails. /// /// Exception triggered when validation fails. /// The condition of the contract. /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference. /// State will be passed to the registered exception build factory. [System.Diagnostics.DebuggerNonUserCode] public static void Requires(bool condition, string message = null, SException innerException = null, object state = null) where TException : SException, new() { Requires(typeof(TException), condition, message, innerException, state); } /// /// Verifies a condition and throws an exception if the condition of the contract fails. /// /// Exception triggered when validation fails. /// The condition of the contract. /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference. /// State will be passed to the registered exception build factory. [System.Diagnostics.DebuggerNonUserCode] public static void Requires(Type exception, bool condition, string message = null, SException innerException = null, object state = null) { if (condition) { return; } throw CreateExceptionInstance(exception, message, innerException, state); } /// /// The verification parameter is not Null. /// /// The parameter value. /// The parameter name. /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference. [System.Diagnostics.DebuggerNonUserCode] public static void ParameterNotNull(object argumentValue, string argumentName, string message = null, SException innerException = null) { if (argumentValue != null) { return; } message = message ?? $"Parameter {argumentName} not allowed for null. please check the function input."; var exception = new ArgumentNullException(argumentName, message); if (innerException != null) { SetField(exception, "_innerException", innerException); } throw exception; } /// /// Extend an exception generation factory. /// /// The type of exception. /// The exception factory. [System.Diagnostics.DebuggerNonUserCode] public static void Extend(ExtendException factory) { Extend(typeof(T), factory); } /// /// Extend an exception generation factory. /// /// The type of exception. /// The exception factory. [System.Diagnostics.DebuggerNonUserCode] public static void Extend(Type exception, ExtendException factory) { VerfiyExceptionFactory(); exceptionFactory[exception] = factory; } private static SException CreateExceptionInstance(Type exceptionType, string message, SException innerException, object state) { if (!typeof(SException).IsAssignableFrom(exceptionType)) { throw new ArgumentException( $"Type: {exceptionType} must be inherited from: {typeof(SException)}.", nameof(exceptionType)); } VerfiyExceptionFactory(); if (exceptionFactory.TryGetValue(exceptionType, out ExtendException factory)) { var ret = factory(message, innerException, state); if (ret != null) { return ret; } } var exception = Activator.CreateInstance(exceptionType); if (!string.IsNullOrEmpty(message)) { SetField(exception, "_message", message); } if (innerException != null) { SetField(exception, "_innerException", innerException); } return (SException)exception; } private static void VerfiyExceptionFactory() { if (exceptionFactory == null) { exceptionFactory = new Dictionary(); } } private static void SetField(object obj, string field, object value) { var flag = BindingFlags.Instance | BindingFlags.NonPublic; var fieldInfo = obj.GetType().GetField(field, flag); if (fieldInfo != null) { fieldInfo.SetValue(obj, value); } } } } ================================================ FILE: src/CatLib.Core/Util/InternalHelper.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; namespace CatLib.Util { /// /// Represents an internal generic helper class. /// internal static class InternalHelper { internal static Random MakeRandom(int? seed = null) { return new Random(seed.GetValueOrDefault(MakeSeed())); } internal static int MakeSeed() { return Environment.TickCount ^ Guid.NewGuid().GetHashCode(); } internal static void NormalizationPosition(int sourceLength, ref int start, ref int? length) { start = (start >= 0) ? Math.Min(start, sourceLength) : Math.Max(sourceLength + start, 0); if (length == null) { length = Math.Max(sourceLength - start, 0); return; } length = (length >= 0) ? Math.Min(length.Value, sourceLength - start) : Math.Max(sourceLength + length.Value - start, 0); } } } ================================================ FILE: src/CatLib.Core/Util/SortSet.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; #pragma warning disable S2589 #pragma warning disable S2583 #pragma warning disable S2259 namespace CatLib.Util { /// /// Represents an ordered set, implemented by a jump list. /// /// The element type. /// The score type. [DebuggerDisplay("Count = {" + nameof(Count) + "}")] #pragma warning disable CA1710 public sealed class SortSet : IEnumerable where TScore : IComparable #pragma warning restore CA1710 { private readonly int maxLevel; private readonly SkipNode header; private readonly double probability; private readonly Random random = new Random(); private readonly IComparer comparer; private readonly Dictionary elementMapping = new Dictionary(); private int level; private SkipNode tail; /// /// Initializes a new instance of the class. /// /// Probability coefficient of possible number of level(0-1). /// The max level. public SortSet(double probable = 0.25, int maxLevel = 32) { Guard.Requires(probable < 1); Guard.Requires(probable > 0); maxLevel = Math.Max(1, maxLevel); probability = probable * 0xFFFF; level = 1; this.maxLevel = maxLevel; header = new SkipNode { Level = new SkipNode.SkipNodeLevel[maxLevel], }; } /// /// Initializes a new instance of the class. /// /// The comparer. /// Probability coefficient of possible number of level(0-1). /// The max level. public SortSet(IComparer comparer, double probable = 0.25, int maxLevel = 32) : this(probable, maxLevel) { Guard.Requires(comparer != null); this.comparer = comparer; } /// /// Gets the element count. /// public int Count { get; private set; } /// /// Get the element of the specified ranking. /// /// The ranking(0 is the bottom). /// The element. public TElement this[int rank] => GetElementByRank(rank); /// /// Clear the . /// public void Clear() { for (var i = 0; i < header.Level.Length; ++i) { header.Level[i].Span = 0; header.Level[i].Forward = null; } tail = null; level = 1; Count = 0; elementMapping.Clear(); } /// /// Get iterator. /// /// True if iterator element forward. /// Returns iterator instance. public IEnumerable GetIterator(bool forward = true) { return new Iterator(this, forward); } /// IEnumerator IEnumerable.GetEnumerator() { return new Iterator(this, true); } /// IEnumerator IEnumerable.GetEnumerator() { return new Iterator(this, true); } /// /// Convert to array. /// /// An element array. public TElement[] ToArray() { var elements = new TElement[Count]; var node = header.Level[0]; var i = 0; while (node.Forward != null) { elements[i++] = node.Forward.Element; node = node.Forward.Level[0]; } return elements; } /// /// Gets the first element. /// /// The first element. public TElement First() { if (header.Level[0].Forward != null) { return header.Level[0].Forward.Element; } throw new InvalidOperationException($"{nameof(SortSet)} is empty."); } /// /// Gets the last element. /// /// The last element. public TElement Last() { if (tail != null) { return tail.Element; } throw new InvalidOperationException($"{nameof(SortSet)} is empty"); } /// /// Remove and return the elements at the head. /// /// The first element. public TElement Shift() { if (!Remove(header.Level[0].Forward, out TElement ret)) { throw new InvalidOperationException($"{nameof(SortSet)} is empty"); } return ret; } /// /// Remove and return the elements at the end. /// /// The last element. public TElement Pop() { if (!Remove(tail, out TElement ret)) { throw new InvalidOperationException($"{nameof(SortSet)} is empty"); } return ret; } /// /// Add a new record in sortset. /// /// The element. /// The score. public void Add(TElement element, TScore score) { Guard.Requires( element != null, $"{nameof(element)} should not be null."); Guard.Requires( score != null, $"{nameof(score)} should not be null."); if (elementMapping.TryGetValue(element, out TScore dictScore)) { Remove(element, dictScore); } AddElement(element, score); } /// /// Whether is contains the specided element. /// /// The element. /// True if contains the specided element. public bool Contains(TElement element) { Guard.Requires( element != null, $"{nameof(element)} should not be null."); return elementMapping.ContainsKey(element); } /// /// Gets the element's score. /// /// The element. /// The element's score. public TScore GetScore(TElement element) { Guard.Requires( element != null, $"{nameof(element)} should not be null."); if (!elementMapping.TryGetValue(element, out TScore score)) { throw new KeyNotFoundException(); } return score; } /// /// Get the number of elements in the specified score range. /// /// The start score.(contain). /// The end score.(contain). /// The number of elements in the score range. public int GetRangeCount(TScore start, TScore end) { Guard.Requires( start != null, $"{nameof(start)} should not be null."); Guard.Requires( end != null, $"{nameof(end)} should not be null."); Guard.Requires( Compare(start, end) <= 0, $"{nameof(start)} should not larger than {nameof(end)}."); int rank = 0, leftRank = 0; SkipNode leftCursor = null; var isRight = false; var cursor = header; do { for (var i = level - 1; i >= 0; --i) { while (cursor.Level[i].Forward != null && ((!isRight && Compare(cursor.Level[i].Forward.Score, start) < 0) || (isRight && Compare(cursor.Level[i].Forward.Score, end) <= 0))) { rank += cursor.Level[i].Span; cursor = cursor.Level[i].Forward; } if (leftCursor != null) { continue; } // First set the skip cursor and ranking of the top leftmost level. // Than the cursor will start to look down. leftCursor = cursor; leftRank = rank; } if (isRight) { continue; } cursor = leftCursor; var foo = rank; rank = leftRank; leftRank = foo; } #pragma warning disable S1121 while (isRight = !isRight); #pragma warning restore S1121 return Math.Max(0, rank - leftRank); } /// /// Remove an element from the sortset, return false if the element does not exist. /// /// The element. /// Whether is removed the element. public bool Remove(TElement element) { Guard.Requires( element != null, $"{nameof(element)} should not be null."); return elementMapping.TryGetValue(element, out TScore score) && Remove(element, score); } /// /// Remove elements from the rank range. /// /// The start rank.(contains, 0 bottom). /// The end rank.(contains, 0 bottom). /// Returns the removed elements count. public int RemoveRangeByRank(int startRank, int stopRank) { startRank = Math.Max(startRank, 0); Guard.Requires( startRank <= stopRank, $"{nameof(startRank)} should not larger than {nameof(stopRank)}."); int traversed = 0, removed = 0; var update = new SkipNode[maxLevel]; var cursor = header; for (var i = level - 1; i >= 0; --i) { while (cursor.Level[i].Forward != null && (traversed + cursor.Level[i].Span <= startRank)) { traversed += cursor.Level[i].Span; cursor = cursor.Level[i].Forward; } update[i] = cursor; } cursor = cursor.Level[0].Forward; while (cursor != null && traversed <= stopRank) { var next = cursor.Level[0].Forward; elementMapping.Remove(cursor.Element); DeleteNode(cursor, update); ++removed; ++traversed; cursor = next; } return removed; } /// /// Remove elements from the score range. /// /// The start score.(contains). /// The end score.(contains). /// Returns removed elements count. public int RemoveRangeByScore(TScore startScore, TScore stopScore) { Guard.Requires( startScore != null, $"{nameof(startScore)} should not be null."); Guard.Requires( stopScore != null, $"{nameof(stopScore)} should not be null."); Guard.Requires( Compare(startScore, stopScore) <= 0, $"{nameof(startScore)} should not larger than {nameof(stopScore)}."); var removed = 0; var update = new SkipNode[maxLevel]; var cursor = header; for (var i = level - 1; i >= 0; --i) { while (cursor.Level[i].Forward != null && Compare(cursor.Level[i].Forward.Score, startScore) < 0) { cursor = cursor.Level[i].Forward; } update[i] = cursor; } cursor = cursor.Level[0].Forward; while (cursor != null && Compare(cursor.Score, stopScore) <= 0) { var next = cursor.Level[0].Forward; elementMapping.Remove(cursor.Element); DeleteNode(cursor, update); ++removed; cursor = next; } return removed; } /// /// Get specific element rank. /// /// The specific element. /// Returns the element rank(0 bottom) -1 means not found element. public int GetRank(TElement element) { Guard.Requires( element != null, $"{nameof(element)} should not be null."); return elementMapping.TryGetValue(element, out TScore dictScore) ? GetRank(element, dictScore) : -1; } /// /// Get the reverse ranking of the specified element. /// /// The specified element. /// The element's rank(0 bottom)-1 means not found element. public int GetRevRank(TElement element) { Guard.Requires( element != null, $"{nameof(element)} should not be null."); var rank = GetRank(element); return rank < 0 ? rank : Count - rank - 1; } /// /// Get the elements in the ranking range. /// /// The start rank(contains). /// The stop rank(contains). /// An array of the elements. public TElement[] GetElementRangeByRank(int startRank, int stopRank) { startRank = Math.Max(startRank, 0); Guard.Requires( startRank <= stopRank, $"{nameof(startRank)} should not larger than {nameof(stopRank)}."); int traversed = 0; var cursor = header; for (var i = level - 1; i >= 0; --i) { while (cursor.Level[i].Forward != null && (traversed + cursor.Level[i].Span <= startRank)) { traversed += cursor.Level[i].Span; cursor = cursor.Level[i].Forward; } } cursor = cursor.Level[0].Forward; var result = new List(); while (cursor != null && traversed <= stopRank) { result.Add(cursor.Element); ++traversed; cursor = cursor.Level[0].Forward; } return result.ToArray(); } /// /// Get the elements in the score range. /// /// The start score(contains). /// The end score(contains). /// An array of the elements. public TElement[] GetElementRangeByScore(TScore startScore, TScore stopScore) { Guard.Requires( startScore != null, $"{nameof(startScore)} should not be null."); Guard.Requires( stopScore != null, $"{nameof(stopScore)} should not be null."); Guard.Requires( Compare(startScore, stopScore) <= 0, $"{nameof(startScore)} should not larger than {nameof(stopScore)}."); var cursor = header; for (var i = level - 1; i >= 0; --i) { while (cursor.Level[i].Forward != null && Compare(cursor.Level[i].Forward.Score, startScore) < 0) { cursor = cursor.Level[i].Forward; } } cursor = cursor.Level[0].Forward; var result = new List(); while (cursor != null && Compare(cursor.Score, stopScore) <= 0) { result.Add(cursor.Element); cursor = cursor.Level[0].Forward; } return result.ToArray(); } /// /// Get element by rank. /// /// The rank(0 bottom). /// The element. public TElement GetElementByRank(int rank) { rank = Math.Max(0, rank); rank += 1; var traversed = 0; var cursor = header; for (var i = level - 1; i >= 0; i--) { while (cursor.Level[i].Forward != null && (traversed + cursor.Level[i].Span) <= rank) { traversed += cursor.Level[i].Span; cursor = cursor.Level[i].Forward; } if (traversed == rank) { return cursor.Element; } } if (Count > 0) { throw new ArgumentOutOfRangeException($"Rank is out of range [{rank}]"); } throw new InvalidOperationException("SortSet is Null"); } /// /// Get element by reverse rank. /// /// The rank.(0 bottom). /// The element. public TElement GetElementByRevRank(int rank) { return GetElementByRank(Count - rank - 1); } /// /// Add an element in the sortset. /// /// The element. /// The score. private void AddElement(TElement element, TScore score) { int i; elementMapping.Add(element, score); var update = new SkipNode[maxLevel]; var cursor = header; var rank = new int[maxLevel]; // Find from high to low skip level for (i = level - 1; i >= 0; --i) { // Rank is the starting point of the node of the previous level as the starting point. rank[i] = i == (level - 1) ? 0 : rank[i + 1]; while (cursor.Level[i].Forward != null && (Compare(cursor.Level[i].Forward.Score, score) < 0)) { rank[i] += cursor.Level[i].Span; cursor = cursor.Level[i].Forward; } // Place the last node found in the node that needs to be updated update[i] = cursor; } var newLevel = GetRandomLevel(); if (newLevel > level) { for (i = level; i < newLevel; ++i) { rank[i] = 0; update[i] = header; update[i].Level[i].Span = Count; } level = newLevel; } // Point the cursor to a new skip node cursor = new SkipNode { Element = element, Score = score, Level = new SkipNode.SkipNodeLevel[newLevel], }; for (i = 0; i < newLevel; ++i) { cursor.Level[i].Forward = update[i].Level[i].Forward; update[i].Level[i].Forward = cursor; cursor.Level[i].Span = update[i].Level[i].Span - (rank[0] - rank[i]); update[i].Level[i].Span = (rank[0] - rank[i]) + 1; } for (i = newLevel; i < level; ++i) { ++update[i].Level[i].Span; } cursor.Backward = (update[0] == header) ? null : update[0]; if (cursor.Level[0].Forward != null) { cursor.Level[0].Forward.Backward = cursor; } else { tail = cursor; } ++Count; } /// /// Remove the skip node. /// /// The skip node. /// The removed element. /// Whether is removed the element. private bool Remove(SkipNode node, out TElement element) { if (node == null) { element = default; return false; } var result = node.Element; if (!Remove(node.Element, node.Score)) { element = default; return false; } element = result; return true; } /// /// Remove the skip node. /// /// The element. /// The score. /// Whether is removed the element. private bool Remove(TElement element, TScore score) { Guard.Requires( element != null, $"{nameof(element)} should not be null."); Guard.Requires( score != null, $"{nameof(score)} should not be null."); var update = new SkipNode[maxLevel]; var cursor = header; for (var i = level - 1; i >= 0; --i) { while (IsFindNext(cursor.Level[i].Forward, element, score, i)) { cursor = cursor.Level[i].Forward; } update[i] = cursor; } cursor = update[0].Level[0].Forward; if (cursor == null || Compare(cursor.Score, score) != 0 || !cursor.Element.Equals(element)) { return false; } elementMapping.Remove(element); DeleteNode(cursor, update); return true; } /// /// Get the element rank. /// /// The element. /// The element score. /// The element rank. private int GetRank(TElement element, TScore score) { var rank = 0; var cursor = header; for (var i = level - 1; i >= 0; --i) { while (IsFindNext(cursor.Level[i].Forward, element, score, i)) { rank += cursor.Level[i].Span; cursor = cursor.Level[i].Forward; } } cursor = cursor.Level[0].Forward; if (cursor != null && cursor != header && cursor.Element != null && cursor.Element.Equals(element)) { return rank; } return -1; } /// /// Determine if you need to find the next node. /// /// The skip not. /// The element. /// The element score. /// The level. /// True if find next. private bool IsFindNext(SkipNode node, TElement element, TScore score, int level) { if (node == null) { return false; } var compare = Compare(node.Score, score); if (compare < 0 || compare > 0) { return compare < 0; } // If the level is greater than 0, it means that it is possible // to directly locate element 2, resulting in a bug. // So we think that the level is greater than 0, then the value // is equal and still returns the pre-order jump node. // // ---------------------------------------------------- // | element 1(score:50) | element 2 (score:50) // ---------------------------------------------------- if (level > 0) { return false; } return !node.Element.Equals(element); } /// /// Delete the skip node. /// /// The skip node. /// The updated node list. private void DeleteNode(SkipNode cursor, SkipNode[] update) { for (var i = 0; i < level; ++i) { if (update[i].Level[i].Forward == cursor) { update[i].Level[i].Span += cursor.Level[i].Span - 1; update[i].Level[i].Forward = cursor.Level[i].Forward; } else { update[i].Level[i].Span -= 1; } } if (cursor.Level[0].Forward != null) { cursor.Level[0].Forward.Backward = cursor.Backward; } else { tail = cursor.Backward; } while (level > 1 && header.Level[level - 1].Forward == null) { --level; } cursor.IsDeleted = true; --Count; } /// /// Get the rand level. /// /// The rand level. private int GetRandomLevel() { var newLevel = 1; while (random.Next(0, 0xFFFF) < probability) { ++newLevel; } return (newLevel < maxLevel) ? newLevel : maxLevel; } /// /// Compare left and right values. /// /// The left value. /// The right value. /// Return a value indicating which value is larger. private int Compare(TScore left, TScore right) { return comparer?.Compare(left, right) ?? left.CompareTo(right); } /// /// The default iterator. /// #pragma warning disable CA1710 public struct Iterator : IEnumerator, IEnumerable #pragma warning restore CA1710 { /// /// The sorset instance. /// private readonly SortSet collection; /// /// Whether to traverse from the forward. /// private readonly bool forward; /// /// The current node. /// private SkipNode current; /// /// Initializes a new instance of the struct. /// /// The sortset instnace. /// Whether to traverse from the forward. internal Iterator(SortSet collection, bool forward) { this.collection = collection; this.forward = forward; current = forward ? collection.header : null; } /// public TElement Current { get { return current.Element; } } /// object IEnumerator.Current { get { return current.Element; } } /// public void Dispose() { // ignore. } /// public IEnumerator GetEnumerator() { return this; } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// public bool MoveNext() { if (forward) { do { current = current.Level[0].Forward; } while (current != null && current.IsDeleted); return current != null; } if (current == null) { do { current = collection.tail; } while (current != null && current.IsDeleted); return current != null; } do { current = current.Backward; } while (current != null && current.IsDeleted); return current != null; } /// void IEnumerator.Reset() { current = forward ? collection.header : null; } } /// /// Represents a skip node. /// private class SkipNode { /// /// Gets or sets the element. /// public TElement Element { get; set; } /// /// Gets or sets the score. /// public TScore Score { get; set; } /// /// Gets or sets a value indicating whether was deleted. /// public bool IsDeleted { get; set; } /// /// Gets or sets the previous node. /// public SkipNode Backward { get; set; } /// /// Gets or sets the skip node levels. /// public SkipNodeLevel[] Level { get; set; } internal struct SkipNodeLevel { /// /// The next skip node. /// internal SkipNode Forward; /// /// The number represents how many nodes are crossed with the next node. /// internal int Span; } } } } ================================================ FILE: src/CatLib.Core/Util/Str.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System; using System.Text; using System.Text.RegularExpressions; namespace CatLib.Util { /// /// String helper. /// public static class Str { /// /// The space string. /// public const string Space = " "; /// /// Fill types. /// public enum PadType { /// /// Fill both sides of the string. If it is not even, the right side gets extra padding. /// Both, /// /// Fill the left side of the string. /// Left, /// /// Fill the right side of the string. /// Right, } /// /// Get the method name expressed by the string. /// /// The string will extract method name. /// The method name. public static string Method(string pattern) { if (string.IsNullOrEmpty(pattern)) { return string.Empty; } var chars = new char[pattern.Length]; var count = 0; for (var i = pattern.Length - 1; i >= 0; i--) { var segment = pattern[i]; if ((segment >= 'A' && segment <= 'Z') || (segment >= 'a' && segment <= 'z') || (segment >= '0' && segment <= '9') || segment == '_') { chars[count++] = segment; continue; } if (count > 0) { break; } } for (var i = count - 1; i >= 0; i--) { if (chars[i] >= '0' && chars[i] <= '9') { count--; continue; } break; } Array.Resize(ref chars, count); Array.Reverse(chars); return new string(chars); } /// /// Translate the specified string into an asterisk match expression and test. /// /// The match pattern. /// The match value. /// True if value is matched. public static bool Is(string pattern, string value) { return pattern == value || Regex.IsMatch(value, "^" + AsteriskWildcard(pattern) + "$"); } /// /// Translate the specified string into an asterisk match expression and test. /// Returns true if any match patten matches. /// /// The type of source array. /// The match pattern. /// The match value. /// True if matches. public static bool Is(string[] patterns, T value) { if (patterns == null || value == null) { return false; } foreach (var pattern in patterns) { if (Is(pattern, value.ToString())) { return true; } } return false; } /// /// Translate the specified string into an asterisk match expression. /// /// The match pattern. /// Returns processed string. public static string AsteriskWildcard(string pattern) { pattern = Regex.Escape(pattern); return pattern.Replace(@"\*", ".*?"); } /// /// Splits the string into an array based on length, and the /// last element may be less than the required length. /// /// The specified string. /// Specify the length of each array element. /// Returns an array of the string. public static string[] Split(string str, int length = 1) { if (string.IsNullOrEmpty(str)) { return Array.Empty(); } length = Math.Max(1, length); var ret = new string[(str.Length / length) + (str.Length % length == 0 ? 0 : 1)]; for (var i = 0; i < str.Length; i += length) { ret[i / length] = str.Substring(i, Math.Min(str.Length - i, length)); } return ret; } /// /// Repeat the specified number of times the string. /// /// String that needs to be repeated. /// Number of repetitions. /// Return the repeated string. public static string Repeat(string str, int num) { num = Math.Max(0, num); if (string.IsNullOrEmpty(str) || num == 0) { return string.Empty; } var ret = new StringBuilder(); for (var i = 0; i < num; i++) { ret.Append(str); } return ret.ToString(); } /// /// Shuffle all characters in the string. /// /// The specified string. /// The random seed. /// Returns disrupted string. public static string Shuffle(string str, int? seed = null) { if (string.IsNullOrEmpty(str)) { return string.Empty; } var random = InternalHelper.MakeRandom(seed); var ret = new string[str.Length]; for (var i = 0; i < str.Length; i++) { var index = random.Next(0, str.Length - 1); ret[i] = ret[i] ?? str.Substring(i, 1); ret[index] = ret[index] ?? str.Substring(index, 1); if (index == i) { continue; } #pragma warning disable S4143 var temporary = ret[i]; ret[i] = ret[index]; ret[index] = temporary; #pragma warning restore S4143 } return Arr.Reduce(ret, (a, b) => a + b, string.Empty); } /// /// Calculate the number of times a substring appears in a string. /// This function does not count overlapping substrings. /// /// The specified string. /// The substring. /// The starting position. /// The length to calculate. /// The string comparison. /// Returns the number of times a substring appears. -1 means unable to calculate. public static int SubstringCount(string str, string substr, int start = 0, int? length = null, StringComparison comparison = StringComparison.CurrentCultureIgnoreCase) { if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(substr)) { return 0; } InternalHelper.NormalizationPosition(str.Length, ref start, ref length); var count = 0; while (length.Value > 0) { int index; if ((index = str.IndexOf(substr, start, length.Value, comparison)) < 0) { break; } count++; length -= index + substr.Length - start; start = index + substr.Length; } return count; } /// /// Reverse specified string. /// /// The specified string. /// Returns reversed string. public static string Reverse(string str) { var chars = str.ToCharArray(); Array.Reverse(chars); return new string(chars); } /// /// Fill the string with the new length. /// /// The new string length. If the value is less than the original length of the string, no action is taken. /// The string to be filled. /// A string to be used for padding. The default is blank. /// /// Fill in which side of the string. /// Fill both sides of the string. If not even, get extra padding on the right side. /// Fill the left side of the string. /// Fill the right side of the string. /// /// Returns filled string. public static string Pad(int length, string str = null, string padStr = null, PadType type = PadType.Right) { str = str ?? string.Empty; var needlePadding = length - str.Length; if (needlePadding <= 0) { return str; } int rightPadding; var leftPadding = rightPadding = 0; if (type == PadType.Both) { leftPadding = needlePadding >> 1; rightPadding = (needlePadding >> 1) + (needlePadding % 2 == 0 ? 0 : 1); } else if (type == PadType.Right) { rightPadding = needlePadding; } else { leftPadding = needlePadding; } padStr = padStr ?? Space; padStr = padStr.Length <= 0 ? Space : padStr; var leftPadCount = (leftPadding / padStr.Length) + (leftPadding % padStr.Length == 0 ? 0 : 1); var rightPadCount = (rightPadding / padStr.Length) + (rightPadding % padStr.Length == 0 ? 0 : 1); return Repeat(padStr, leftPadCount).Substring(0, leftPadding) + str + Repeat(padStr, rightPadCount).Substring(0, rightPadding); } /// /// Finds the specified value in the string and returns the rest. /// If not found, return the specified string itself. /// /// The specified string. /// The search value. /// The remaining part. public static string After(string str, string search) { if (string.IsNullOrEmpty(str)) { return string.Empty; } if (string.IsNullOrEmpty(search)) { return str ?? string.Empty; } var index = str.IndexOf(search, StringComparison.Ordinal); return index < 0 ? str : str.Substring(index + search.Length, str.Length - index - search.Length); } /// /// Determine whether the specified string contains the specified substring. /// Substrings are case sensitive. /// /// /// The specified string. /// An array of the specified substring. /// True if contains substring. public static bool Contains(string str, params string[] needles) { needles = needles ?? Array.Empty(); foreach (var needle in needles) { if (str == needle || str.Contains(needle)) { return true; } } return false; } /// /// Replace the match in the specified string. /// /// An array of the match string. /// The replacement value. /// The specified string. /// Returns the replacement string. public static string Replace(string[] matches, string replace, string str) { matches = matches ?? Array.Empty(); replace = replace ?? string.Empty; if (string.IsNullOrEmpty(str)) { return string.Empty; } foreach (var match in matches) { if (match == null) { continue; } str = str.Replace(match, replace); } return str; } /// /// Replace the first occurrence of a match in the specified string. /// This function is case sensitive. /// /// The match string. /// The replacement value. /// The specified string. /// Returns the replacement string. public static string ReplaceFirst(string match, string replace, string str) { if (string.IsNullOrEmpty(match) || string.IsNullOrEmpty(str)) { return str ?? string.Empty; } replace = replace ?? string.Empty; var index = str.IndexOf(match, StringComparison.Ordinal); return index < 0 ? str : str.Remove(index, match.Length).Insert(index, replace); } /// /// Replaces the first occurrence of a match in the specified string from the back to the front. /// This function is case sensitive. /// /// The match string. /// The replacement value. /// The specified string. /// Returns the replacement string. public static string ReplaceLast(string match, string replace, string str) { if (string.IsNullOrEmpty(match) || string.IsNullOrEmpty(str)) { return str ?? string.Empty; } replace = replace ?? string.Empty; var index = str.LastIndexOf(match, StringComparison.Ordinal); return index < 0 ? str : str.Remove(index, match.Length).Insert(index, replace); } /// /// Generate a random letter (with case), a string of numbers. /// /// The length of the generate string. /// The random seed. /// The random string. public static string Random(int length = 16, int? seed = null) { length = Math.Max(1, length); var ret = new StringBuilder(); var random = InternalHelper.MakeRandom(seed); for (int len; (len = ret.Length) < length;) { var size = length - len; var bytes = new byte[size]; random.NextBytes(bytes); var code = Replace(new[] { "/", "+", "=" }, string.Empty, Convert.ToBase64String(bytes)); ret.Append(code.Substring(0, Math.Min(size, code.Length))); } return ret.ToString(); } /// /// If the length exceeds the given maximum string length, the string is truncated. /// The last character of the truncated string will be replaced with the mission string. /// eg: Str.Truncate("hello world , the sun is shine", 15, Str.Space) => hello world... /// /// The string to be truncated. /// Truncation length (with default character length). /// The adjacent separator, if set, truncates the separator position with the length of the truncation length, and uses a regular match if a regular expression is passed. /// The mission string. /// Reutrns truncated string. public static string Truncate(string str, int length, object separator = null, string mission = null) { if (str == null || length > str.Length) { return str; } mission = mission ?? "..."; var end = length - mission.Length; if (end < 1) { return mission; } var ret = str.Substring(0, end); if (separator == null) { return ret + mission; } var separatorStr = separator.ToString(); var index = -1; if (separator is Regex separatorRegex) { if (separatorRegex.IsMatch(ret)) { index = (separatorRegex.RightToLeft ? separatorRegex.Match(ret) : Regex.Match(ret, separatorRegex.ToString(), separatorRegex.Options | RegexOptions.RightToLeft)).Index; } } else if (!string.IsNullOrEmpty(separatorStr) && str.IndexOf(separatorStr, StringComparison.Ordinal) != end) { index = ret.LastIndexOf(separatorStr, StringComparison.Ordinal); } if (index > -1) { ret = ret.Substring(0, index); } return ret + mission; } /// /// Calculate Levenshtein distance between two strings. /// /// The string 1. /// The string 2. /// /// This function returns the Levenshtein-Distance between the two argument /// strings or -1, if one of the argument strings is longer than the limit /// of 255 characters. /// public static int Levenshtein(string a, string b) { if (a == null || b == null) { return -1; } var lengthA = a.Length; var lengthB = b.Length; if (lengthA > 255 || lengthB > 255) { return -1; } var pA = new int[lengthB + 1]; var pB = new int[lengthB + 1]; for (var i = 0; i <= lengthB; i++) { pA[i] = i; } int Min(int num1, int num2, int num3) { var min = num1; if (min > num2) { min = num2; } if (min > num3) { min = num3; } return min; } for (var i = 0; i < lengthA; i++) { pB[0] = pA[0] + 1; for (var n = 0; n < lengthB; n++) { var distance = a[i] == b[n] ? Min(pA[n], pA[n + 1] + 1, pB[n] + 1) : Min(pA[n] + 1, pA[n + 1] + 1, pB[n] + 1); pB[n + 1] = distance; } var temp = pA; pA = pB; pB = temp; } return pA[lengthB]; } /// /// Returns all sequential combination of the given array. /// /// /// v[0] = "hello" /// v[1] = "world" /// var result = Str.JoinList(v, "/"); /// result[0] == "hello"; /// result[1] == "hello/world";. /// /// The source array. /// The separator. /// The sequential combination array. public static string[] JoinList(string[] sources, string separator = null) { sources = sources ?? Array.Empty(); var ret = new StringBuilder(); for (var index = 1; index < sources.Length; index++) { ret.Append(sources[index - 1]); if (!string.IsNullOrEmpty(separator)) { ret.Append(separator); } ret.Append(sources[index]); sources[index] = ret.ToString(); ret.Remove(0, sources[index].Length); } return sources; } /// public static string[] JoinList(string[] source, char separator) { return JoinList(source, separator.ToString()); } } } ================================================ FILE: src/CatLib.Core.Tests/CatLib/TestsApplication.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using CatLib.EventDispatcher; using CatLib.Exception; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System.Diagnostics; using System.Reflection; using System.Threading.Tasks; namespace CatLib.Tests { [TestClass] public class TestsApplication { private Application application; private IEventDispatcher dispatcher; [TestInitialize] public void Initialize() { application = new Application(); dispatcher = application.Make(); } [TestMethod] public void TestGetFileVersion() { var expected = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; Assert.AreEqual(expected, Application.Version); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestBootstrapRepeat() { application.Bootstrap(); application.Init(); application.Bootstrap(); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestInitRepeat() { application.Bootstrap(); application.Init(); application.Init(); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestInitNoBootstrap() { application.Init(); } [TestMethod] public void TestBootstrapSkip() { var foo = new Mock(); var bar = new Mock(); dispatcher.AddListener(ApplicationEvents.OnBooting, (sender, eventArgs) => { if (eventArgs is BootingEventArgs args && args.GetBootstrap() == foo.Object) { args.Skip(); } }); application.Bootstrap(foo.Object, bar.Object); foo.Verify((o) => o.Bootstrap(), Times.Never); bar.Verify((o) => o.Bootstrap(), Times.Once); } [TestMethod] public void TestRegisterSkip() { var foo = new Mock(); var bar = new Mock(); dispatcher.AddListener(ApplicationEvents.OnRegisterProvider, (sender, eventArgs) => { if (eventArgs is RegisterProviderEventArgs args && args.GetServiceProvider() == foo.Object) { args.Skip(); } }); application.Register(foo.Object); application.Register(bar.Object); foo.Verify((o) => o.Register(), Times.Never); bar.Verify((o) => o.Register(), Times.Once); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestRegisterRepeat() { var foo = new Mock(); application.Register(foo.Object); application.Register(foo.Object); } [TestMethod] [ExpectedExceptionAndMessage(typeof(LogicException))] public void TestInitingRegister() { var foo = new Mock(); var bar = new Mock(); dispatcher.AddListener(ApplicationEvents.OnInitProvider, (sender, eventArgs) => { application.Register(foo.Object); }); application.Register(bar.Object); application.Bootstrap(); application.Init(); } [TestMethod] [ExpectedExceptionAndMessage(typeof(LogicException))] public void TestTerminateRegister() { var foo = new Mock(); dispatcher.AddListener(ApplicationEvents.OnBeforeTerminate, (sender, eventArgs) => { application.Register(foo.Object); }); application.Bootstrap(); application.Init(); application.Terminate(); } [TestMethod] public void TestTerminateSequenceOfEvents() { var count = 0; dispatcher.AddListener(ApplicationEvents.OnBeforeTerminate, (sender, eventArgs) => { Assert.AreEqual(0, count++); }); dispatcher.AddListener(ApplicationEvents.OnAfterTerminate, (sender, eventArgs) => { Assert.AreEqual(1, count++); }); application.Terminate(); Assert.AreEqual(2, count); } [TestMethod] public void TestGetProcess() { Assert.AreEqual(StartProcess.Construct, application.Process); } [TestMethod] [DataRow(DebugLevel.Development)] [DataRow(DebugLevel.Production)] [DataRow(DebugLevel.Staging)] public void TestDebugLevel(DebugLevel expected) { application.DebugLevel = expected; Assert.AreEqual(expected, application.DebugLevel); } [TestMethod] public void TestGetRuntimeId() { Assert.AreNotEqual(application.GetRuntimeId(), application.GetRuntimeId()); } [TestMethod] public async Task TestIsMainThread() { Assert.AreEqual(true, application.IsMainThread); await Task.Run(() => { Assert.AreEqual(false, application.IsMainThread); }).ConfigureAwait(false); } [TestMethod] public void TestInitAfterRegister() { var foo = new Mock(); var bar = new Mock(); application.Bootstrap(); application.Register(foo.Object); application.Init(); application.Register(bar.Object); foo.Verify((o) => o.Register(), Times.Once); bar.Verify((o) => o.Register(), Times.Once); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestRegistingMake() { var foo = new Mock(); foo.Setup((o) => o.Register()).Callback(() => { application.Make("foo"); }); application.Register(foo.Object); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestBoostrapRepeat() { var foo = new Mock().Object; application.Bootstrap(foo, foo); } [TestMethod] public void TestBoostrapOrder() { var foo = new Mock(); var bar = new Mock(); var baz = new Mock(); var count = 0; foo.Setup((o) => o.Bootstrap()).Callback(() => { Assert.AreEqual(0, count++); }); bar.Setup((o) => o.Bootstrap()).Callback(() => { Assert.AreEqual(1, count++); }); baz.Setup((o) => o.Bootstrap()).Callback(() => { Assert.AreEqual(2, count++); }); application.Bootstrap(foo.Object, bar.Object, baz.Object); Assert.AreEqual(3, count); } } } ================================================ FILE: src/CatLib.Core.Tests/CatLib/TestsFacade.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using CatLib.Tests.Fixture; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CatLib.Tests { [TestClass] public class TestsFacade { private Application application; [TestInitialize] public void Initialize() { application = Application.New(); application.Bootstrap(); } [TestMethod] public void TestFacade() { application.Singleton(); var foo = Facade.That; Assert.AreSame(foo, Facade.That); } [TestMethod] public void TestAlwaysWatchNewer() { application.Singleton(); var older = Facade.That; application.Unbind(); application.Singleton(); var newer = Facade.That; Assert.AreNotSame(older, newer); } [TestMethod] public void TestAlwaysWatchWithInstance() { application.Singleton(); Assert.AreNotEqual(null, Facade.That); var foo = new Foo(); application.Instance(foo); Assert.AreSame(foo, Facade.That); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestFacadeMakeFaild() { application.Singleton(); Assert.AreNotEqual(null, Facade.That); application.Unbind(); _ = Facade.That; } [TestMethod] public void TestFacadeRelease() { application.Singleton(); var foo = Facade.That; Assert.AreSame(foo, Facade.That); application.Release(); Assert.AreNotSame(foo, Facade.That); } [TestMethod] public void TestNotStaticBindFacade() { application.Bind(); var foo = Facade.That; Assert.AreNotSame(foo, Facade.That); } [TestMethod] public void TestSingletonChangeToBind() { application.Singleton(); var foo = Facade.That; Assert.AreSame(foo, Facade.That); application.Unbind(); application.Bind(); Assert.AreNotSame(foo, Facade.That); Assert.AreNotSame(Facade.That, Facade.That); } [TestMethod] public void TestBindChangeToSingleton() { application.Bind(); var foo = Facade.That; Assert.AreNotSame(foo, Facade.That); application.Unbind(); application.Singleton(); Assert.AreNotSame(foo, Facade.That); Assert.AreSame(Facade.That, Facade.That); } [TestMethod] public void TestInstance() { application.Instance(new Foo()); var foo = Facade.That; Assert.AreSame(foo, Facade.That); } } } ================================================ FILE: src/CatLib.Core.Tests/CatLib.Core.Tests.csproj ================================================  netcoreapp3.0 false 2.0.0 ================================================ FILE: src/CatLib.Core.Tests/Container/TestsBindData.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using CatLib.Exception; using CatLib.Tests.Fixture; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using CContainer = CatLib.Container.Container; namespace CatLib.Tests.Container { [TestClass] public class TestsBindData { private CContainer container; private BindData bindData; [TestInitialize] public void Init() { container = new CContainer(); bindData = (BindData)container.Bind("foo", (container, args) => "foo", true); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void CheckNeedsIllegal() { bindData.Needs(null); } [TestMethod] public void TestGetContextual() { bindData.Needs("foo").Given("bar"); Assert.AreEqual("bar", bindData.GetContextual("foo")); bindData.Needs("bar").Given(); Assert.AreEqual(container.Type2Service(typeof(Foo)), bindData.GetContextual("bar")); } [TestMethod] public void TestGetContextualClosure() { bindData.Needs("foo").Given(() => "foo"); Assert.AreEqual("foo", bindData.GetContextualClosure("foo")()); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestNeedsDuplicate() { bindData.Needs("foo").Given("bar"); bindData.Needs("foo").Given("baz"); } [TestMethod] public void TestAlias() { bindData.Alias("foo-alias"); bindData.Alias(); Assert.AreSame(bindData, container.GetBind("foo-alias")); Assert.AreSame(bindData, container.GetBind()); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestAliasIllegal() { bindData.Alias(null); } [TestMethod] public void TestTag() { bindData.Tag("tag"); CollectionAssert.AreEqual(new[] { "foo" }, container.Tagged("tag")); } [TestMethod] public void TestOnRelease() { var count = 0; bindData.OnRelease((binder, instance) => { Assert.AreEqual("foo", instance); Assert.AreSame(bindData, binder); count++; }); bindData.OnRelease(() => { count++; }); container.Make("foo"); container.Release("foo"); Assert.AreEqual(2, count); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestOnReleaseIllegal() { bindData.OnRelease(null); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestOnResolvingIllegal() { bindData.OnResolving(null); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestUnbind() { Assert.AreEqual("foo", container.Make("foo")); bindData.Unbind(); container.Make("foo"); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestUnbindAfterChanged() { bindData.Unbind(); bindData.Alias("foo-alias"); } } } ================================================ FILE: src/CatLib.Core.Tests/Container/TestsContainer.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using CatLib.Exception; using CatLib.Tests.Fixture; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Linq; using CContainer = CatLib.Container.Container; using SException = System.Exception; namespace CatLib.Tests.Container { [TestClass] public class TestsContainer { private IContainer container; [TestInitialize] public void Initialize() { container = CreateContainer(); container.OnFindType((service) => { return Type.GetType(service); }); } [TestMethod] public void TestTag() { var foo = new Foo(); container["baz"] = 100; container["bar"] = 200; var service = container.Type2Service(typeof(Foo)); container.Bind(service, (container, args) => foo, false); container.Tag("tag", service); container.Tag("tag", "baz", "bar"); CollectionAssert.AreEqual( new object[] { foo, 100, 200 }, container.Tagged("tag")); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void TestTagIllegal() { container.Tag(string.Empty); container.Tag(null); } [TestMethod] [ExpectedExceptionAndMessage( typeof(LogicException), "Tag \"foo\" is not exist.")] public void TestTagNotExists() { container.Tagged("foo"); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestUnbind() { container.Bind("foo", (app, args) => "foo", false); container.Unbind("foo"); container.Make("foo"); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestUnbindWithAlias() { container.Bind("bar", (app, args) => "bar", false).Alias("baz"); container.Unbind("baz"); container.Make("baz"); } [TestMethod] [DataRow(typeof(string[]))] [DataRow(typeof(AbstractClass))] [DataRow(typeof(IFoo))] [ExpectedExceptionAndMessage(typeof(LogicException), "can not bind.")] public void TestUnableType(Type type) { container.Bind("foo", type, false); } [TestMethod] public void TestBindIf() { var foo = container.BindIf("foo", (container, args) => "foo", true, out IBindData bindFoo); var bar = container.BindIf("foo", (container, args) => "bar", true, out IBindData bindBar); Assert.AreSame(bindFoo, bindBar); Assert.AreEqual(true, foo); Assert.AreEqual(false, bar); } [TestMethod] public void TestbBindIfWithType() { var foo = container.BindIf("foo", typeof(Foo), true, out IBindData bindFoo); var bar = container.BindIf("foo", typeof(Bar), true, out IBindData bindBar); Assert.AreSame(bindFoo, bindBar); Assert.AreEqual(true, foo); Assert.AreEqual(false, bar); } [TestMethod] public void TestBindStatic() { container.Bind("foo", (container, args) => new Foo(), true); var fooBind = container.GetBind("foo"); var foo = container.Make("foo"); Assert.AreNotEqual(null, fooBind); Assert.AreEqual(true, fooBind.IsStatic); Assert.AreSame(foo, container.Make("foo")); } [TestMethod] public void TestBindNonStatic() { container.Bind("foo", (container, args) => new Foo(), false); var older = container.Make("foo"); var newer = container.Make("foo"); Assert.AreNotSame(older, newer); } [TestMethod] public void TestGetBind() { var fooBind = container.Bind("foo", (container, args) => "foo", false); var getBind = container.GetBind("foo"); Assert.AreSame(fooBind, getBind); getBind = container.GetBind("non-service"); Assert.AreEqual(null, getBind); container.Alias("foo-alias", "foo"); getBind = container.GetBind("foo-alias"); Assert.AreEqual(fooBind, getBind); getBind = container.GetBind(null); Assert.AreEqual(null, getBind); } [TestMethod] public void TestBindIllegal() { container.Bind("foo", (container, args) => "foo", true); Assert.ThrowsException(() => { container.Bind("foo", (container, args) => "repeat bind", false); }); container.Instance("bar", "bar"); Assert.ThrowsException(() => { container.Bind("bar", (container, args) => "instance repeat bind", false); }); container.Alias("foo-alias", "foo"); Assert.ThrowsException(() => { container.Bind("foo-alias", (container, args) => "alias repeat bind", false); }); Assert.ThrowsException(() => { container.Bind(null, (container, args) => "invalid service name", false); }); Assert.ThrowsException(() => { container.Bind("$foo", (container, args) => "Illegal placeholder", false); }); } [TestMethod] public void TestHasBind() { container.Bind("foo", (container, args) => "foo", false) .Alias("foo-alias"); Assert.IsTrue(container.HasBind("foo")); Assert.IsTrue(container.HasBind("foo-alias")); } [TestMethod] public void TestIsStatic() { container.Bind("foo", (container, args) => "foo", true) .Alias("foo-alias"); container.Bind("bar", (container, args) => "bar", false) .Alias("bar-alias"); Assert.IsTrue(container.IsStatic("foo")); Assert.IsTrue(container.IsStatic("foo-alias")); Assert.IsFalse(container.IsStatic("bar")); Assert.IsFalse(container.IsStatic("bar-alias")); } [TestMethod] public void TestAlias() { container.Bind("foo", (container, args) => "foo", false) .Alias("foo-alias"); container.Instance("bar", "bar"); container.Alias("bar-alias", "bar"); Assert.AreEqual("foo", container["foo-alias"]); Assert.AreEqual("bar", container["bar-alias"]); } [TestMethod] [DataRow("bar-alias", "bar", typeof(LogicException), "Set an alias for a service that does not exist.")] [DataRow(null, "bar", typeof(ArgumentNullException), "Alias name is null.")] [DataRow("baz", null, typeof(ArgumentNullException), "Service name is null.")] [DataRow("foo", "foo", typeof(LogicException), "Alias is same as service")] public void TestAliasIllegal(string alias, string service, Type expected, string reason) { container.Bind("foo", (container, args) => "foo", false) .Alias("foo-alias"); try { container.Alias(alias, service); Assert.Fail(reason); } catch (SException ex) when (ex.GetType() != expected) { Assert.Fail($"Expected throw an exception: {expected}, actual throw: {ex}"); } #pragma warning disable CA1031 catch (SException) #pragma warning restore CA1031 { // test passed. } } [TestMethod] [ExpectedExceptionAndMessage(typeof(LogicException), "Circular dependency detected while for")] public void TestCircularDependencyInject() { container.Bind("foo", typeof(CircularDependency), false); container.Make("foo"); } [TestMethod] public void TestIsAlias() { container.Bind("foo", (container, args) => "foo", false) .Alias("foo-alias"); Assert.IsTrue(container.IsAlias("foo-alias")); Assert.IsFalse(container.IsAlias("foo")); } [TestMethod] [DataRow(null)] [DataRow(new[] { "unless-parameter" })] public void TestCall(string[] userParams) { var serviceFoo = container.Type2Service(typeof(Foo)); var serviceBar = container.Type2Service(typeof(Bar)); container.Bind(serviceFoo, typeof(Foo), true); container.Bind(serviceBar, typeof(Bar), true); var fooBar = FooBar.New(); var methodInfo = fooBar.GetType().GetMethod(nameof(fooBar.GetName)); Assert.AreEqual("foobar", container.Call(fooBar, methodInfo, userParams)); } [TestMethod] [ExpectedExceptionAndMessage(typeof(LogicException), "Circular dependency detected while for")] public void TestCallCircularDependencyInject() { var service = container.Type2Service(typeof(CircularDependency)); container.Bind(service, typeof(CircularDependency), false); var foo = new CircularDependency(null); container.Call(foo, foo.GetType().GetMethod(nameof(foo.Foo))); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestCallGivenIncorrectParament() { container.Bind("num", (container, args) => "foo", true) .Alias("$num"); Action action = (int num) => { Assert.Fail("Should throw UnresolvableException exception."); }; container.Call(action.Target, action.Method); } [TestMethod] [ExpectedExceptionAndMessage( typeof(LogicException), "Too many parameters , must be less or equal than 255")] public void TestMakearameterOverflow() { container.Bind("foobar", typeof(FooBar), false); container.Make("foobar", new object[256]); } [TestMethod] public void TestTightInject() { Action action = (objs, foo, bar) => { Assert.AreEqual(100, objs[0]); Assert.AreEqual("foo", objs[1]); Assert.AreNotEqual(null, foo); Assert.AreNotEqual(null, bar); }; container.Call(action.Target, action.Method, 100, "foo"); } [TestMethod] public void TestContextInjectionWithProperty() { var service = container.Type2Service(typeof(Baz)); container.Bind(service, typeof(Baz), false) .Needs("$Name").Given(() => "bazbaz") .Needs("$Qux").Given(() => 100); var baz = (Baz)container.Make(service); Assert.AreEqual("bazbaz", baz.Name); } [TestMethod] public void TestContextInjection() { container.Bind("baz", typeof(Baz), false) .Needs("$boo").Given(() => 500) .Needs("$Qux").Given(() => 100); var baz = (Baz)container.Make("baz"); Assert.AreEqual(500, baz.Boo); container.Bind("baz-cast-type", typeof(Baz), false) .Needs("$boo").Given(() => "300") .Needs("$Qux").Given(() => 100); baz = (Baz)container.Make("baz-cast-type"); Assert.AreEqual(300, baz.Boo); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestContextInjectionErrorType() { container = CreateContainer(); container.Bind("foobar", typeof(FooBar), false) .Needs("$foo").Given(() => new Bar()); container.Make("foobar"); } [TestMethod] public void TestMake() { var service = container.Type2Service(typeof(FooBar)); container.Bind(service, typeof(FooBar), true); var foobar = container.Make(service); Assert.AreNotEqual(null, foobar); } [TestMethod] public void TestMakeWithParams() { container.Bind("baz", typeof(Baz), false) .Needs("$Qux").Given(() => 100); var baz = (Baz)container.Make("baz", 500); Assert.AreEqual(500, baz.Boo); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestMakeEmptyService() { container.Make(string.Empty); } [TestMethod] public void TestMakeWithAlias() { container.Bind("foo", typeof(Foo), true).Alias("foo-alias"); container.Bind("bar", typeof(Bar), true).Alias("bar-alias"); var foo = container.Make("foo"); var bar = container.Make("bar"); Assert.AreSame(foo, container.Make("foo-alias")); Assert.AreSame(bar, container.Make("bar-alias")); } [TestMethod] [ExpectedException( typeof(UnresolvableException), "Unresolvable dependency , resolving [Name] in class")] public void TestMakeAttributeInjectFaild() { container = CreateContainer(); container.Bind("baz", typeof(Baz), true) .Needs("$Qux").Given(() => 100); container.Make("baz", new Foo()); } [TestMethod] [ExpectedExceptionAndMessage( typeof(UnresolvableException), "Unresolvable dependency , resolving [Qux] in class")] public void TestMakeAttributeInjectFaildWithPrimitiveAttr() { container.Bind("baz", typeof(Baz), true); container.Make("baz", new Foo()); } [TestMethod] public void TestMakeWithDefaultValuePrimitive() { container.Bind("baz", typeof(Baz), true) .Needs("$Qux").Given(() => 500); var baz = (Baz)container.Make("baz"); Assert.AreEqual(100, baz.Boo); } [TestMethod] public void TestMakeWithDefaultValue() { container = CreateContainer(); container.Bind("fubar", typeof(Fubar), true); var fubar = (Fubar)container.Make("fubar"); Assert.AreEqual(null, fubar.Bar); } [TestMethod] public void TestMakeInjectWithStruct() { var service = container.Type2Service(typeof(Position)); container.Bind("fubar", typeof(Fubar), true); container.Bind(service, (container, args) => new Position() { X = 1, Y = 2, }); var fubar = (Fubar)container.Make("fubar"); Assert.AreEqual(1, fubar.Position.X); Assert.AreEqual(2, fubar.Position.Y); } [TestMethod] public void TestMakeInjectWithGeneric() { container = CreateContainer(); container.Bind("fubar", typeof(Fubar), true); var service = container.Type2Service(typeof(IList)); container.Bind(service, (container, args) => new List() { "iron man", "black window", }); service = container.Type2Service(typeof(IList)); container.Bind(service, (container, args) => new List() { 25, 23, }); var fubar = (Fubar)container.Make("fubar"); CollectionAssert.AreEqual( new[] { "iron man", "black window" }, fubar.Heros.ToArray()); CollectionAssert.AreEqual( new[] { 25, 23 }, fubar.Ages.ToArray()); Assert.AreEqual(null, fubar.Bar); } [TestMethod] [ExpectedExceptionAndMessage( typeof(LogicException), "can not bind.")] public void TestMakeAbstractClass() { container.Bind("foo", typeof(AbstractClass), true); container.Make("foo"); } [TestMethod] public void TestMakeInjectWithInheritance() { var service = container.Type2Service(typeof(IList)); container.Bind("fubar", typeof(Fubar), true); container.Bind(service, (container, args) => new List() { "iron man", "black window", }); var fubar = (Fubar)container.Make("fubar"); CollectionAssert.AreEqual( new[] { "iron man", "black window" }, fubar.Heros.ToArray()); Assert.AreNotEqual(null, fubar.Bar); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestMakeMissConstructor() { container.Bind("quux", typeof(Quux), true); container.Make("quux"); } [TestMethod] [ExpectedExceptionAndMessage(typeof(TestException), "QuuxFoo")] public void TestMakeConstructorThrowException() { container.Bind("foo", typeof(QuuxFoo), false); container.Make("foo"); } [TestMethod] public void TestMakeWithIParams() { container = CreateContainer(); container.Bind("foobar", typeof(FooBar), false); var foo = new Foo(); var bar = new Bar(); var foobar = (FooBar)container.Make( "foobar", new ParamsCollection { { "foo", foo }, }, bar); Assert.AreEqual("foobar", foobar.ToString()); Assert.AreSame(foo, foobar.Foo); Assert.AreSame(bar, foobar.Bar); } [TestMethod] public void TestMakeWithMultIParamsOrder() { container = CreateContainer(); container.Bind("foobar", typeof(FooBar), false); var foo1 = new Foo(); var bar1 = new Bar(); var foo2 = new Foo(); var bar2 = new Bar(); var foobar = (FooBar)container.Make( "foobar", new ParamsCollection { { "foo", foo1 }, { "bar", bar1 }, }, new ParamsCollection { { "foo", foo2 }, }, bar2); Assert.AreEqual("foobar", foobar.ToString()); Assert.AreSame(foo1, foobar.Foo); Assert.AreSame(bar1, foobar.Bar); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestMakeWithIParamsGivenErrorType() { container = CreateContainer(); container.Bind("foobar", typeof(FooBar), false); container.Make("foobar", new ParamsCollection { { "foo", new Bar() }, { "bar", new Foo() }, }); } [TestMethod] public void TestMakeNullable() { container.Bind("bar", typeof(Bar), false) .Needs("$Age").Given(() => 24); var bar = (Bar)container.Make("bar"); Assert.IsFalse(bar.Num.HasValue); Assert.AreEqual(24, bar.Age.Value); } [TestMethod] public void TestCanMake() { Assert.IsFalse(container.CanMake("foo")); Assert.IsTrue(container.CanMake()); container = CreateContainer(); Assert.IsFalse(container.CanMake()); } [TestMethod] public void TestInstance() { var bar = new object(); container.Instance("foo", "foo"); container.Instance("bar", bar); Assert.AreEqual("foo", container.Make("foo")); Assert.AreSame(bar, container.Make("bar")); // double checked Assert.AreSame(bar, container.Make("bar")); } [TestMethod] [ExpectedException(typeof(LogicException))] [DataRow("foo:bar")] [DataRow("$foo")] [DataRow("foo@bar")] public void TestInstanceIllegalChars(string service) { container.Instance(service, "illegal chars"); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestInstanceNotAllowedSameObject() { var foo = new object(); container.Instance("foo", foo); container.Instance("bar", foo); } [TestMethod] public void TestInstanceAllowedDifferenceObject() { container.Instance("foo", "foo"); container.Instance("bar", "bar"); } [TestMethod] [ExpectedException( typeof(LogicException), "is not Singleton(Static) Bind.")] public void TestInstanceIllegal() { container.Bind("foo", typeof(Foo), false); container.Instance("foo", "foo"); } [TestMethod] public void TestReleaseAutoCallDispose() { container.Bind("bar", typeof(Bar), true); var bar = (Bar)container.Make("bar"); Assert.IsFalse(bar.Disposed); container.Release("bar"); Assert.IsTrue(bar.Disposed); } [TestMethod] public void TestOnRelease() { var count = 0; container.Bind("foo", typeof(Foo), true) .OnRelease((binder, instance) => { Assert.AreEqual(typeof(Foo), instance.GetType()); count++; }); container.Make("foo"); container.Release("foo"); container = CreateContainer(); container.Instance("foo", "foo"); container.OnRelease((binder, instance) => { Assert.AreEqual("foo", instance); count++; }); container.Release("foo"); Assert.AreEqual(2, count); } [TestMethod] public void TestFlush() { container["foo"] = "foo"; container["bar"] = "bar"; container["baz"] = "baz"; container.Flush(); Assert.ThrowsException(() => { container.Make("foo"); }); Assert.ThrowsException(() => { container.Make("bar"); }); Assert.ThrowsException(() => { container.Make("baz"); }); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestFlushInstanceService() { container.Bind("foo", typeof(Foo), true) .OnRelease((binder, instance) => { container.Instance("bar", "bar"); }); container.Make("foo"); container.Flush(); } [TestMethod] public void TestFlushOrder() { var foo = container.Type2Service(typeof(Foo)); var bar = container.Type2Service(typeof(Bar)); container.Bind(foo, typeof(Foo), true); container.Bind(bar, typeof(Bar), true); container.Bind("foobar", typeof(FooBar), true); var actual = new List(); container.OnRelease((binder, instance) => { actual.Add(instance.GetType()); }); container.Make("foobar"); container.Flush(); CollectionAssert.AreEqual( new[] { typeof(FooBar), typeof(Bar), typeof(Foo), }, actual); } [TestMethod] public void TestVariant() { container.Bind("foo", typeof(Variant), false); var foo = (Variant)container.Make("foo", 1); Assert.AreEqual("iron man", foo.Model.Name); foo = (Variant)container.Make("foo", 2); Assert.AreEqual("black window", foo.Model.Name); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestVariantThrowException() { container.Bind("foo", typeof(Variant), false); container.Make("foo", -1); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestVariantForceGivenNull() { container.Bind("foo", typeof(Variant), false); container.Make("foo", new ParamsCollection { { "model", null }, }); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestReboundNotExistsService() { container.OnRebound("foobar", (instance) => { }); } [TestMethod] public void TestRebound() { var count = 0; var binder = container.Bind("foo", (container, args) => "foo"); container.OnRebound("foo", (instance) => { Assert.AreEqual("bar", instance); count++; }); binder.Unbind(); // first built don't trigger OnRebound. binder = container.Bind("foo", (c, p) => "boo"); container.Make("foo"); binder.Unbind(); container.Bind("foo", (c, p) => "bar"); Assert.AreEqual(1, count); } [TestMethod] public void TestReboundWithInstance() { var count = 0; container.Instance("foo", "foo"); container.OnRebound("foo", (instance) => { Assert.AreEqual("bar", instance); count++; }); container.Instance("foo", "bar"); Assert.AreEqual(1, count); } [TestMethod] public void TestUnbindNotExistsService() { container.Unbind("service not exists"); } [TestMethod] public void TestHasInstance() { container.Instance("foo", "foo"); Assert.IsTrue(container.HasInstance("foo")); Assert.IsFalse(container.HasInstance("bar")); } [TestMethod] public void TestIsResolved() { container.Instance("foo", "foo"); Assert.IsTrue(container.IsResolved("foo")); Assert.IsFalse(container.IsResolved("bar")); container.Bind("bar", typeof(Bar), true); Assert.IsFalse(container.IsResolved("bar")); container.Make("bar"); Assert.IsTrue(container.IsResolved("bar")); } [TestMethod] public void TestExtend() { container.Bind("foo", (container, args) => "foo", false); container.Extend("foo", (instance, container) => instance + "bar"); Assert.AreEqual("foobar", container["foo"]); } [TestMethod] public void TestExtendMult() { container.Bind("foo", (container, args) => "foo", false); container.Extend("foo", (instance, container) => instance + "bar"); container.Extend("foo", (instance, container) => instance + "baz"); Assert.AreEqual("foobarbaz", container["foo"]); } [TestMethod] public void TestExtendSingle() { container.Bind("foo", (container, args) => "foo", true); container.Extend("foo", (instance, container) => instance + "bar"); Assert.AreEqual("foobar", container["foo"]); // Existing instances are only valid if they are extended. container.Extend("foo", (instance, container) => instance + "baz"); Assert.AreEqual("foobarbaz", container["foo"]); container.Release("foo"); Assert.AreEqual("foobar", container["foo"]); } [TestMethod] public void TestExtendAndRebound() { container.Bind("foo", (container, args) => "foo", false); // marked resolved. container.Make("foo"); container.Release("foo"); var actual = string.Empty; container.OnRebound("foo", (instance) => { actual = instance.ToString(); }); container.Extend("foo", (instance, container) => instance + "bar"); Assert.AreEqual("foobar", actual); } [TestMethod] [ExpectedException(typeof(UnresolvableException))] public void TestExtendGivenMismatchedType() { var service = container.Type2Service(typeof(Foo)); container.Bind(service, typeof(Foo), true); container.Bind("foobar", typeof(FooBar), true); container.Extend(service, (instance, container) => "mismatched type"); container.Make("foobar"); } [TestMethod] public void TestClearExtend() { if (!(container is CContainer catlibContainer)) { return; } container.Bind("foo", (container, args) => "foo", false); container.Extend("foo", (instance, container) => instance + "bar"); container.Extend("foo", (instance, container) => instance + "baz"); container.Bind("bar", (container, args) => "bar", false); container.Extend("bar", (instance, container) => instance + "foo"); container.Extend("bar", (instance, container) => instance + "baz"); Assert.AreEqual("foobarbaz", container["foo"]); Assert.AreEqual("barfoobaz", container["bar"]); catlibContainer.ClearExtenders("foo"); Assert.AreEqual("foo", container["foo"]); Assert.AreEqual("barfoobaz", container["bar"]); } [TestMethod] public void TestIndexer() { container["foo"] = "bar"; Assert.AreEqual("bar", container["foo"]); } [TestMethod] public void TestIndexerOverride() { container["foo"] = "bar"; Assert.AreEqual("bar", container["foo"]); container["foo"] = "baz"; Assert.AreEqual("baz", container["foo"]); } [TestMethod] public void TestOnAfterResolving() { var step = 0; container.OnAfterResolving((binder, instance) => { Assert.AreEqual(1, step); step = 2; }); container.OnAfterResolving((binder, instance) => { Assert.AreEqual(2, step); step = 3; }); container.OnResolving((binder, instance) => { Assert.AreEqual(0, step); step = 1; }); container["foo"] = "bar"; Assert.AreEqual("bar", container["foo"]); Assert.AreEqual(3, step); } [TestMethod] public void TestOnAfterResolvingLocal() { var step = 0; container.Bind("foo", (binder, instance) => "bar") .OnAfterResolving(() => { Assert.AreEqual(1, step); step = 2; }).OnAfterResolving((_) => { Assert.AreEqual(2, step); step = 3; }).OnResolving(() => { Assert.AreEqual(0, step); step = 1; }); Assert.AreEqual("bar", container["foo"]); Assert.AreEqual(3, step); } [TestMethod] public void TestNullRelease() { Assert.AreEqual(false, container.Release(null)); } protected virtual IContainer CreateContainer() { return new CContainer(); } } } ================================================ FILE: src/CatLib.Core.Tests/Container/TestsExtensionContainer.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using CatLib.Exception; using CatLib.Tests.Fixture; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using CContainer = CatLib.Container.Container; namespace CatLib.Tests.Container { [TestClass] public class TestsExtensionContainer { private IContainer container; [TestInitialize] public void Initialize() { container = new CContainer(); container.Singleton(() => container); container.Bind() .Alias() .Alias("foo-alias"); } [TestMethod] public void TestMake() { Assert.AreSame(container, container.Make()); Assert.AreNotEqual(null, container.Make("foo-alias")); var bar = container.Make(typeof(Bar)); Assert.AreNotEqual(null, bar); } [TestMethod] public void TestInstance() { container.Instance(new Bar()); var bar = container.Make(); Assert.AreNotEqual(null, bar); } [TestMethod] public void TestRelease() { container.Release(); Assert.IsFalse(container.HasInstance()); } [TestMethod] public void TestAlias() { container.Alias(); Assert.IsTrue(container.IsAlias()); } [TestMethod] public void TestReleaseWithObject() { container.Instance("foo"); container.Instance(10); object[] data = null; Assert.AreEqual(true, container.Release(ref data)); data = Array.Empty(); Assert.AreEqual(true, container.Release(ref data)); data = new object[] { "foo", 10 }; Assert.AreEqual(true, container.Release(ref data)); Assert.AreEqual(true, data.Length == 0); data = new object[] { "foo", 10, 100 }; Assert.AreEqual(false, container.Release(ref data)); Assert.AreEqual(true, data.Length == 3); container.Instance(10); data = new object[] { "foo", 10, 100 }; Assert.AreEqual(false, container.Release(ref data, false)); Assert.AreEqual(true, data.Length == 2); CollectionAssert.AreEqual(new object[] { "foo", 100 }, data); container.Instance("foo"); data = new object[] { 10, "foo", 100 }; Assert.AreEqual(false, container.Release(ref data)); Assert.AreEqual(true, data.Length == 2); CollectionAssert.AreEqual(new object[] { 10, 100 }, data); } [TestMethod] public void TestBindIf() { Assert.IsFalse(container.BindIf("foo-alias", (container, args) => new Foo(), true, out _)); Assert.IsFalse(container.BindIf(out _)); Assert.IsTrue(container.BindIf(out _)); } [TestMethod] public void TestSingletonIf() { Assert.IsFalse(container.SingletonIf("foo-alias", (container, args) => new Foo(), out _)); Assert.IsFalse(container.SingletonIf(out _)); Assert.IsTrue(container.SingletonIf(out _)); } [TestMethod] public void TestGetBind() { Assert.AreNotEqual(null, container.GetBind()); } [TestMethod] public void TestCanMake() { Assert.IsTrue(container.CanMake()); Assert.IsFalse(container.CanMake()); } [TestMethod] public void TestIsStatic() { Assert.IsTrue(container.IsStatic()); Assert.IsFalse(container.IsStatic()); } [TestMethod] public void TestIsAlias() { Assert.IsFalse(container.IsAlias()); Assert.IsTrue(container.IsAlias()); Assert.IsFalse(container.IsAlias()); } [TestMethod] public void TestWrap() { container = new CContainer(); container.Singleton().Alias(); var expected = container.Make(); var count = 0; var wrapped = container.Wrap((IFoo foo) => { Assert.AreSame(expected, foo); count++; }); wrapped(); wrapped = container.Wrap((IFoo foo1, Foo foo2) => { Assert.AreSame(expected, foo1); Assert.AreSame(foo1, foo2); count++; }); wrapped(); wrapped = container.Wrap((IFoo foo1, Foo foo2, IFoo foo3) => { Assert.AreSame(expected, foo1); Assert.AreSame(foo1, foo2); Assert.AreSame(foo2, foo3); count++; }); wrapped(); wrapped = container.Wrap((IFoo foo1, IFoo foo2, IFoo foo3, Foo foo4) => { Assert.AreSame(expected, foo1); Assert.AreSame(foo1, foo2); Assert.AreSame(foo2, foo3); Assert.AreSame(foo3, foo4); count++; }); wrapped(); Assert.AreEqual(4, count); } [TestMethod] public void TestCall() { container = new CContainer(); container.Singleton().Alias(); var expected = container.Make(); var count = 0; container.Call((IFoo foo) => { Assert.AreSame(expected, foo); count++; }); container.Call((IFoo foo1, Foo foo2) => { Assert.AreSame(expected, foo1); Assert.AreSame(foo1, foo2); count++; }); container.Call((IFoo foo1, Foo foo2, IFoo foo3) => { Assert.AreSame(expected, foo1); Assert.AreSame(foo1, foo2); Assert.AreSame(foo2, foo3); count++; }); container.Call((IFoo foo1, IFoo foo2, IFoo foo3, Foo foo4) => { Assert.AreSame(expected, foo1); Assert.AreSame(foo1, foo2); Assert.AreSame(foo2, foo3); Assert.AreSame(foo3, foo4); count++; }); Assert.AreEqual(4, count); } [TestMethod] public void TestOnResolving() { var count = 0; container.OnResolving((foo) => { Assert.AreNotEqual(null, foo); count++; }); container.OnResolving((binder, foo) => { Assert.AreNotEqual(null, foo); count++; }); container.OnResolving((binder, foo) => { Assert.AreNotEqual(null, foo); count++; }); container.Make(); Assert.AreEqual(2, count); } [TestMethod] public void TestOnAfterResolving() { var count = 0; container.OnAfterResolving((foo) => { Assert.AreNotEqual(null, foo); count++; }); container.OnAfterResolving((binder, foo) => { Assert.AreNotEqual(null, foo); count++; }); container.OnAfterResolving((binder, foo) => { Assert.AreNotEqual(null, foo); count++; }); container.Make(); Assert.AreEqual(2, count); } [TestMethod] public void TestOnRelease() { container.Unbind(); container.Singleton(); var count = 0; container.OnRelease((foo) => { Assert.AreNotEqual(null, foo); count++; }); container.OnRelease((binder, foo) => { Assert.AreNotEqual(null, foo); count++; }); container.OnRelease((binder, foo) => { Assert.AreNotEqual(null, foo); count++; }); container.Make(); Assert.AreEqual(0, count); container.Release(); Assert.AreEqual(2, count); } [TestMethod] [ExpectedExceptionAndMessage( typeof(LogicException), "Function \"method-is-not-found\" not found.")] public void TestCallIllegal() { var foobar = FooBar.New(); container.Call(foobar, "method-is-not-found"); } [TestMethod] public void TestFactory() { container.Flush(); container.Instance(new Foo()); container.Instance("bar", "bar"); var factory1 = container.Factory(100); var expected = container.Make(); Assert.AreEqual(expected, factory1()); var factory2 = container.Factory("bar", 200); Assert.AreEqual("bar", factory2()); } [TestMethod] public void TestHasBind() { Assert.IsTrue(container.HasBind()); } [TestMethod] public void TestWatch() { var binder = container.Singleton(() => "foo"); container.Make(); binder.Unbind(); var actual = string.Empty; container.Watch((instance) => { actual = instance; }); container.Singleton(() => "bar"); Assert.AreEqual("bar", actual); } [TestMethod] public void TestWatchInstanceOverride() { container.Singleton(() => "foo"); container.Make(); var actual1 = string.Empty; container.Watch((instance) => { actual1 = instance; }); var actual2 = false; container.Watch(() => { actual2 = true; }); container.Instance( container.Type2Service(typeof(string)), "bar"); Assert.AreEqual("bar", actual1); Assert.IsTrue(actual2); } [TestMethod] public void TestReleaseWithTag() { container.Bind("foo", typeof(Foo), true) .Tag("foobar"); container.Bind("bar", typeof(Bar), true) .Tag("foobar"); var instances = container.Tagged("foobar"); Assert.AreEqual(true, container.Release(ref instances)); } [TestMethod] public void TestExtend() { container.Bind(() => "foo"); container.Bind(() => "baz"); container.Extend((instance) => instance + "bar"); Assert.AreEqual("foobar", container.Make()); Assert.AreEqual("bazbar", container.Make()); } [TestMethod] public void TestBindMethod() { container.BindMethod("foo", () => "foo"); Assert.AreEqual("foo", container.Invoke("foo")); container.BindMethod("bar", (IContainer container) => container != null); Assert.IsTrue((bool)container.Invoke("bar")); container.BindMethod("echo", (string input) => { return input; }); Assert.AreEqual("foobar", container.Invoke("echo", "foobar")); } [TestMethod] public void TestUnbindMethodWithMethodName() { container.BindMethod("foo", () => "foo"); container.BindMethod("bar", () => "bar"); container.UnbindMethod("foo"); Assert.ThrowsException(() => { container.Invoke("foo"); }); Assert.AreEqual("bar", container.Invoke("bar")); } [TestMethod] [ExpectedException(typeof(LogicException))] public void TestBindMethodExists() { container.BindMethod("foo", () => "foo"); container.BindMethod("foo", () => "bar"); } [TestMethod] public void TestBindMethodStatic() { container.BindMethod("echo", Foo.Echo); Assert.AreEqual("foo", container.Invoke("echo", "foo")); } [TestMethod] public void TestUnbindWithObject() { var foo1 = new Foo(); container.BindMethod("Foo1.EchoInt", foo1); container.BindMethod("Foo1.EchoFloat", foo1); var foo2 = new Foo(); container.BindMethod("Foo2.EchoInt", foo2); container.BindMethod("Foo2.EchoFloat", foo2); container.UnbindMethod(foo1); container.UnbindMethod("unknow-method"); Assert.ThrowsException(() => { container.Invoke("Foo1.EchoInt", 100); }); Assert.ThrowsException(() => { container.Invoke("Foo1.EchoFloat", 0.5f); }); Assert.AreEqual(100, container.Invoke("Foo2.EchoInt", 100)); Assert.AreEqual(0.5f, container.Invoke("Foo2.EchoFloat", 0.5f)); } [TestMethod] public void TestContainerMethodContextual() { var foo = new Foo(); container.BindMethod("Foo.EchoInt", foo).Needs("$input").Given(() => 200); Assert.AreEqual(100, container.Invoke("Foo.EchoInt", 100)); Assert.AreEqual(200, container.Invoke("Foo.EchoInt")); } } } ================================================ FILE: src/CatLib.Core.Tests/EventDispatcher/TestsEventDispatcher.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Tests.Fixture; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System; using Dispatcher = CatLib.EventDispatcher.EventDispatcher; namespace CatLib.EventDispatcher.Tests { [TestClass] public class TestsEventDispatcher { private Dispatcher eventDispatcher; [TestInitialize] public void Initialize() { eventDispatcher = new Dispatcher(); } [TestMethod] public void TestRaise() { var expected = new TestEventArgs(); var handler = new Mock(); eventDispatcher.AddListener("foo", handler.Object); eventDispatcher.Raise("foo", this, expected); handler.Verify((o) => o.Invoke(this, expected)); } [TestMethod] public void TestAddListener() { var foo1 = new Mock(); var foo2 = new Mock(); eventDispatcher.AddListener("foo", foo1.Object); eventDispatcher.AddListener("foo", foo2.Object); CollectionAssert.AreEqual( new[] { foo1.Object, foo2.Object, }, eventDispatcher.GetListeners("foo")); } [TestMethod] public void TestHasListener() { var foo = new Mock(); var bar = new Mock(); eventDispatcher.AddListener("foo", foo.Object); eventDispatcher.AddListener("bar", bar.Object); Assert.IsTrue(eventDispatcher.HasListener("foo")); Assert.IsTrue(eventDispatcher.HasListener("bar")); Assert.IsFalse(eventDispatcher.HasListener("baz")); } [TestMethod] public void TestRepateAddSameListeners() { var foo = new Mock(); Assert.IsTrue(eventDispatcher.AddListener("foo", foo.Object)); Assert.IsFalse(eventDispatcher.AddListener("foo", foo.Object)); } [TestMethod] public void TestRemoveListeners() { var foo = new Mock(); var bar = new Mock(); var expected = new TestEventArgs(); eventDispatcher.AddListener("foo", foo.Object); eventDispatcher.AddListener("bar", bar.Object); eventDispatcher.Raise("foo", this, expected); eventDispatcher.Raise("bar", this, expected); foo.Verify((o) => o.Invoke(this, expected)); bar.Verify((o) => o.Invoke(this, expected)); eventDispatcher.RemoveListener("foo", foo.Object); eventDispatcher.Raise("foo", this, expected); eventDispatcher.Raise("bar", this, expected); foo.Verify((o) => o.Invoke(this, expected), Times.Exactly(1)); bar.Verify((o) => o.Invoke(this, expected), Times.Exactly(2)); } [TestMethod] public void TestRemoveAllListeners() { var foo1 = new Mock(); var foo2 = new Mock(); var expected = new TestEventArgs(); eventDispatcher.AddListener("foo", foo1.Object); eventDispatcher.AddListener("foo", foo2.Object); eventDispatcher.Raise("foo", this, expected); foo1.Verify((o) => o.Invoke(this, expected)); foo2.Verify((o) => o.Invoke(this, expected)); eventDispatcher.RemoveListener("foo"); eventDispatcher.Raise("foo", this, expected); foo1.Verify((o) => o.Invoke(this, expected), Times.Exactly(1)); foo2.Verify((o) => o.Invoke(this, expected), Times.Exactly(1)); } [TestMethod] public void TestRemoveNotExistsListener() { var foo = new Mock(); Assert.IsFalse(eventDispatcher.RemoveListener("foo", foo.Object)); Assert.IsFalse(eventDispatcher.RemoveListener("bar")); } [TestMethod] public void TestStoppableEvent() { var foo1 = new Mock(); var foo2 = new Mock(); var expected = new TestEventArgs(); foo1.Setup((o) => o.Invoke(this, expected)).Callback(() => { expected.StopPropagation(); }); eventDispatcher.AddListener("foo", foo1.Object); eventDispatcher.AddListener("foo", foo2.Object); eventDispatcher.Raise("foo", this, expected); foo1.Verify((o) => o.Invoke(this, expected)); foo2.Verify((o) => o.Invoke(this, expected), Times.Never); } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/AbstractClass.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.Tests.Fixture { public abstract class AbstractClass { } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/Bar.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using System; namespace CatLib.Tests.Fixture { public sealed class Bar : IDisposable { public Bar(int? num = null) { Num = num; } [Inject(Required = false)] public int? Age { get; set; } = 18; public int? Num { get; private set; } public bool Disposed { get; private set; } = false; public override string ToString() { return "bar"; } public void Dispose() { Disposed = true; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/Baz.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CatLib.Tests.Fixture { public sealed class Baz { public Baz(Foo foo, int boo = 100) { Boo = boo; Assert.AreNotEqual(null, foo); Assert.AreNotEqual(null, boo); } [Inject(Required = false)] public string Name { get; set; } = "baz"; [Inject] public Bar Bar { get; set; } [Inject] public int Qux { get; set; } public int Boo { get; private set; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/CircularDependency.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CatLib.Tests.Fixture { public class CircularDependency { public CircularDependency(CircularDependency dependency) { Assert.AreEqual(null, dependency); // This is a class that causes a circular // dependency call to occur in DI. } public virtual object Foo(CircularDependency dependency) { return "foo"; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/Foo.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable CA1822 namespace CatLib.Tests.Fixture { public class Foo : IFoo { public static string Echo(string input) { return input; } public int EchoInt(int input) { return input; } public float EchoFloat(float input) { return input; } public override string ToString() { return "foo"; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/FooBar.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable CA1822 using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CatLib.Tests.Fixture { public class FooBar { public FooBar(Foo foo, Bar bar) { Assert.AreNotEqual(null, foo); Assert.AreNotEqual(null, bar); Foo = foo; Bar = bar; } public Foo Foo { get; private set; } public Bar Bar { get; private set; } public static FooBar New() { return new FooBar(new Foo(), new Bar()); } public string GetName(Foo foo, Bar bar) { return foo.ToString() + bar.ToString(); } public override string ToString() { return Foo.ToString() + Bar.ToString(); } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/Fubar.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable CA2227 using CatLib.Container; using System.Collections.Generic; namespace CatLib.Tests.Fixture { public class Fubar { public Fubar(Bar bar = null, IList heros = null) { Bar = bar; Heros = heros; } public Bar Bar { get; private set; } [Inject(Required = false)] public Position Position { get; private set; } [Inject(Required = false)] public IList Ages { get; set; } public IList Heros { get; set; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/FubarChild.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; using System.Collections.Generic; namespace CatLib.Tests.Fixture { public class FubarChild : Fubar { public FubarChild(Bar bar = null, IList heros = null) : base(bar, heros) { } [Inject(Required = false)] public Foo Foo { get; set; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/IFoo.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.Tests.Fixture { #pragma warning disable CA1040 public interface IFoo #pragma warning restore CA1040 { // empty interface. } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/Position.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable CA1815 namespace CatLib.Tests.Fixture { public struct Position { public int X { get; set; } public int Y { get; set; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/Quux.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.Tests.Fixture { public class Quux { protected Quux() { // There are no constructors available for this class. } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/QuuxFoo.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.Tests.Fixture { public class QuuxFoo : Quux { public QuuxFoo() { throw new TestException(nameof(QuuxFoo)); } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/TestEventArgs.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.EventDispatcher; using System; namespace CatLib.Tests.Fixture { public class TestEventArgs : EventArgs, IStoppableEvent { public bool IsPropagationStopped { get; private set; } public void StopPropagation() { IsPropagationStopped = true; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/Variant.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ namespace CatLib.Tests.Fixture { public class Variant { public Variant(VariantModel model) { Model = model; } public VariantModel Model { get; private set; } } } ================================================ FILE: src/CatLib.Core.Tests/Fixture/VariantModel.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Container; namespace CatLib.Tests.Fixture { [Variant] public class VariantModel { public VariantModel(int id) { if (id == -1) { throw new TestException("VariantModel"); } if (id == 1) { Name = "iron man"; } else if (id == 2) { Name = "black window"; } else { Name = "Undefiend"; } } public string Name { get; } } } ================================================ FILE: src/CatLib.Core.Tests/Framework/ExpectedExceptionAndMessageAttribute.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using SException = System.Exception; namespace CatLib.Tests { /// /// Expectation exception class that allows developers to make exception content decisions. /// public class ExpectedExceptionAndMessageAttribute : ExpectedExceptionBaseAttribute { private readonly Type expectedExceptionType; private readonly string expectedExceptionMessage; private readonly bool strict; public ExpectedExceptionAndMessageAttribute(Type expectedExceptionType) { this.expectedExceptionType = expectedExceptionType; expectedExceptionMessage = string.Empty; } public ExpectedExceptionAndMessageAttribute(Type expectedExceptionType, string expectedExceptionMessage, bool strict = false) { this.expectedExceptionType = expectedExceptionType; this.expectedExceptionMessage = expectedExceptionMessage; this.strict = strict; } protected override void Verify(SException exception) { Assert.IsNotNull(exception); do { if (expectedExceptionType.IsAssignableFrom(exception.GetType())) { break; } exception = exception.InnerException; } while (exception != null); Assert.IsInstanceOfType(exception, expectedExceptionType, "Wrong type of exception was thrown."); if (!expectedExceptionMessage.Length.Equals(0)) { var message = exception.Message.Replace(Environment.NewLine, "\n", StringComparison.Ordinal); if (strict) { Assert.AreEqual(expectedExceptionMessage, message, "Wrong exception message was returned."); } else { if (message.IndexOf( expectedExceptionMessage, StringComparison.InvariantCultureIgnoreCase) < 0) { Assert.AreEqual(expectedExceptionMessage, message, "Wrong exception message was returned."); } } } } } } ================================================ FILE: src/CatLib.Core.Tests/Framework/TestException.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using System.Runtime.Serialization; using SException = System.Exception; namespace CatLib.Tests { public class TestException : SException { public TestException() { } public TestException(string message) : base(message) { } public TestException(string message, SException innerException) : base(message, innerException) { } protected TestException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } } } ================================================ FILE: src/CatLib.Core.Tests/Framework/TestMethodIterativeAttribute.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; namespace CatLib.Core.Tests.Framework { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class TestMethodIterativeAttribute : TestMethodAttribute { private readonly int stabilityThreshold; public TestMethodIterativeAttribute(int stabilityThreshold = 1) { this.stabilityThreshold = stabilityThreshold; } public override TestResult[] Execute(ITestMethod testMethod) { var results = new List(); for (int count = 0; count < stabilityThreshold; count++) { var currentResults = base.Execute(testMethod); results.AddRange(currentResults); } return results.ToArray(); } } } ================================================ FILE: src/CatLib.Core.Tests/IO/TestsCombineStream.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.IO; using System.Text; #pragma warning disable CA1034 namespace CatLib.Tests.IO { [TestClass] public class TestsCombineStream { private Encoding encoding; private Stream foo; private Stream bar; [TestInitialize] public void Initialize() { encoding = Encoding.UTF8; foo = "foo".ToStream(); bar = "bar".ToStream(); } [TestMethod] public void TestCombineStream() { var foobar = new CombineStream(foo, bar); Assert.AreEqual("foobar", foobar.ToText()); } [TestMethod] public void TestRead() { var foobar = new CombineStream(foo, bar); var buffer = new byte[5]; Assert.AreEqual(5, foobar.Read(buffer, 0, 5)); Assert.AreEqual("fooba", encoding.GetString(buffer)); Assert.AreEqual(1, foobar.Read(buffer, 0, 5)); Assert.AreEqual("r", encoding.GetString(buffer, 0, 1)); } [TestMethod] public void TestCombineSeek() { var foobar = new CombineStream(foo, bar); var buffer = new byte[3]; Assert.AreEqual(3, foobar.Read(buffer, 0, 3)); Assert.AreEqual("foo", encoding.GetString(buffer)); Assert.AreEqual(3, foobar.Read(buffer, 0, 3)); Assert.AreEqual("bar", encoding.GetString(buffer)); foobar.Seek(3, SeekOrigin.Begin); Assert.AreEqual(3, foobar.Read(buffer, 0, 3)); Assert.AreEqual("bar", encoding.GetString(buffer)); foobar.Seek(2, SeekOrigin.Begin); Assert.AreEqual(3, foobar.Read(buffer, 0, 3)); Assert.AreEqual("oba", encoding.GetString(buffer)); foobar.Seek(4, SeekOrigin.Begin); Assert.AreEqual(2, foobar.Read(buffer, 0, 3)); Assert.AreEqual("ar", encoding.GetString(buffer, 0, 2)); foobar.Seek(6, SeekOrigin.Begin); Assert.AreEqual(0, foobar.Read(buffer, 0, 3)); Assert.AreEqual(0, foobar.Read(buffer, 0, 3)); foobar.Seek(-3, SeekOrigin.End); Assert.AreEqual(3, foobar.Read(buffer, 0, 3)); Assert.AreEqual("bar", encoding.GetString(buffer)); } [TestMethod] public void TestSetPosition() { var foobar = new CombineStream(foo, bar); var buffer = new byte[3]; foobar.Position = 3; Assert.AreEqual(3, foobar.Read(buffer, 0, 3)); Assert.AreEqual("bar", encoding.GetString(buffer)); } [TestMethod] public void TestCanStatus() { var foobar = new CombineStream(foo, bar); Assert.IsFalse(foobar.CanWrite); Assert.IsTrue(foobar.CanSeek); Assert.IsTrue(foobar.CanRead); } [TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void TestSeekOutOfRange() { var foobar = new CombineStream(foo, bar); foobar.Seek(999, SeekOrigin.Begin); } [TestMethod] public void TestCannotSeekStream() { var foobar = new CombineStream(foo, new CannotSeekStream()); Assert.IsFalse(foobar.CanSeek); } [TestMethod] [ExpectedException(typeof(NotSupportedException))] public void TestCannotSeekStreamSetPosition() { var foobar = new CombineStream(foo, new CannotSeekStream()); foobar.Seek(0, SeekOrigin.Begin); } [TestMethod] public void TestDispose() { Assert.IsTrue(foo.CanWrite); Assert.IsTrue(bar.CanWrite); var foobar = new CombineStream(foo, bar, true); foobar.Dispose(); Assert.IsFalse(foo.CanWrite); Assert.IsFalse(bar.CanWrite); } [TestMethod] [ExpectedException(typeof(NotSupportedException))] public void TestWrite() { var foobar = new CombineStream(foo, bar); foobar.Write(new byte[5], 0, 1); } [TestMethod] [ExpectedException(typeof(NotSupportedException))] public void TestSetLength() { var foobar = new CombineStream(foo, bar); foobar.SetLength(10); } [TestMethod] [ExpectedException(typeof(NotSupportedException))] public void TestFlush() { var foobar = new CombineStream(foo, bar); foobar.Flush(); } private sealed class CannotSeekStream : WrapperStream { public override bool CanSeek => false; } } } ================================================ FILE: src/CatLib.Core.Tests/IO/TestsRingBuffer.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Exception; using CatLib.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; namespace CatLib.Tests.IO { [TestClass] public class TestsRingBuffer { [TestMethod] public void TestReadWrite() { var ringBuffer = new RingBufferStream(); ringBuffer.Write(new byte[] { 1, 2, 4, 5 }, 1, 2); var read = Read(ringBuffer); CollectionAssert.AreEqual( new byte[] { 2, 4, }, read); ringBuffer = new RingBufferStream(); ringBuffer.Write(new byte[] { 1, 2, 3, 4, 5 }, 2, 3); var actual = new byte[5]; var count = ringBuffer.Read(actual, 2, actual.Length - 2); Assert.AreEqual(3, count); CollectionAssert.AreEqual( new byte[] { 0, 0, 3, 4, 5, }, actual); } [TestMethod] public void TestCanRead() { var ringBuffer = new RingBufferStream(); var data = new byte[] { 1, 2, 3, 4, 5 }; ringBuffer.Write(data, 0, data.Length); Assert.IsTrue(ringBuffer.ReadableCount >= 4); Assert.IsTrue(ringBuffer.ReadableCount >= 5); Assert.IsFalse(ringBuffer.ReadableCount >= 6); ringBuffer.Write(data, 0, data.Length); Assert.IsTrue(ringBuffer.ReadableCount >= 9); Assert.IsTrue(ringBuffer.ReadableCount >= 10); Assert.IsFalse(ringBuffer.ReadableCount >= 11); } [TestMethod] public void TestCanWrite() { // 16 cap var ringBuffer = new RingBufferStream(12); Assert.IsTrue(ringBuffer.WriteableCount >= 15); Assert.IsTrue(ringBuffer.WriteableCount >= 16); Assert.IsFalse(ringBuffer.WriteableCount >= 17); ringBuffer.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); Assert.IsTrue(ringBuffer.WriteableCount >= 10); Assert.IsTrue(ringBuffer.WriteableCount >= 11); Assert.IsFalse(ringBuffer.WriteableCount >= 12); ringBuffer.Write(new byte[] { 1, 2, 3, 4, 5 }, 3, 2); Assert.IsTrue(ringBuffer.WriteableCount >= 8); Assert.IsTrue(ringBuffer.WriteableCount >= 9); Assert.IsFalse(ringBuffer.WriteableCount >= 10); } [TestMethod] public void TestPeek() { // 16 cap var ringBuffer = new RingBufferStream(12); var buffer = new byte[] { 1, 2, 3, 4, 5 }; ringBuffer.Write(buffer, 0, buffer.Length); Assert.AreEqual(11, ringBuffer.WriteableCount); Assert.AreEqual(5, ringBuffer.ReadableCount); CollectionAssert.AreEqual( new byte[] { 1, 2, 3, 4, 5, }, Peek(ringBuffer)); var actual = new byte[5]; var read = ringBuffer.Peek(actual, 3, actual.Length - 3); Assert.AreEqual(2, read); CollectionAssert.AreEqual( new byte[] { 0, 0, 0, 1, 2, }, actual); read = ringBuffer.Peek(actual, 0, actual.Length); Assert.AreEqual(5, read); CollectionAssert.AreEqual( new byte[] { 1, 2, 3, 4, 5, }, actual); Assert.AreEqual(11, ringBuffer.WriteableCount); Assert.AreEqual(5, ringBuffer.ReadableCount); Read(ringBuffer); Assert.AreEqual(16, ringBuffer.WriteableCount); Assert.AreEqual(0, ringBuffer.ReadableCount); } [TestMethod] [ExpectedException(typeof(RuntimeException))] public void TestFullBuffer() { var ringBuffer = new RingBufferStream(4); ringBuffer.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); } [TestMethod] public void TestReadEmptyBuffer() { var ringBuffer = new RingBufferStream(4); Assert.AreEqual(0, Read(ringBuffer).Length); Assert.AreEqual(0, Peek(ringBuffer).Length); var count = ringBuffer.Read(new byte[5], 2, 3); Assert.AreEqual(0, count); } [TestMethod] public void TestOutOffset() { var ringBuffer = new RingBufferStream(4); var buffer = new byte[5]; Assert.ThrowsException(() => { ringBuffer.Read(buffer, 2, 10); }); Assert.ThrowsException(() => { ringBuffer.Write(buffer, 2, 10); }); } [TestMethod] public void TestGetBuffer() { // 16 cap var ringBuffer = new RingBufferStream(12); Assert.AreEqual(16, ringBuffer.GetBuffer().Length); ringBuffer = new RingBufferStream(12, false); Assert.ThrowsException(() => { ringBuffer.GetBuffer(); }); } [TestMethod] public void TestBufferReuse() { // 16 cap var ringBuffer = new RingBufferStream(12); var expected = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; ringBuffer.Write(expected, 0, expected.Length); Read(ringBuffer); ringBuffer.Write(expected, 0, expected.Length); var read = Read(ringBuffer); CollectionAssert.AreEqual(expected, read); } [TestMethod] public void TestCapacity() { // 32 cap var ringBuffer = new RingBufferStream(18); Assert.AreEqual(32, ringBuffer.Capacity); } [TestMethod] public void TestClear() { // 32 cap var ringBuffer = new RingBufferStream(18); var data = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; ringBuffer.Write(data, 0, data.Length); ringBuffer.Clear(); Assert.AreEqual(0, ringBuffer.ReadableCount); Assert.AreEqual(32, ringBuffer.WriteableCount); using (ringBuffer = new RingBufferStream(18)) { ringBuffer.Write(data, 0, data.Length); } Assert.AreEqual(0, ringBuffer.ReadableCount); Assert.AreEqual(32, ringBuffer.WriteableCount); } private static byte[] Read(RingBufferStream stream) { var buffer = new byte[stream.Length - stream.Position]; stream.Read(buffer, 0, buffer.Length); return buffer; } private static byte[] Peek(RingBufferStream stream) { var buffer = new byte[stream.Length - stream.Position]; stream.Peek(buffer, 0, buffer.Length); return buffer; } } } ================================================ FILE: src/CatLib.Core.Tests/IO/TestsSegmentStream.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.IO; using System.Text; namespace CatLib.Tests.IO { [TestClass] public class TestsSegmentStream { private Encoding encoding; private Stream foobarbaz; [TestInitialize] public void Initialize() { encoding = Encoding.UTF8; foobarbaz = "foo bar baz".ToStream(); } [TestMethod] public void TestRead() { var segment = new SegmentStream(foobarbaz, 3); Assert.AreEqual("foo", segment.ToText()); } [TestMethod] public void TestReadMiddle() { foobarbaz.Seek(4, SeekOrigin.Begin); var segment = new SegmentStream(foobarbaz, 3); Assert.AreEqual("bar", segment.ToText()); } [TestMethod] public void TestReadEnd() { foobarbaz.Seek(4, SeekOrigin.Begin); var segment = new SegmentStream(foobarbaz); Assert.AreEqual("bar baz", segment.ToText()); } [TestMethod] public void TestSeekEnd() { var segment = new SegmentStream(foobarbaz, 7); segment.Seek(-3, SeekOrigin.End); Assert.AreEqual("bar", segment.ToText()); } [TestMethod] public void TestSeekCurrent() { foobarbaz.Seek(2, SeekOrigin.Begin); var segment = new SegmentStream(foobarbaz, 5); segment.Seek(2, SeekOrigin.Current); Assert.AreEqual("bar", segment.ToText()); } [TestMethod] public void TestSeekBegin() { var segment = new SegmentStream(foobarbaz, 7); segment.ToText(null, false); segment.Seek(0, SeekOrigin.Begin); Assert.AreEqual("foo bar", segment.ToText()); } [TestMethod] public void TestGetLength() { var segmentStream = new SegmentStream(foobarbaz, 7); Assert.AreEqual(7, segmentStream.Length); } [TestMethod] public void TestGetPosition() { foobarbaz.Seek(2, SeekOrigin.Begin); var segment = new SegmentStream(foobarbaz, 5) { Position = 4, }; Assert.AreEqual(4, segment.Position); } [TestMethod] public void TestReadBuffer() { var segment = new SegmentStream(foobarbaz, 7); var actual = new byte[255]; Assert.AreEqual(7, segment.Read(actual, 0, 255)); Assert.AreEqual("foo bar", encoding.GetString(actual, 0, 7)); } [TestMethod] public void TestReadBufferEnd() { var segment = new SegmentStream(foobarbaz, 7); var actual = new byte[255]; Assert.AreEqual(7, segment.Read(actual, 0, 255)); Assert.AreEqual("foo bar", encoding.GetString(actual, 0, 7)); Assert.AreEqual(0, segment.Read(actual, 0, 255)); } [TestMethod] public void TestReadBufferMin() { var segment = new SegmentStream(foobarbaz, 7); var buffer = new byte[3]; Assert.AreEqual(3, segment.Read(buffer, 0, 3)); Assert.AreEqual("foo", encoding.GetString(buffer)); } [TestMethod] [ExpectedException(typeof(NotSupportedException))] public void TestSetLength() { var segment = new SegmentStream(foobarbaz); segment.SetLength(100); } [TestMethod] [ExpectedException(typeof(NotSupportedException))] public void TestWrite() { var segment = new SegmentStream(foobarbaz); var data = encoding.GetBytes("foo"); segment.Write(data, 0, data.Length); } [TestMethod] public void TestSeekEndToRead() { var segment = new SegmentStream(foobarbaz); segment.Seek(999, SeekOrigin.End); Assert.AreEqual(string.Empty, segment.ToText()); } [TestMethod] public void TestSeekSmallThenStart() { var segment = new SegmentStream(foobarbaz); segment.Seek(-999, SeekOrigin.Begin); Assert.AreEqual("foo bar baz", segment.ToText()); } [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void TestGivenCanNotSeek() { new SegmentStream(new CanNotSeekStream()); } private class CanNotSeekStream : WrapperStream { public override bool CanSeek => false; } } } ================================================ FILE: src/CatLib.Core.Tests/IO/TestsStreamExtension.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; using System.Text; namespace CatLib.Tests.IO { [TestClass] public class TestsStreamExtension { [TestMethod] public void TestAppendTo() { var stream1 = "foo".ToStream(); var stream2 = new MemoryStream(); Assert.AreEqual(3, stream1.AppendTo(stream2)); Assert.AreEqual("foo", stream2.ToText()); } [TestMethod] public void TestStreamToText() { var stream = "foo".ToStream(); Assert.AreEqual("foo", stream.ToText()); } [TestMethod] public void TestStreamToTextLarage() { var stream = new MemoryStream(); var builder = new StringBuilder(); var buffer = new byte[4096]; for (var i = 0; i < (buffer.Length / 10) + 1; i++) { stream.Write(Encoding.Default.GetBytes("1234567890"), 0, 10); builder.Append("1234567890"); } stream.Seek(0, SeekOrigin.Begin); Assert.AreEqual(builder.ToString(), stream.ToText()); } [TestMethod] public void TestDoubleWidthText() { var stream = new MemoryStream(); var builder = new StringBuilder(); var buffer = new byte[4096]; for (var i = 0; i < (buffer.Length / 10) + 1; i++) { var data = Encoding.UTF8.GetBytes("12双宽度34测试567890"); stream.Write(data, 0, data.Length); builder.Append("12双宽度34测试567890"); } stream.Seek(0, SeekOrigin.Begin); Assert.AreEqual(builder.ToString(), stream.ToText()); } [TestMethod] public void TestStreamToTextEmpty() { var stream = new MemoryStream(0); Assert.AreEqual(string.Empty, stream.ToText()); } [TestMethod] public void TestStreamClosed() { var stream = new MemoryStream(0); Assert.AreEqual(string.Empty, stream.ToText(null, false)); Assert.AreEqual(true, stream.CanWrite); Assert.AreEqual(string.Empty, stream.ToText()); Assert.AreEqual(false, stream.CanWrite); } } } ================================================ FILE: src/CatLib.Core.Tests/Util/TestsArr.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Util; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; #pragma warning disable CA1031 namespace CatLib.Tests.Util { [TestClass] public sealed class TestsArr { private string[] foo; private string[] bar; private string[] foobar; private string[] foobarbar; private string[] aux; [TestInitialize] public void Initialize() { foo = new[] { "foo" }; bar = new[] { "bar", "baz" }; foobar = new[] { "foo", "bar", "baz" }; foobarbar = new[] { "foo", "bar", "baz", "bar", "baz" }; aux = null; } [TestMethod] public void TestMerge() { var actual = Arr.Merge(foo, bar); CollectionAssert.AreEqual(foobar, actual); } [TestMethod] public void TestMergeAllEmpty() { var actual = Arr.Merge(Array.Empty(), Array.Empty()); Assert.AreEqual(0, actual.Length); } [TestMethod] public void TestMergeNull() { var actual = Arr.Merge(foo, aux, bar); CollectionAssert.AreEqual(foobar, actual); } [TestMethod] public void TestMergeEmpty() { var actual = Arr.Merge(foo, Array.Empty(), bar); CollectionAssert.AreEqual(foobar, actual); } [TestMethod] public void TestRandom() { var source = new[] { "foo", "bar", "baz", "aux" }; var actual = Arr.Rand(source); var i = 0; while (Arr.Rand(source)[0] == actual[0]) { if (i++ > 1000) { Assert.Fail("Random look's not work."); } } CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", "aux", }, source); } [TestMethod] public void TestSplice() { var removed = Arr.Splice(ref foobar, 1, 1, bar); Assert.AreEqual("bar", removed[0]); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", "baz", }, foobar); } [TestMethod] public void TestBaseNegativeSplice() { var removed = Arr.Splice(ref foobar, -1, null, bar); Assert.AreEqual("baz", removed[0]); CollectionAssert.AreEqual( new[] { "foo", "bar", "bar", "baz", }, foobar); } [TestMethod] public void TestSimpleArgsSplice() { var removed = Arr.Splice(ref foobar, 1); CollectionAssert.AreEqual( new[] { "bar", "baz", }, removed); CollectionAssert.AreEqual(new[] { "foo" }, foobar); } [TestMethod] public void TestSimpleNegativeStart() { var removed = Arr.Splice(ref foobar, -1); CollectionAssert.AreEqual(new[] { "baz" }, removed); CollectionAssert.AreEqual( new[] { "foo", "bar", }, foobar); } [TestMethod] public void TestZeroStart() { var removed = Arr.Splice(ref foobar, 0); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, removed); CollectionAssert.AreEqual(Array.Empty(), foobar); } [TestMethod] public void TestOverflowNegativeStart() { var removed = Arr.Splice(ref foobar, -999); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, removed); CollectionAssert.AreEqual(Array.Empty(), foobar); } [TestMethod] public void TestOverflowStart() { var removed = Arr.Splice(ref foobar, 999); CollectionAssert.AreEqual(Array.Empty(), removed); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, foobar); } [TestMethod] public void TestOverflowStartRepl() { var removed = Arr.Splice(ref foobar, 999, -999, bar); CollectionAssert.AreEqual(Array.Empty(), removed); CollectionAssert.AreEqual(foobarbar, foobar); } [TestMethod] public void TestChunk() { var actual = Arr.Chunk(foobarbar, 2); CollectionAssert.AreEqual(new[] { "foo", "bar" }, actual[0]); CollectionAssert.AreEqual(new[] { "baz", "bar" }, actual[1]); CollectionAssert.AreEqual(new[] { "baz" }, actual[2]); } [TestMethod] public void TestChunkInsufficientQuantity() { var source = new[] { "foo" }; var actual = Arr.Chunk(source, 2); CollectionAssert.AreEqual(new[] { "foo" }, actual[0]); } [TestMethod] public void TestChunkBound() { var actual = Arr.Chunk(foobar, 2); CollectionAssert.AreEqual(new[] { "foo", "bar" }, actual[0]); CollectionAssert.AreEqual(new[] { "baz" }, actual[1]); } [TestMethod] public void TestFill() { var actual = Arr.Fill(1, 5, "foo"); CollectionAssert.AreEqual( new[] { null, "foo", "foo", "foo", "foo", "foo", }, actual); } [TestMethod] public void TestFillZeroStart() { var actual = Arr.Fill(0, 5, "foo"); CollectionAssert.AreEqual( new[] { "foo", "foo", "foo", "foo", "foo", }, actual); } [TestMethod] public void TestFillWithSource() { var actual = Arr.Fill(2, 3, "foo", foobar); CollectionAssert.AreEqual( new[] { "foo", "bar", "foo", "foo", "foo", "baz", }, actual); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, foobar); } [TestMethod] public void TestFillBoundWithSource() { var actual = Arr.Fill(3, 3, "foo", foobar); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", "foo", "foo", "foo", }, actual); } [TestMethod] public void TestFillOutOfRangeWithSource() { var actual = Arr.Fill(4, 2, "foo", foobar); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", null, "foo", "foo", }, actual); } [TestMethod] public void TestFillZeroWithSource() { var actual = Arr.Fill(0, 3, "foo", foobar); CollectionAssert.AreEqual( new[] { "foo", "foo", "foo", "foo", "bar", "baz", }, actual); } [TestMethod] public void TestFillThrowException() { Assert.ThrowsException(() => { Arr.Fill(-1, 3, "foo"); }); Assert.ThrowsException(() => { Arr.Fill(0, 0, "foo"); }); } [TestMethod] public void TestFilter() { var actual = Arr.Filter(foobar, (f) => f == "foo"); CollectionAssert.AreEqual(foo, actual); } [TestMethod] public void TestFilterExpected() { var actual = Arr.Filter(foobar, (f) => f == "foo", false); CollectionAssert.AreEqual(bar, actual); } [TestMethod] public void TestFilterIEnumerable() { var actual = Arr.Filter( new List(foobar), (f) => f == "foo"); CollectionAssert.AreEqual(foo, actual); } [TestMethod] public void TestMap() { var actual = Arr.Map(foobar, (o) => o + o); CollectionAssert.AreEqual( new[] { "foofoo", "barbar", "bazbaz", }, actual); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, foobar); } [TestMethod] public void TestMapIEnumerable() { var data = new List(foobar); var actual = Arr.Map(data, (o) => o + o); CollectionAssert.AreEqual( new[] { "foofoo", "barbar", "bazbaz", }, actual); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, data); } [TestMethod] public void TestPop() { var actual = Arr.Pop(ref foobar); Assert.AreEqual("baz", actual); CollectionAssert.AreEqual( new[] { "foo", "bar", }, foobar); } [TestMethod] public void TestPush() { var actual = Arr.Push(ref foobar, "aux", "foobar"); Assert.AreEqual(5, actual); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", "aux", "foobar", }, foobar); } [TestMethod] public void TestReduce() { var actual = Arr.Reduce(foobar, (left, right) => $"{left}-{right}", "heros"); Assert.AreEqual("heros-foo-bar-baz", actual); } [TestMethod] public void TestSlice() { var actual = Arr.Slice(foobar, 1, -1); CollectionAssert.AreEqual( new[] { "bar", }, actual); } [TestMethod] public void TestShift() { var actual = Arr.Shift(ref foobar); Assert.AreEqual("foo", actual); CollectionAssert.AreEqual( new[] { "bar", "baz", }, foobar); } [TestMethod] public void TestUnShift() { var actual = Arr.Unshift(ref foobar, "aux", "foobar"); Assert.AreEqual(5, actual); CollectionAssert.AreEqual( new[] { "aux", "foobar", "foo", "bar", "baz", }, foobar); } [TestMethod] public void TestReverse() { var actual = Arr.Reverse(foobar); CollectionAssert.AreEqual( new[] { "baz", "bar", "foo", }, actual); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, foobar); } [TestMethod] public void TestReverseWithStartLength() { var actual = Arr.Reverse(foobarbar, 1, 2); CollectionAssert.AreEqual( new[] { "baz", "bar", }, actual); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", "bar", "baz", }, foobarbar); } [TestMethod] public void TestIndexOf() { var actual = Arr.IndexOf(foobar, new[] { "bar", "baz" }); Assert.AreEqual(1, actual); } [TestMethod] public void TestIndexOfNull() { Assert.AreEqual(-1, Arr.IndexOf(null, new[] { "foo" })); } [TestMethod] public void TestIndexNotFind() { var actual = Arr.IndexOf(foobar, new[] { "baz", "bar" }); Assert.AreEqual(-1, actual); } [TestMethod] public void TestIndexOfAny() { var actual = Arr.IndexOfAny(foobar, new[] { "baz", "bar" }); Assert.AreEqual(1, actual); } [TestMethod] public void TestIndexAnyNotFind() { var actual = Arr.IndexOfAny(foobar, new[] { "auz" }); Assert.AreEqual(-1, actual); } [TestMethod] public void TestIndexOfAnyNull() { var actual = Arr.IndexOfAny(foobar, null); Assert.AreEqual(-1, actual); actual = Arr.IndexOfAny(null, null); Assert.AreEqual(-1, actual); } [TestMethod] public void TestDifference() { var actual = Arr.Difference(foobar, new[] { "bar", "baz" }); CollectionAssert.AreEqual( new[] { "foo", }, actual); } [TestMethod] public void TestDifferenceEmptyMatch() { var actual = Arr.Difference(foobar, null); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", }, actual); } [TestMethod] public void TestRemoveAt() { var actual = Arr.RemoveAt(ref foobar, 1); Assert.AreEqual("bar", actual); CollectionAssert.AreEqual( new[] { "foo", "baz", }, foobar); } [TestMethod] public void TestRemoveAtWithDefault() { Assert.AreEqual("foo", Arr.RemoveAt(ref foobar, 999, "foo")); } [TestMethod] public void TestRemoveAtNegativeNumber() { var actual = Arr.RemoveAt(ref foobar, -2); Assert.AreEqual("bar", actual); CollectionAssert.AreEqual( new[] { "foo", "baz", }, foobar); actual = Arr.RemoveAt(ref foobar, -999); Assert.AreEqual("foo", actual); CollectionAssert.AreEqual( new[] { "baz", }, foobar); } [TestMethod] public void TestCut() { Arr.Cut(ref foobar, 1); CollectionAssert.AreEqual( new[] { "bar", "baz", }, foobar); Arr.Cut(ref foobar, -1); CollectionAssert.AreEqual( new[] { "bar", }, foobar); Arr.Cut(ref foobar, 0); CollectionAssert.AreEqual( new[] { "bar", }, foobar); Arr.Cut(ref foobar, 999); CollectionAssert.AreEqual(Array.Empty(), foobar); Arr.Cut(ref bar, -999); CollectionAssert.AreEqual(Array.Empty(), bar); } [TestMethod] public void TestRemove() { var actual = Arr.Remove(ref foobar, (o) => o == "bar"); CollectionAssert.AreEqual( new[] { "bar", }, actual); CollectionAssert.AreEqual( new[] { "foo", "baz", }, foobar); actual = Arr.Remove(ref foobarbar, (o) => o == "bar"); CollectionAssert.AreEqual( new[] { "bar", "bar", }, actual); CollectionAssert.AreEqual( new[] { "foo", "baz", "baz", }, foobarbar); } [TestMethod] public void TestTest() { Assert.AreEqual(true, Arr.Test(foobar, (o) => o == "foo")); } [TestMethod] public void TestTestOutMatch() { Assert.AreEqual(true, Arr.Test(foobar, (o) => o == "bar", out string match)); Assert.AreEqual("bar", match); } [TestMethod] public void TestSetReplace() { Arr.Set(ref foobar, (o) => o == "bar", "aux"); CollectionAssert.AreEqual( new[] { "foo", "aux", "baz", }, foobar); } [TestMethod] public void TestSetPush() { Arr.Set(ref foobar, (o) => o == "not-found", "aux"); CollectionAssert.AreEqual( new[] { "foo", "bar", "baz", "aux", }, foobar); } } } ================================================ FILE: src/CatLib.Core.Tests/Util/TestsGuard.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ #pragma warning disable CA1031 using CatLib.Util; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using SException = System.Exception; namespace CatLib.Tests.Util { [TestClass] public class TestsGuard { [TestMethod] public void TestRequires() { var innerException = new SException("inner exception"); try { Guard.Requires(false, "foo", innerException); Assert.Fail(); } catch (SException ex) { Assert.AreEqual("foo", ex.Message); Assert.AreEqual("inner exception", ex.InnerException.Message); } } [TestMethod] public void TestExtend() { var innerException = new SException("inner exception"); Guard.Extend((messgae, inner, state) => { return new ArgumentNullException("foo", inner); }); try { Guard.Requires(false, null, innerException); Assert.Fail(); } catch (SException ex) { Assert.AreEqual("foo", ex.Message); Assert.AreEqual("inner exception", ex.InnerException.Message); } } [TestMethod] public void TestRequireNotBaseException() { var innerException = new SException("inner exception"); try { Guard.Requires(false, "foo", innerException); Assert.Fail(); } catch (SException ex) { Assert.AreEqual("foo", ex.Message); Assert.AreEqual("inner exception", ex.InnerException.Message); } } } } ================================================ FILE: src/CatLib.Core.Tests/Util/TestsSortSet.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Core.Tests.Framework; using CatLib.Util; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; namespace CatLib.Tests.Util { [TestClass] public class TestsSortSet { private SortSet sortset; [TestInitialize] public void Init() { sortset = new SortSet(); } [TestMethod] public void TestRandValue() { var random = new Random(); for (var i = 1000; i >= 1; i--) { sortset.Add(i, random.Next(0, 1000)); } for (var i = 1; i <= 1000; i++) { Assert.IsTrue(sortset.Remove(i), $"can not remove index: {i}."); } Assert.AreEqual(0, sortset.Count); } [TestMethodIterative(100)] public void TestGetElementByRank() { sortset.Add(1000, 85); sortset.Add(999, 75); sortset.Add(998, 185); sortset.Add(997, 85); sortset.Add(996, 185); sortset.Add(995, 85); Assert.AreEqual(1, sortset.GetRank(995)); Assert.AreEqual(995, sortset.GetElementByRank(1)); Assert.AreEqual(997, sortset.GetElementByRank(2)); Assert.AreEqual(1000, sortset.GetElementByRank(3)); Assert.AreEqual(996, sortset.GetElementByRank(4)); Assert.AreEqual(998, sortset.GetElementByRank(5)); } [TestMethod] public void TestCustomComparer() { var foo = new SortSet(new PriorityComparer()); for (var i = 0; i < 10; i++) { foo.Add(i, i); } for (var i = 9; i >= 0; i--) { Assert.AreEqual(i, foo.Shift()); } foo = new SortSet(); for (var i = 0; i < 10; i++) { foo.Add(i, i); } for (var i = 0; i < 10; i++) { Assert.AreEqual(i, foo.Shift()); } } [TestMethod] public void TestAddObject() { var foo = new SortSet(); var collection = new List(); for (var i = 0; i < 10; i++) { var obj = new object(); collection.Add(obj); foo.Add(obj, i); } CollectionAssert.AreEqual(collection, foo.ToArray()); } [TestMethod] public void TestGetElementRangeByRank() { for (var i = 0; i < 10; i++) { sortset.Add(i, i); } var foos = sortset.GetElementRangeByRank(3, 8); var n = 3; foreach (var foo in foos) { Assert.AreEqual(n++, foo); } } [TestMethod] public void TestGetElementRangeWithOutOfRange() { Assert.AreEqual(0, sortset.GetElementRangeByRank(3, 8).Length); } [TestMethod] public void TestGetElementRangeByScore() { for (var i = 0; i < 10; i++) { sortset.Add(i, i); } var foos = sortset.GetElementRangeByScore(3, 8); var n = 3; foreach (var foo in foos) { Assert.AreEqual(n++, foo); } } [TestMethod] public void TestGetElementRangeByScoreOutOfRange() { Assert.AreEqual(0, sortset.GetElementRangeByScore(3, 8).Length); } [TestMethodIterative(100)] public void TestRemoveRangeByScore() { for (var n = 0; n < 10; n++) { sortset.Add(n, n); } sortset.RemoveRangeByScore(3, 8); Assert.AreEqual(0, sortset.GetElementByRank(0)); Assert.AreEqual(1, sortset.GetElementByRank(1)); Assert.AreEqual(2, sortset.GetElementByRank(2)); Assert.AreEqual(9, sortset.GetElementByRank(3)); for (var n = 3; n < 9; n++) { sortset.Add(n, n); } sortset.Add(33, 3); sortset.RemoveRangeByScore(3, 3); Assert.AreEqual(0, sortset.GetElementByRank(0)); Assert.AreEqual(1, sortset.GetElementByRank(1)); Assert.AreEqual(2, sortset.GetElementByRank(2)); Assert.AreEqual(4, sortset.GetElementByRank(3)); } [TestMethodIterative(100)] public void TestGetElementByRevRank() { for (var i = 0; i < 10; i++) { sortset.Add(i, i); } Assert.AreEqual(6, sortset.GetElementByRevRank(3)); Assert.AreEqual(9, sortset.GetElementByRevRank(0)); Assert.AreEqual(0, sortset.GetElementByRevRank(9)); } [TestMethod] public void TestReversEnumerator() { int count = 50000; for (int i = 0; i < count; i++) { sortset.Add(i, i); } int n = 0; foreach (var i in sortset.GetIterator(false)) { Assert.AreEqual(count - (++n), i); } } [TestMethod] public void TestScoreRangeCountBound() { sortset.Add(6, 6); Assert.AreEqual(1, sortset.GetRangeCount(0, 100)); Assert.AreEqual(0, sortset.GetRangeCount(7, 100)); Assert.AreEqual(0, sortset.GetRangeCount(0, 5)); Assert.AreEqual(1, sortset.GetRangeCount(6, 100)); Assert.ThrowsException(() => { Assert.AreEqual(0, sortset.GetRangeCount(800, 100)); }); } [TestMethod] public void TestGetRangeCountWithOutOfRange() { Assert.AreEqual(0, sortset.GetRangeCount(0, 100)); } [TestMethod] public void TestScoreRangeCount() { var rand = new Random(); var expected = new List(); int count = 50000; for (var i = 0; i < count; i++) { var score = rand.Next(0, 1000); sortset.Add(i, score); if (score <= 100) { expected.Add(i); } } Assert.AreEqual(expected.Count, sortset.GetRangeCount(0, 100)); } [TestMethod] public void TestAdd() { int count = 50000; var rand = new Random(); for (var i = 0; i < count; i++) { var value = rand.Next(); sortset.Add(value, value); } var max = 0; foreach (var value in sortset) { if (max <= value) { max = value; } else { Assert.Fail("Element sorting is incorrect."); } } } [TestMethod] public void TestRemove() { int count = 50000; var collection = new List(); var rand = new Random(); for (var i = 0; i < count; i++) { var value = rand.Next(); collection.Add(value); sortset.Add(value, value); } foreach (int value in collection) { sortset.Remove(value); } Assert.AreEqual(0, sortset.Count); } [TestMethod] public void TestGetElementByRankAndIndexer() { var count = 50000; for (var i = 0; i < count; i++) { sortset.Add(i, i); } var rand = new Random(); for (var i = 0; i < Math.Min(count, 100); i++) { var expected = rand.Next(0, count); Assert.AreEqual(expected, sortset.GetElementByRank(expected)); Assert.AreEqual(expected, sortset[expected]); } } [TestMethod] public void TestGetRevRank() { for (int i = 0; i < 10; i++) { sortset.Add(i, i); } Assert.AreEqual(6, sortset.GetRevRank(3)); } [TestMethodIterative(100)] public void TestGetRank() { var count = 100; var collection = new List(); var rand = new Random(); for (var i = 0; i < count; i++) { if (rand.NextDouble() < 0.1) { collection.Add(i); } sortset.Add(i, i); } foreach (var expected in collection) { Assert.AreEqual(expected, sortset.GetRank(expected)); } Assert.AreEqual(-1, sortset.GetRank(-1)); } [TestMethod] public void TestSequentialAdd() { for (var i = 0; i < 50000; i++) { sortset.Add(i, i); } var expected = 0; foreach (var i in sortset) { Assert.AreEqual(expected++, i); } } [TestMethod] public void TestEmptyListForeach() { foreach (var item in sortset) { Assert.Fail("Iteration is not allowed."); } foreach (var item in sortset.GetIterator(false)) { Assert.Fail("Iteration is not allowed."); } } [TestMethod] public void TestOverrideElement() { sortset.Add(10, 100); sortset.Add(10, 200); Assert.AreEqual(200, sortset.GetScore(10)); } [TestMethod] public void TestContains() { sortset.Add(10, 100); Assert.IsTrue(sortset.Contains(10)); Assert.IsFalse(sortset.Contains(11)); } [TestMethod] public void TestGetScore() { sortset.Add(10, 100); Assert.AreEqual(100, sortset.GetScore(10)); } [TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void TestGetElementByRankOverflow() { sortset.Add(10, 10); sortset.GetElementByRank(1000); } [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void TestGetElementByRankEmpty() { sortset.GetElementByRank(100); } [TestMethod] public void TestGetRankOverflow() { sortset.Add(10, 100); Assert.AreEqual(-1, sortset.GetRank(100)); } [TestMethod] public void TestGetRevRankOverflow() { sortset.Add(10, 100); Assert.AreEqual(-1, sortset.GetRevRank(100)); Assert.AreEqual(0, sortset.GetRevRank(10)); } [TestMethod] public void TestMaxLevelLimit() { sortset = new SortSet(0.5, 3); for (var i = 0; i < 2048; i++) { sortset.Add(i, i); } } [TestMethod] public void TestClear() { for (var i = 0; i < 65536; i++) { sortset.Add(i, i); } sortset.Clear(); for (var i = 0; i < 65536; i++) { sortset.Add(i, i); } for (var i = 0; i < 65536; i++) { Assert.AreEqual(i, sortset.GetRank(i)); } Assert.AreEqual(65536, sortset.Count); } [TestMethod] public void TestFirstLast() { for (var i = 0; i < 65536; i++) { sortset.Add(i, i); } Assert.AreEqual(0, sortset.First()); Assert.AreEqual(65535, sortset.Last()); for (var i = 0; i < 65536; i++) { Assert.AreEqual(i, sortset.First()); Assert.AreEqual(i, sortset.Shift()); } Assert.AreEqual(0, sortset.Count); sortset.Clear(); for (var i = 0; i < 65536; i++) { sortset.Add(i, i); } for (var i = 0; i < 65536; i++) { Assert.AreEqual(65535 - i, sortset.Last()); Assert.AreEqual(65535 - i, sortset.Pop()); } } [TestMethod] public void TestPop() { for (var i = 0; i < 65536; i++) { sortset.Add(i, i); } for (var i = 0; i < 65536; i++) { Assert.AreEqual(65535 - i, sortset.Pop()); } Assert.AreEqual(0, sortset.Count); } [TestMethod] public void TestShift() { for (var i = 0; i < 65536; i++) { sortset.Add(i, i); } for (var i = 0; i < 65536; i++) { Assert.AreEqual(i, sortset.Shift()); } Assert.AreEqual(0, sortset.Count); } [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void TestFirstBound() { sortset.First(); } [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void TestLastBound() { sortset.Last(); } [TestMethod] public void TestToArray() { sortset.Add(1, 1); sortset.Add(9, 9); sortset.Add(2, 2); sortset.Add(8, 8); sortset.Add(0, 0); sortset.Add(4, 4); sortset.Add(5, 5); sortset.Add(6, 6); sortset.Add(3, 3); sortset.Add(7, 7); var expected = 0; foreach (var actual in sortset.ToArray()) { Assert.AreEqual(expected++, actual); } } [TestMethod] public void TestSameScore() { // According to the ordered set rule, the same // score after the insertion will be traversed // to the priority sortset.Add(10, 10); sortset.Add(90, 10); sortset.Add(20, 10); sortset.Add(80, 10); var actual = sortset.ToArray(); Assert.AreEqual(80, actual[0]); Assert.AreEqual(20, actual[1]); Assert.AreEqual(90, actual[2]); Assert.AreEqual(10, actual[3]); } [TestMethod] public void TestRemoveNotExistsElement() { Assert.IsFalse(sortset.Remove(8888)); } [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void TestPopEmpty() { sortset.Pop(); } [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void TestShiftEmpty() { sortset.Shift(); } [TestMethod] public void TestIterationsDeleted() { for (var i = 0; i < 100; i++) { sortset.Add(i, i); } var expected = new List() { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }; var actuals = new List(); foreach (var i in sortset) { actuals.Add(i); sortset.Remove(i); sortset.Remove(i + 1); sortset.Remove(i + 2); sortset.Remove(i + 3); sortset.Remove(i + 4); sortset.Remove(i + 5); sortset.Remove(i + 6); sortset.Remove(i + 7); sortset.Remove(i + 8); sortset.Remove(i + 9); } var n = 0; foreach (var actual in actuals) { Assert.AreEqual(expected[n++], actual); } Assert.AreEqual(expected.Count, actuals.Count); } [TestMethod] [ExpectedException(typeof(KeyNotFoundException))] public void TestGetScoreNotFound() { sortset.GetScore(100); } private class PriorityComparer : IComparer { public int Compare(int x, int y) { return y - x; } } } } ================================================ FILE: src/CatLib.Core.Tests/Util/TestsStr.cs ================================================ /* * This file is part of the CatLib package. * * (c) CatLib * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * Document: https://catlib.io/ */ using CatLib.Util; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Text; using System.Text.RegularExpressions; namespace CatLib.Tests.Util { [TestClass] public class TestsStr { [TestMethod] [DataRow(@"foo\.\?\+/bar/ba.*?z", "foo.?+/bar/ba*z", "Special characters should be escaped")] public void TestAsteriskWildcard(string expected, string input, string message) { Assert.AreEqual(expected, Str.AsteriskWildcard(input), message); } [TestMethod] [DataRow(true, "foo.?+/bar/b*z", "foo.?+/bar/baz")] [DataRow(true, "foob*r", "fooboooooooor")] [DataRow(true, "foo", "foo")] [DataRow(false, "foo.?+/bar/b*z", "foobar.?+/bar/baz")] [DataRow(false, "foob*r", "fooboooooooorp")] public void TestIs(bool expected, string pattern, string value) { Assert.AreEqual(expected, Str.Is(pattern, value)); } [TestMethod] public void TestSplit() { var actual = Str.Split("foobaro", 2); Assert.AreEqual("fo", actual[0]); Assert.AreEqual("ob", actual[1]); Assert.AreEqual("ar", actual[2]); Assert.AreEqual("o", actual[3]); Assert.AreEqual(4, actual.Length); actual = Str.Split("foobar", 2); Assert.AreEqual("fo", actual[0]); Assert.AreEqual("ob", actual[1]); Assert.AreEqual("ar", actual[2]); Assert.AreEqual(3, actual.Length); } [TestMethod] public void TestSplitEmpty() { var actual = Str.Split(string.Empty, 2); Assert.AreEqual(0, actual.Length); } [TestMethod] public void TestRepeat() { Assert.AreEqual("foofoo", Str.Repeat("foo", 2)); Assert.AreEqual("foo", Str.Repeat("foo", 1)); Assert.AreEqual(string.Empty, Str.Repeat("foo", 0)); } [TestMethod] public void TestShuffle() { var i = 0; var foo = "foobar"; while (Str.Shuffle(foo) == "foobar") { if (i++ > 1000) { Assert.Fail("Must disturb the string."); } } } [TestMethod] public void TestShuffleEmpty() { Assert.AreEqual(string.Empty, Str.Shuffle(string.Empty)); } [TestMethod] public void TestSubstringCount() { var count = Str.SubstringCount("foobarbaz", "ba"); Assert.AreEqual(2, count); count = Str.SubstringCount("foobar", "o"); Assert.AreEqual(2, count); count = Str.SubstringCount("foobarbaz", "b", 5); Assert.AreEqual(1, count); count = Str.SubstringCount("foobarfoobar", "r", 5, 5); Assert.AreEqual(1, count); count = Str.SubstringCount("foobar", "l", 0, null, StringComparison.CurrentCulture); Assert.AreEqual(0, count); count = Str.SubstringCount("abcabcab", "abcab"); Assert.AreEqual(1, count); count = Str.SubstringCount(string.Empty, "foo"); Assert.AreEqual(0, count); } [TestMethod] public void TestSubstringStartLargeThanLength() { Assert.AreEqual(0, Str.SubstringCount("foobar", "foo", 999, 9)); } [TestMethod] public void TestReverse() { Assert.AreEqual("oof", Str.Reverse("foo")); } [TestMethod] public void TestPad() { var actual = Str.Pad(10, "foo", "barbaz", Str.PadType.Both); Assert.AreEqual("barfoobarb", actual); actual = Str.Pad(10, "foo", "barbaz"); Assert.AreEqual("foobarbazb", actual); actual = Str.Pad(10, "foo", "barbaz", Str.PadType.Left); Assert.AreEqual("barbazbfoo", actual); actual = Str.Pad(3, "foo", "barbaz", Str.PadType.Left); Assert.AreEqual("foo", actual); actual = Str.Pad(10, "foo", null, Str.PadType.Left); Assert.AreEqual(" foo", actual); actual = Str.Pad(10, "foo", string.Empty, Str.PadType.Left); Assert.AreEqual(" foo", actual); } [TestMethod] public void TestStrPadEmpty() { var actual = Str.Pad(10, string.Empty, "foo", Str.PadType.Left); Assert.AreEqual("foofoofoof", actual); actual = Str.Pad(10, string.Empty, "foobar", Str.PadType.Both); Assert.AreEqual("foobafooba", actual); actual = Str.Pad(10, null, "foobar"); Assert.AreEqual("foobarfoob", actual); } [TestMethod] [DataRow("bar", "foobar", "foo")] [DataRow("bar", "foobar", "oo")] [DataRow("", "foobar", "bar")] [DataRow("foobar", "foobar", "baz")] public void TestAfter(string expected, string value, string search) { Assert.AreEqual(expected, Str.After(value, search)); } [TestMethod] public void TestContains() { Assert.AreEqual(true, Str.Contains("foobar", "baz", "foo")); Assert.AreEqual(false, Str.Contains("foobar", "baz", "aux")); } [TestMethod] public void TestReplace() { var actual = Str.Replace(new[] { "+", "-", "bar" }, string.Empty, "fo+obar,f-+oobar"); Assert.AreEqual("foo,foo", actual); actual = Str.Replace(new[] { "+", "-", "oo" }, string.Empty, "fo+obar,f-+oobar"); Assert.AreEqual("fbar,fbar", actual); } [TestMethod] public void TestReplaceFirst() { var actual = Str.ReplaceFirst("foo", "baz", "foobar,foobar"); Assert.AreEqual("bazbar,foobar", actual); actual = Str.ReplaceFirst("bar", "baz", "foo,bar"); Assert.AreEqual("foo,baz", actual); } [TestMethod] public void TestReplaceLast() { var actual = Str.ReplaceLast("foo", "baz", "foobar,foobar"); Assert.AreEqual("foobar,bazbar", actual); actual = Str.ReplaceLast("foobar", "baz", "foobar,foobar"); Assert.AreEqual("foobar,baz", actual); } [TestMethod] public void TestRandom() { var i = 0; var str = Str.Random(); while (Str.Random() == str) { if (i++ > 1000) { Assert.Fail("Need random numbers."); } } Assert.AreEqual(16, str.Length, "The random length defaults to 16."); } [TestMethod] public void TestSpace() { Assert.AreEqual(" ", Str.Space); } [TestMethod] public void TestTruncate() { var actual = Str.Truncate("hello world , the sun is shine", 11); Assert.AreEqual("hello wo...", actual); actual = Str.Truncate("hello world , the sun is shine", 11, Str.Space); Assert.AreEqual("hello...", actual); actual = Str.Truncate("hello world , the sun is shine", 15, Str.Space); Assert.AreEqual("hello world...", actual); actual = Str.Truncate("hello world sun sname", 15, Str.Space); Assert.AreEqual("hello world...", actual); var regex = new Regex("orl"); actual = Str.Truncate("hello worldrldddorl sun sname", 15, regex); Assert.AreEqual("hello w...", actual); regex = new Regex("rld"); actual = Str.Truncate("hello worldrldddorl sun sname", 17, regex); Assert.AreEqual("hello world...", actual); actual = Str.Truncate(null, 17, regex); Assert.AreEqual(null, actual); actual = Str.Truncate("hello world", 17, regex); Assert.AreEqual("hello world", actual); regex = new Regex("rld", RegexOptions.RightToLeft); actual = Str.Truncate("hello worldrldddorl sun sname", 17, regex); Assert.AreEqual("hello world...", actual); actual = Str.Truncate("hel", 2); Assert.AreEqual("...", actual); actual = Str.Truncate(string.Empty, -1); Assert.AreEqual("...", actual); actual = Str.Truncate("喵h喵e越l来l越l漂o亮!了", 12, "l"); Assert.AreEqual("喵h喵e越l来...", actual); actual = Str.Truncate("喵h喵e越l来l越l漂o亮!了", 12); Assert.AreEqual("喵h喵e越l来l越...", actual); } [TestMethod] public void TestMethod() { Assert.AreEqual("GetNameSpace", Str.Method("Helloworld.GetNameSpace")); Assert.AreEqual("GetNameSpace", Str.Method("Helloworld.GetNameSpace()")); Assert.AreEqual("Space", Str.Method("Helloworld.GetName@Space()")); Assert.AreEqual("_Space", Str.Method("Helloworld.GetName@_Space()")); Assert.AreEqual("g8975GetNameSpace", Str.Method("Helloworld.g8975GetNameSpace()")); Assert.AreEqual("GetNameSpace", Str.Method("Helloworld.8975GetNameSpace()")); Assert.AreEqual("ame_Space", Str.Method("Helloworld.8975GetN(;)ame_Space()")); Assert.AreEqual("GetName_Space", Str.Method("Helloworld.8GetName_Space()")); Assert.AreEqual(string.Empty, Str.Method(null)); Assert.AreEqual(string.Empty, Str.Method(string.Empty)); } [TestMethod] public void TestIsArray() { var actual = Arr.Filter( new string[] { "CatLib.Core", "CatLib.ILRuntime", "CatLib.Route", "Hello.World", }, (assembly) => Str.Is( new string[] { "CatLib.*", }, assembly)); CollectionAssert.AreEqual( new[] { "CatLib.Core", "CatLib.ILRuntime", "CatLib.Route", }, actual); } [TestMethod] public void TestLevenshtein() { Assert.AreEqual(4, Str.Levenshtein("hello", "world")); Assert.AreEqual(5, Str.Levenshtein("hello", "catlib")); Assert.AreEqual(10, Str.Levenshtein("hello", "catlib-world")); } [TestMethod] public void TestLevenshteinLargeThan255() { var builder = new StringBuilder(256); for (var i = 0; i < 256; i++) { builder.Append('a'); } Assert.AreEqual(256, builder.Length); Assert.AreEqual(-1, Str.Levenshtein(builder.ToString(), "foo")); } [TestMethod] public void TestLevenshteinNull() { Assert.AreEqual(-1, Str.Levenshtein(null, "foo")); } [TestMethod] public void TestJoinList() { var actual = Str.JoinList(new[] { "foo", "bar", "baz" }, "/"); CollectionAssert.AreEqual( new[] { "foo", "foo/bar", "foo/bar/baz", }, actual); } [TestMethod] public void TestJoinListChar() { var actual = Str.JoinList(new[] { "foo", "bar", "baz" }, '@'); CollectionAssert.AreEqual( new[] { "foo", "foo@bar", "foo@bar@baz", }, actual); } } } ================================================ FILE: src/Directory.Build.props ================================================ $(MSBuildProjectName.Contains('Tests')) true all runtime; build; native; contentfiles; analyzers all runtime; build; native; contentfiles; analyzers $(MSBuildThisFileDirectory)analysis.test.ruleset all runtime; build; native; contentfiles; analyzers $(MSBuildThisFileDirectory)analysis.ruleset ================================================ FILE: src/analysis.ruleset ================================================  ================================================ FILE: src/analysis.test.ruleset ================================================  ================================================ FILE: src/settings.runsettings ================================================ %temp%\TestResults .*\.tests\.dll$ .*\.ExcludeFromCodeCoverageAttribute$ .*\\3rd\\.* .*\\Vendor\\.* .*\\Exception\\.* ================================================ FILE: src/stylecop.json ================================================ { "settings":{ "orderingRules":{ "usingDirectivesPlacement" : "outsideNamespace" }, "documentationRules":{ "documentInternalElements" : false }, "readabilityRules":{ "allowBuiltInTypeAliases": true }, "maintainabilityRules":{ "topLevelTypes" : [ "class", "interface", "enum", "delegate", "struct" ] }, "layoutRules":{ "newlineAtEndOfFile" : "require" } } }