diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..8f642da
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,79 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+ij_xml_attribute_wrap = off
+ij_xml_text_wrap = off
+indent_size = 4
+indent_style = space
+insert_final_newline = false
+trim_trailing_whitespace = true
+
+[{*.json,*.yml}]
+indent_size = 2
+
+[*.cs]
+dotnet_analyzer_diagnostic.severity = warning
+dotnet_diagnostic.CA1200.severity = none
+dotnet_diagnostic.CA1707.severity = none
+dotnet_diagnostic.CA1716.severity = none
+dotnet_diagnostic.IDE0005.severity = none
+dotnet_diagnostic.IDE0008.severity = none
+dotnet_diagnostic.IDE0010.severity = none
+dotnet_diagnostic.IDE0055.severity = none
+dotnet_diagnostic.IDE0160.severity = none
+dotnet_diagnostic.IDE0270.severity = none
+dotnet_diagnostic.RCS1141.severity = none
+dotnet_diagnostic.RCS1142.severity = none
+dotnet_diagnostic.RCS1181.severity = none
+dotnet_diagnostic.RCS1186.severity = none
+dotnet_diagnostic.S101.severity = none
+dotnet_diagnostic.S1121.severity = none
+dotnet_diagnostic.S1199.severity = none
+dotnet_diagnostic.S125.severity = none
+dotnet_diagnostic.S2094.severity = none
+dotnet_diagnostic.S3925.severity = none
+dotnet_diagnostic.S4663.severity = none
+dotnet_diagnostic.SYSLIB1045.severity = none
+
+
+# ReSharper properties
+resharper_align_linq_query = true
+resharper_align_multiline_argument = true
+resharper_align_multiline_array_and_object_initializer = true
+resharper_align_multiline_binary_patterns = true
+resharper_align_multiline_calls_chain = true
+resharper_align_multiline_extends_list = true
+resharper_align_multiline_parameter = true
+resharper_align_multiline_property_pattern = true
+resharper_align_multiline_switch_expression = true
+resharper_align_multiple_declaration = true
+resharper_align_multline_type_parameter_constrains = true
+resharper_align_multline_type_parameter_list = true
+resharper_align_tuple_components = true
+resharper_allow_comment_after_lbrace = true
+resharper_blank_lines_before_single_line_comment = 1
+resharper_csharp_empty_block_style = together_same_line
+resharper_csharp_outdent_commas = true
+resharper_csharp_place_type_constraints_on_same_line = false
+resharper_csharp_stick_comment = false
+resharper_csharp_wrap_before_comma = true
+resharper_indent_nested_for_stmt = true
+resharper_indent_nested_foreach_stmt = true
+resharper_indent_nested_while_stmt = true
+resharper_indent_preprocessor_if = usual_indent
+resharper_indent_preprocessor_other = usual_indent
+resharper_int_align = true
+resharper_keep_existing_arrangement = false
+resharper_place_linq_into_on_new_line = false
+resharper_place_simple_embedded_statement_on_same_line = false
+resharper_place_simple_switch_expression_on_single_line = true
+resharper_wrap_before_eq = true
+resharper_wrap_chained_method_calls = chop_if_long
+resharper_wrap_switch_expression = chop_if_long
+
+
+# Microsoft .NET properties
+csharp_indent_braces = false
+csharp_new_line_before_open_brace = local_functions, methods, types
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 8d5a8e9..acde096 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,405 @@
-pkg
-build
-bin
-obj
-packages
-Migrations
-_gsdata_
-_ReSharper*
-TestResults
-app_data
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
*.suo
*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+# [Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
-*.publish.xml
-.svn
-.vs
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
*.dbmdl
+*.dbproj.schemaview
*.jfm
-*.exe
-.idea
-node_modules
-dist
\ No newline at end of file
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+.idea/
+
+# User Define
+dist/
+refs/
+*.[Dd]esigner.cs
+*.db
diff --git a/.tgitconfig b/.tgitconfig
index c98b9ad..cea19aa 100644
--- a/.tgitconfig
+++ b/.tgitconfig
@@ -1,4 +1,4 @@
[hook "startcommit"]
- cmdline = code-format.cmd
- wait = true
- show = true
\ No newline at end of file
+ cmdline = dot.clean.cmd
+ wait = true
+ show = true
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 371971c..6a86153 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,26 @@
- ../build/temp/bin
- ../build/temp/obj
- ../build/temp/obj
+
+ $(MSBuildThisFileDirectory)
+ nsnail
+ $(SolutionDir)/dist/$(MSBuildProjectName)/obj
+ $(SolutionDir)/dist/$(MSBuildProjectName)/bin
+ © 2006-2023 nsnail
+ A .NET extension function library
+ false
+ true
+ enable
+ beta
+ NSExt
+ git
+ https://github.com/nsnail/NSExt.git
+ net7.0;net8.0
+ $(AssemblyName)
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7777560
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 nsnail
+
+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.
\ No newline at end of file
diff --git a/NSExt.sln b/NSExt.sln
new file mode 100644
index 0000000..5897daf
--- /dev/null
+++ b/NSExt.sln
@@ -0,0 +1,57 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{5198A03D-0CAC-4828-A807-34A693F73859}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ .gitattributes = .gitattributes
+ .gitignore = .gitignore
+ .tgitconfig = .tgitconfig
+ build.cake = build.cake
+ code.cleanup.full.ps1 = code.cleanup.full.ps1
+ code.quality.props = code.quality.props
+ Directory.Build.props = Directory.Build.props
+ dot.clean.cmd = dot.clean.cmd
+ dotnet-tools.json = dotnet-tools.json
+ git.pr.ps1 = git.pr.ps1
+ git.rc.ps1 = git.rc.ps1
+ global.json = global.json
+ image.optimize.csx = image.optimize.csx
+ key.snk = key.snk
+ LICENSE = LICENSE
+ logo.png = logo.png
+ NSExt.sln.DotSettings = NSExt.sln.DotSettings
+ nuget.config = nuget.config
+ packable.props = packable.props
+ README.md = README.md
+ README.zh-CN.md = README.zh-CN.md
+ stylecop.analyzers.ruleset = stylecop.analyzers.ruleset
+ stylecop.json = stylecop.json
+ sync.metafiles.csx = sync.metafiles.csx
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSExt.Tests", "src\NSExt.Tests\NSExt.Tests.csproj", "{557FBEF6-E6D5-4531-86DF-D772A10E2261}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSExt", "src\NSExt\NSExt.csproj", "{BA0982BE-6E57-4AAF-9778-F9B2EB46F505}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {557FBEF6-E6D5-4531-86DF-D772A10E2261}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {557FBEF6-E6D5-4531-86DF-D772A10E2261}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {557FBEF6-E6D5-4531-86DF-D772A10E2261}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {557FBEF6-E6D5-4531-86DF-D772A10E2261}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BA0982BE-6E57-4AAF-9778-F9B2EB46F505}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA0982BE-6E57-4AAF-9778-F9B2EB46F505}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA0982BE-6E57-4AAF-9778-F9B2EB46F505}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA0982BE-6E57-4AAF-9778-F9B2EB46F505}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
\ No newline at end of file
diff --git a/NSExt.sln.DotSettings b/NSExt.sln.DotSettings
new file mode 100644
index 0000000..30a9cb5
--- /dev/null
+++ b/NSExt.sln.DotSettings
@@ -0,0 +1,106 @@
+
+ Inherit
+ True
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ NEVER
+ NEVER
+ NEVER
+ NEVER
+ ID
+ IOS
+ IP
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" />
+
+ True
+ True
+ True
+ True
+ True
+ 1
+ 1
+ OFF
+ HINT
+ Required
+ Required
+ Required
+ Required
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
+ <Policy Inspect="True" Prefix="_" Suffix="" Style="AA_BB" />
+ <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <?xml version="1.0" encoding="utf-16"?>
+<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
+ <TypePattern>
+ <Entry>
+ <Entry.SortBy>
+ <Kind>
+ <Kind.Order>
+ <DeclarationKind>Constant</DeclarationKind>
+ <DeclarationKind>Field</DeclarationKind>
+ <DeclarationKind>Constructor</DeclarationKind>
+ <DeclarationKind>Destructor</DeclarationKind>
+ <DeclarationKind>Delegate</DeclarationKind>
+ <DeclarationKind>Event</DeclarationKind>
+ <DeclarationKind>Enum</DeclarationKind>
+ <DeclarationKind>Interface</DeclarationKind>
+ <DeclarationKind>Property</DeclarationKind>
+ <DeclarationKind>Indexer</DeclarationKind>
+ <DeclarationKind>Method</DeclarationKind>
+ <DeclarationKind>Struct</DeclarationKind>
+ <DeclarationKind>Record</DeclarationKind>
+ <DeclarationKind>Class</DeclarationKind>
+ </Kind.Order>
+ </Kind>
+ <Access>
+ <Access.Order>
+ <AccessModifier>Public</AccessModifier>
+ <AccessModifier>Internal</AccessModifier>
+ <AccessModifier>ProtectedInternal</AccessModifier>
+ <AccessModifier>Protected</AccessModifier>
+ <AccessModifier>PrivateProtected</AccessModifier>
+ <AccessModifier>Private</AccessModifier>
+ </Access.Order>
+ </Access>
+ <Static />
+ <Readonly />
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ </TypePattern>
+</Patterns>
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 90f739e..0e53b79 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,65 @@
-# ns-ext
\ No newline at end of file
+# ns-ext
+
+[中](README.zh-CN.md) | **En**
+The **ns-ext** is a .NET extension function library, containing the following types of extension modules:
+
+| Features | File name |
+|------------------------------------------|------------------------------------|
+| Byte type extension | ByteExtensions.cs |
+| Character Type Extensions | CharExtensions.cs |
+| Date Type Extensions | DateTimeExtensions.cs |
+| Database command type extension | DbCommandExtensions.cs |
+| Decimal Number Type extension | DecimalExtensions.cs |
+| Enumable type extension | EnumerableExtensions.cs |
+| Enumeration type extension | EnumExtensions.cs |
+| General type extension | GenericExtensions.cs |
+| Integer type extension | IntExtensions.cs |
+| Json Serialization option type extension | JsonSerializerOptionsExtensions.cs |
+| Log type extension | LoggerExtensions.cs |
+| Long integer extension | LongExtensions.cs |
+| Object type extension | ObjectExtensions.cs |
+| Stream type extension | StreamExtensions.cs |
+| String type extension | StringExtensions.cs |
+| Prototype type extension | TypeExtensions.cs |
+| Resource locator type extension | UriExtensions.cs |
+
+## Quick start
+
+### Install
+
+```shell
+dotnet add package NSExt --prerelease
+```
+
+### Example
+
+```c#
+using NSExt.Extensions;
+
+internal static class Program
+{
+ private class Person
+ {
+ public string Name { get; set; }
+ public int Age { get; set; }
+ }
+ public static void Main(string[] args)
+ {
+ var person =
+ """
+{
+ "Name": "Jason",
+ "Age": "30"
+}
+""".Object();
+
+ Console.WriteLine(person.Json());
+ }
+}
+```
+
+### Output
+
+```json
+{"name":"Jason","age":30}
+```
\ No newline at end of file
diff --git a/README.zh-CN.md b/README.zh-CN.md
new file mode 100644
index 0000000..127d239
--- /dev/null
+++ b/README.zh-CN.md
@@ -0,0 +1,65 @@
+# ns-ext
+[En](README.md) | **中**
+**ns-ext** 是一个.NET扩展函数库,包含以下类型扩展模块:
+
+
+| 功能 | 文件名 |
+| -------- | ---------------------------------- |
+| 字节类型扩展 | ByteExtensions.cs |
+| 字符类型扩展 | CharExtensions.cs |
+| 日期类型扩展 | DateTimeExtensions.cs |
+| 数据库命令类型扩展 | DbCommandExtensions.cs |
+| 十进制数类型扩展 | DecimalExtensions.cs |
+| 可枚举类型扩展 | EnumerableExtensions.cs |
+| 枚举类型扩展 | EnumExtensions.cs |
+| 泛型类型扩展 | GenericExtensions.cs |
+| 整数型扩展 | IntExtensions.cs |
+| Json序列化选项类型扩展 | JsonSerializerOptionsExtensions.cs |
+| 日志类型扩展 | LoggerExtensions.cs |
+| 长整型扩展 | LongExtensions.cs |
+| 对象类型扩展 | ObjectExtensions.cs |
+| 流类型扩展 | StreamExtensions.cs |
+| 字符串类型扩展 | StringExtensions.cs |
+| 原型类型扩展 | TypeExtensions.cs |
+| 资源定位符类型扩展 | UriExtensions.cs |
+
+## 快速开始
+
+### 安装
+
+```shell
+dotnet add package NSExt --prerelease
+```
+
+### 示例
+
+```c#
+using NSExt.Extensions;
+
+internal static class Program
+{
+ private class Person
+ {
+ public string Name { get; set; }
+ public int Age { get; set; }
+ }
+ public static void Main(string[] args)
+ {
+ var person =
+ """
+{
+ "Name": "Jason",
+ "Age": "30"
+}
+""".Object();
+
+ Console.WriteLine(person.Json());
+ }
+}
+```
+
+### 输出
+
+```json
+{"name":"Jason","age":30}
+```
\ No newline at end of file
diff --git a/build.cake b/build.cake
new file mode 100644
index 0000000..2fb6a2b
--- /dev/null
+++ b/build.cake
@@ -0,0 +1,117 @@
+var target = Argument("target", "Default");
+var configuration = Argument("configuration", "Release");
+var framework = Argument("framework", "net7.0");
+
+////////////////////////////////////////////////////////////////
+// Tasks
+
+Task("Clean")
+ .Does(context =>
+{
+ context.CleanDirectory("./dist");
+});
+
+Task("Build")
+ .IsDependentOn("Clean")
+ .Does(context =>
+{
+ DotNetPublish("./src/NSExt/NSExt.csproj", new DotNetPublishSettings {
+ Configuration = configuration,
+ Framework = framework,
+ });
+});
+
+
+// Task("Test")
+// .IsDependentOn("Build")
+// .Does(context =>
+// {
+// DotNetTest("./test/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetTestSettings {
+// Configuration = configuration,
+// NoRestore = true,
+// NoBuild = true,
+// });
+//
+// DotNetTest("./test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj", new DotNetTestSettings {
+// Configuration = configuration,
+// NoRestore = true,
+// NoBuild = true,
+// });
+//
+// DotNetTest("./test/Spectre.Console.Analyzer.Tests/Spectre.Console.Analyzer.Tests.csproj", new DotNetTestSettings {
+// Configuration = configuration,
+// NoRestore = true,
+// NoBuild = true,
+// });
+// });
+
+
+// Task("Publish-GitHub")
+// .WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
+// //.IsDependentOn("Package")
+// .Does(context =>
+// {
+// var apiKey = Argument("github-key", null);
+// if(string.IsNullOrWhiteSpace(apiKey)) {
+// throw new CakeException("No GitHub API key was provided.");
+// }
+//
+// // Publish to GitHub Packages
+// var exitCode = 0;
+// foreach(var file in context.GetFiles("./.artifacts/*.nupkg"))
+// {
+// context.Information("Publishing {0}...", file.GetFilename().FullPath);
+// exitCode += StartProcess("dotnet",
+// new ProcessSettings {
+// Arguments = new ProcessArgumentBuilder()
+// .Append("gpr")
+// .Append("push")
+// .AppendQuoted(file.FullPath)
+// .AppendSwitchSecret("-k", " ", apiKey)
+// }
+// );
+// }
+//
+// if(exitCode != 0)
+// {
+// throw new CakeException("Could not push GitHub packages.");
+// }
+// });
+
+Task("Publish-NuGet")
+ //.WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
+ .IsDependentOn("Build")
+ .Does(context =>
+{
+ var apiKey = Argument("nuget-key", null);
+ if(string.IsNullOrWhiteSpace(apiKey)) {
+ throw new CakeException("No NuGet API key was provided.");
+ }
+
+ // Publish to GitHub Packages
+ foreach(var file in context.GetFiles("./dist/NSExt/bin/Release/*.*"))
+ {
+ context.Information("Publishing {0}...", file.GetFilename().FullPath);
+ DotNetNuGetPush(file.FullPath, new DotNetNuGetPushSettings
+ {
+ Source = "https://api.nuget.org/v3/index.json",
+ ApiKey = apiKey,
+ SkipDuplicate = true
+ });
+ }
+});
+
+////////////////////////////////////////////////////////////////
+// Targets
+
+Task("Publish")
+// .IsDependentOn("Publish-GitHub")
+ .IsDependentOn("Publish-NuGet");
+
+Task("Default")
+ .IsDependentOn("Build");
+
+////////////////////////////////////////////////////////////////
+// Execution
+
+RunTarget(target)
\ No newline at end of file
diff --git a/code-format.cmd b/code-format.cmd
deleted file mode 100644
index 51fa0a1..0000000
--- a/code-format.cmd
+++ /dev/null
@@ -1,3 +0,0 @@
-do.exe trim-utf8-bom
-do.exe remove-whitespace
-do.exe convert-lf
\ No newline at end of file
diff --git a/code.cleanup.full.ps1 b/code.cleanup.full.ps1
new file mode 100644
index 0000000..5b5c370
--- /dev/null
+++ b/code.cleanup.full.ps1
@@ -0,0 +1 @@
+dotnet jb cleanupcode --no-build ./NSExt.sln
\ No newline at end of file
diff --git a/code.quality.props b/code.quality.props
new file mode 100644
index 0000000..7e4518a
--- /dev/null
+++ b/code.quality.props
@@ -0,0 +1,43 @@
+
+
+ $(SolutionDir)/stylecop.analyzers.ruleset
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dot.clean.cmd b/dot.clean.cmd
new file mode 100644
index 0000000..3eec6ef
--- /dev/null
+++ b/dot.clean.cmd
@@ -0,0 +1,3 @@
+dot rbom -w -e refs -e .git -e node_modules
+dot trim -w -e refs -e .git -e node_modules
+dot tolf -w -e refs -e .git -e node_modules
\ No newline at end of file
diff --git a/dotnet-tools.json b/dotnet-tools.json
new file mode 100644
index 0000000..d2c1b28
--- /dev/null
+++ b/dotnet-tools.json
@@ -0,0 +1,24 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "cake.tool": {
+ "version": "3.1.0",
+ "commands": [
+ "dotnet-cake"
+ ]
+ },
+ "dotnet-script": {
+ "version": "1.4.0",
+ "commands": [
+ "dotnet-script"
+ ]
+ },
+ "jetbrains.resharper.globaltools": {
+ "version": "2023.2.0",
+ "commands": [
+ "jb"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/git-clean.ps1 b/git-clean.ps1
deleted file mode 100644
index 5f7b39a..0000000
--- a/git-clean.ps1
+++ /dev/null
@@ -1 +0,0 @@
-git reset --hard | git clean -d -fx .
\ No newline at end of file
diff --git a/git.pr.ps1 b/git.pr.ps1
new file mode 100644
index 0000000..2b68a89
--- /dev/null
+++ b/git.pr.ps1
@@ -0,0 +1,26 @@
+$types = @{
+ '1' = @('FEA', '新增特性')
+ '2' = @('REF', '项目重构')
+ '3' = @('FIX', '缺陷修复')
+ '4' = @('PER', '性能优化')
+ '5' = @('RVT', '还原变更')
+ '6' = @('FMT', '格式整理')
+ '7' = @('DOC', '文档变更')
+ '8' = @('TST', '单元测试')
+ '9' = @('BLD', '工程构建')
+}
+git add ./
+$prefix = ''
+while ($null -eq $types[$prefix])
+{
+ $prefix = Read-Host "请选择提交类型`n" $( & { param($i) $i | ForEach-Object { "$_ : $( $types[$_][0] )($( $types[$_][1] ))`n" } } $types.Keys | Sort-Object )
+}
+git commit -m "[$($types[$prefix][0])] $(($(Read-Host '是否跳过自动构建?(Y/n)') -eq 'n') ? '': '[SKIP CI] ')$(Read-Host '请输入提交消息')"
+$branch = $(git branch --show-current)
+& './dot.clean.cmd'
+git add ./
+git commit --amend --no-edit
+git pull
+git push --set-upstream origin $branch
+Start-Process -FilePath "https://github.com/nsnail/NSExt/compare/main...$branch"
+Pause
\ No newline at end of file
diff --git a/git.rc.ps1 b/git.rc.ps1
new file mode 100644
index 0000000..b72db93
--- /dev/null
+++ b/git.rc.ps1
@@ -0,0 +1,6 @@
+$branch = $(git branch --show-current)
+git checkout main
+git pull
+git branch -D $branch
+git branch $branch
+git checkout $branch
\ No newline at end of file
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..8947643
--- /dev/null
+++ b/global.json
@@ -0,0 +1,10 @@
+{
+ "sdk": {
+ "version": "7.0.0",
+ "rollForward": "latestMajor",
+ "allowPrerelease": true
+ },
+ "tools": {
+ "dotnet": "7.0.0"
+ }
+}
\ No newline at end of file
diff --git a/image.optimize.csx b/image.optimize.csx
new file mode 100644
index 0000000..e43a4d4
--- /dev/null
+++ b/image.optimize.csx
@@ -0,0 +1,65 @@
+/*
+ for %%i in (*.png) do pngquant %%i --force --output %%i --skip-if-larger
+ for %%i in (*.jpg) do jpegtran -copy none -optimize -perfect %%i %%i
+ *
+ */
+
+
+var files = Directory
+ .EnumerateFiles(
+ "./",
+ "*.png",
+ new EnumerationOptions
+ {
+ RecurseSubdirectories = true,
+ AttributesToSkip = FileAttributes.ReparsePoint,
+ IgnoreInaccessible = true
+ }
+ )
+ .ToArray();
+
+Parallel.ForEach(
+ files,
+ file =>
+ {
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = "pngquant",
+ Arguments = $"\"{file}\" --force --output \"{file}\" --skip-if-larger"
+ };
+ using var p = Process.Start(startInfo);
+ p.WaitForExit();
+ Console.WriteLine($"{file}: {p.ExitCode}");
+ }
+);
+
+files = new[] { "*.jpg", "*.jpeg" }
+ .SelectMany(
+ x =>
+ Directory.EnumerateFiles(
+ "./",
+ x,
+ new EnumerationOptions
+ {
+ RecurseSubdirectories = true,
+ AttributesToSkip = FileAttributes.ReparsePoint,
+ IgnoreInaccessible = true
+ }
+ )
+ )
+ .ToArray();
+
+Parallel.ForEach(
+ files,
+ file =>
+ {
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = "jpegtran",
+ Arguments = $"-copy none -optimize -perfect \"{file}\" \"{file}\""
+ };
+ using var p = Process.Start(startInfo);
+ p.WaitForExit();
+ Console.WriteLine($"{file}: {p.ExitCode}");
+ }
+);
\ No newline at end of file
diff --git a/key.snk b/key.snk
new file mode 100644
index 0000000..aac3fbe
Binary files /dev/null and b/key.snk differ
diff --git a/logo.png b/logo.png
new file mode 100644
index 0000000..77638c1
Binary files /dev/null and b/logo.png differ
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 0000000..eb8bbca
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Directory.Build.props b/packable.props
similarity index 53%
rename from src/Directory.Build.props
rename to packable.props
index 2a19419..ef69428 100644
--- a/src/Directory.Build.props
+++ b/packable.props
@@ -1,18 +1,18 @@
- nsnail
- NSExt
- © 2006-2022 nsnail
- https://github.com/nsnail/ns-ext.git
- true
- true
+ ../../key.snk
+ false
true
- Git
+ true
true
true
- snupkg
+ true
+ logo.png
MIT
- https://github.com/nsnail/ns-ext.git
- ../../build/nupkgs
+ https://github.com/nsnail/NSExt.git
+ extensions
+ true
+ true
+ snupkg
\ No newline at end of file
diff --git a/push2nuget.ps1 b/push2nuget.ps1
deleted file mode 100644
index 3e911c8..0000000
--- a/push2nuget.ps1
+++ /dev/null
@@ -1,25 +0,0 @@
-Param(
- # Nuget APIKey
- [string] $apikey
-)
-
-if ($apikey -eq $null -or $apikey -eq "")
-{
- Write-Error "require apiKey";
- return;
-}
-
-rm -r ./build/nupkgs
-dotnet build -c Release ./src/NSExt.sln
-$files = Get-ChildItem -Path ./build/nupkgs/ -Filter *.nupkg
-foreach($file in $files)
-{
- dotnet nuget push $file.fullName --skip-duplicate --api-key $apikey --source https://api.nuget.org/v3/index.json
- nuget add $file.fullName -source d:\nuget-pkg
-}
-$files = Get-ChildItem -Path ./build/nupkgs/ -Filter *.snupkg
-foreach($file in $files)
-{
- dotnet nuget push $file.fullName --skip-duplicate --api-key $apikey --source https://api.nuget.org/v3/index.json
- nuget add $file.fullName -source d:\nuget-pkg
-}
\ No newline at end of file
diff --git a/src/.editorconfig b/src/.editorconfig
deleted file mode 100644
index bf1a983..0000000
--- a/src/.editorconfig
+++ /dev/null
@@ -1,89 +0,0 @@
-[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,config,cs,cshtml,csproj,css,dbml,discomap,dtd,htm,html,js,json,jsproj,jsx,lsproj,master,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,ts,tsx,vb,vbproj,xaml,xamlx,xml,xoml,xsd}]
-indent_style = space
-indent_size = space
-tab_width = 4
-
-[*]
-
-# Microsoft .NET properties
-csharp_indent_braces = false
-csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_open_brace = local_functions, methods, types
-csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
-csharp_style_var_elsewhere = true:suggestion
-csharp_style_var_for_built_in_types = true:suggestion
-csharp_style_var_when_type_is_apparent = true:suggestion
-dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
-dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
-dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
-dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
-dotnet_style_predefined_type_for_member_access = true:suggestion
-dotnet_style_qualification_for_event = false:suggestion
-dotnet_style_qualification_for_field = false:suggestion
-dotnet_style_qualification_for_method = false:suggestion
-dotnet_style_qualification_for_property = false:suggestion
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
-
-# ReSharper inspection severities
-resharper_arrange_redundant_parentheses_highlighting = hint
-resharper_arrange_this_qualifier_highlighting = hint
-resharper_arrange_type_member_modifiers_highlighting = hint
-resharper_arrange_type_modifiers_highlighting = hint
-resharper_built_in_type_reference_style_for_member_access_highlighting = hint
-resharper_built_in_type_reference_style_highlighting = hint
-resharper_redundant_base_qualifier_highlighting = warning
-resharper_suggest_var_or_type_built_in_types_highlighting = hint
-resharper_suggest_var_or_type_elsewhere_highlighting = hint
-resharper_suggest_var_or_type_simple_types_highlighting = hint
-
-# ReSharper properties
-resharper_align_first_arg_by_paren = true
-resharper_align_linq_query = true
-resharper_align_multiline_argument = true
-resharper_align_multiline_array_and_object_initializer = false
-resharper_align_multiline_calls_chain = true
-resharper_align_multiline_expression = true
-resharper_align_multiline_extends_list = true
-resharper_align_multiline_for_stmt = true
-resharper_align_multiline_property_pattern = true
-resharper_align_multiline_switch_expression = true
-resharper_align_multline_type_parameter_constrains = true
-resharper_align_multline_type_parameter_list = true
-resharper_align_tuple_components = true
-resharper_csharp_align_multiline_parameter = true
-resharper_csharp_align_multiple_declaration = true
-resharper_csharp_empty_block_style = together
-resharper_csharp_indent_size = 4
-resharper_csharp_indent_style = space
-resharper_csharp_insert_final_newline = true
-resharper_csharp_max_line_length = 120
-resharper_csharp_stick_comment = false
-resharper_csharp_tab_width = 4
-resharper_csharp_wrap_arguments_style = chop_if_long
-resharper_csharp_wrap_before_comma = false
-resharper_csharp_wrap_chained_method_calls = chop_if_long
-resharper_csharp_wrap_extends_list_style = chop_if_long
-resharper_csharp_wrap_parameters_style = chop_if_long
-resharper_indent_anonymous_method_block = true
-resharper_indent_nested_fixed_stmt = true
-resharper_indent_nested_foreach_stmt = true
-resharper_indent_nested_for_stmt = true
-resharper_indent_nested_lock_stmt = true
-resharper_indent_nested_usings_stmt = true
-resharper_indent_nested_while_stmt = true
-resharper_indent_preprocessor_if = usual_indent
-resharper_indent_preprocessor_other = usual_indent
-resharper_int_align = true
-resharper_keep_existing_arrangement = false
-resharper_keep_existing_linebreaks = false
-resharper_max_initializer_elements_on_line = 1
-resharper_outdent_commas = true
-resharper_outdent_dots = true
-resharper_wrap_array_initializer_style = chop_always
-resharper_wrap_before_comma = false
-resharper_wrap_object_and_collection_initializer_style = chop_always
-
-[*.cs]
-indent_style = space
-indent_size = 4
-tab_width = 4
\ No newline at end of file
diff --git a/src/NSExt.Tests/NSExt.Tests.csproj b/src/NSExt.Tests/NSExt.Tests.csproj
new file mode 100644
index 0000000..f770abf
--- /dev/null
+++ b/src/NSExt.Tests/NSExt.Tests.csproj
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/NSExt.Tests/TestCase.cs b/src/NSExt.Tests/TestCase.cs
new file mode 100644
index 0000000..3164751
--- /dev/null
+++ b/src/NSExt.Tests/TestCase.cs
@@ -0,0 +1,36 @@
+namespace NSExt.Tests;
+
+///
+/// 测试用例
+///
+public class TestCase
+{
+ // private readonly ITestOutputHelper _testOutputHelper;
+ //
+ // public TestCase(ITestOutputHelper testOutputHelper)
+ // {
+ // _testOutputHelper = testOutputHelper;
+ // }
+ //
+ // public enum MyEnum1
+ // {
+ // [ResourceDescription(nameof(Description))]
+ // Online = 1
+ //
+ // , Offline = 2
+ // }
+ //
+ // public static string Description { get; set; } = "123";
+ //
+ // ///
+ // /// Case1
+ // ///
+ // [Fact]
+ // public void Case1()
+ // {
+ // var test = MyEnum1.Online.ResDesc();
+ //
+ // _testOutputHelper.WriteLine(test);
+ // Assert.True(test is not null);
+ // }
+}
\ No newline at end of file
diff --git a/src/NSExt.sln b/src/NSExt.sln
deleted file mode 100644
index fcf94ce..0000000
--- a/src/NSExt.sln
+++ /dev/null
@@ -1,16 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSExt", "NSExt\NSExt.csproj", "{54814D1B-C587-4D90-A7BE-4BE1E170D5FC}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {54814D1B-C587-4D90-A7BE-4BE1E170D5FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {54814D1B-C587-4D90-A7BE-4BE1E170D5FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {54814D1B-C587-4D90-A7BE-4BE1E170D5FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {54814D1B-C587-4D90-A7BE-4BE1E170D5FC}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
\ No newline at end of file
diff --git a/src/NSExt.sln.DotSettings b/src/NSExt.sln.DotSettings
deleted file mode 100644
index f7cba85..0000000
--- a/src/NSExt.sln.DotSettings
+++ /dev/null
@@ -1,45 +0,0 @@
-
- <?xml version="1.0" encoding="utf-16"?>
-<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
- <TypePattern>
- <Entry>
- <Entry.SortBy>
- <Kind>
- <Kind.Order>
- <DeclarationKind>Interface</DeclarationKind>
- <DeclarationKind>Class</DeclarationKind>
- <DeclarationKind>Enum</DeclarationKind>
- <DeclarationKind>Struct</DeclarationKind>
- <DeclarationKind>Delegate</DeclarationKind>
- <DeclarationKind>Event</DeclarationKind>
- <DeclarationKind>Constant</DeclarationKind>
- <DeclarationKind>Field</DeclarationKind>
- <DeclarationKind>Property</DeclarationKind>
- <DeclarationKind>Constructor</DeclarationKind>
- <DeclarationKind>Destructor</DeclarationKind>
- <DeclarationKind>Indexer</DeclarationKind>
- <DeclarationKind>Method</DeclarationKind>
- </Kind.Order>
- </Kind>
- <Access>
- <Access.Order>
- <AccessModifier>Private</AccessModifier>
- <AccessModifier>PrivateProtected</AccessModifier>
- <AccessModifier>Protected</AccessModifier>
- <AccessModifier>ProtectedInternal</AccessModifier>
- <AccessModifier>Internal</AccessModifier>
- <AccessModifier>Public</AccessModifier>
- </Access.Order>
- </Access>
- <Name />
- </Entry.SortBy>
- </Entry>
- </TypePattern>
-</Patterns>
- <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
- <Policy Inspect="True" Prefix="_" Suffix="" Style="AA_BB" />
- <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
- <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
- False
- HINT
-
\ No newline at end of file
diff --git a/src/NSExt/Attributes/LocalizationAttribute.cs b/src/NSExt/Attributes/LocalizationAttribute.cs
new file mode 100644
index 0000000..9b53058
--- /dev/null
+++ b/src/NSExt/Attributes/LocalizationAttribute.cs
@@ -0,0 +1,24 @@
+namespace NSExt.Attributes;
+
+///
+/// 指定本地化资源类型
+///
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Field)]
+public sealed class LocalizationAttribute : Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LocalizationAttribute(Type resourceClass)
+ {
+ ResourceClass = resourceClass;
+ }
+
+ ///
+ /// Gets or sets 资源类型
+ ///
+ ///
+ /// 资源类型
+ ///
+ public Type ResourceClass { get; set; }
+}
\ No newline at end of file
diff --git a/src/NSExt/Attributes/ResourceDescriptionAttribute.cs b/src/NSExt/Attributes/ResourceDescriptionAttribute.cs
new file mode 100644
index 0000000..f124234
--- /dev/null
+++ b/src/NSExt/Attributes/ResourceDescriptionAttribute.cs
@@ -0,0 +1,26 @@
+namespace NSExt.Attributes;
+
+///
+/// 本地化资源描述特性
+///
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Field)]
+public sealed class ResourceDescriptionAttribute : Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ResourceDescriptionAttribute(string resourceName)
+ {
+ ResourceName = resourceName;
+ }
+
+ ///
+ /// 资源名称
+ ///
+ public string ResourceName { get; set; }
+
+ ///
+ /// 资源对象
+ ///
+ public T ResourceObject { get; set; }
+}
\ No newline at end of file
diff --git a/src/NSExt/Constant/Regexes.cs b/src/NSExt/Constant/Regexes.cs
new file mode 100644
index 0000000..873927a
--- /dev/null
+++ b/src/NSExt/Constant/Regexes.cs
@@ -0,0 +1,21 @@
+namespace NSExt.Constant;
+#pragma warning disable SYSLIB1045
+
+///
+/// 使用 RegexGenerator 新特性会生成重复key值的xmlComment导致出错
+///
+internal static class Regexes
+{
+ public static readonly Regex RegexBacksLantUnicode
+ = new(@"\\u([a-fA-F0-9]{4})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ public static readonly Regex RegexHtmlTag = new("<[^>]*>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ public static readonly Regex RegexMobile
+ = new(@"^(\d{3})\d{4}(\d{4})$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ public static readonly Regex RegexPercentUnicode
+ = new(@"\\u([a-fA-F0-9]{4})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ public static readonly Regex RegexUpLetter = new("([A-Z])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+}
\ No newline at end of file
diff --git a/src/NSExt/Extensions/ByteExtensions.cs b/src/NSExt/Extensions/ByteExtensions.cs
index b443098..5c810e0 100644
--- a/src/NSExt/Extensions/ByteExtensions.cs
+++ b/src/NSExt/Extensions/ByteExtensions.cs
@@ -1,22 +1,24 @@
namespace NSExt.Extensions;
+///
+/// ByteExtensions
+///
public static class ByteExtensions
{
///
/// base64编码
///
- /// 待编码的字节数组
+ /// me
/// 编码后的base64字符串
public static string Base64(this byte[] me)
{
return Convert.ToBase64String(me);
}
-
///
/// 将字节数组解码成字符串
///
- /// 字节数组
+ /// me
/// 字符串使用的编码方式
/// 解码后的原始字符串
public static string HexDe(this byte[] me, Encoding e)
@@ -24,11 +26,10 @@ public static class ByteExtensions
return e.GetString(me);
}
-
///
/// 将字节数组解码成字符串
///
- /// 字节数组
+ /// me
/// 解码后的原始字符串
public static string HexDe(this byte[] me)
{
@@ -38,15 +39,23 @@ public static class ByteExtensions
///
/// 将字节数组转换成16进制字符串
///
- ///
+ /// me
/// 是否大写
/// 字节间分隔符
- ///
- public static string String(this byte[] me, bool upperCase = true, string splitShar = null)
+ /// 分隔符跳跃字节数
+ public static string Str(this IEnumerable me, bool upperCase = true, string splitShar = ""
+ , int splitInterval = 1)
{
- var ret = BitConverter.ToString(me);
- if (!upperCase) ret = ret.ToLower();
- if (splitShar != "-") ret = ret.Replace("-", splitShar ?? string.Empty);
- return ret;
+ var sb = new StringBuilder();
+ var i = 0;
+ foreach (var c in me.Select(x => x.ToString(upperCase ? "X2" : "x2", CultureInfo.InvariantCulture))) {
+ if (i++ % splitInterval == 0) {
+ _ = sb.Append(splitShar);
+ }
+
+ _ = sb.Append(c);
+ }
+
+ return sb.ToString();
}
}
\ No newline at end of file
diff --git a/src/NSExt/Extensions/CharExtensions.cs b/src/NSExt/Extensions/CharExtensions.cs
index 319cf91..3d5438a 100644
--- a/src/NSExt/Extensions/CharExtensions.cs
+++ b/src/NSExt/Extensions/CharExtensions.cs
@@ -1,12 +1,13 @@
namespace NSExt.Extensions;
+///
+/// CharExtensions
+///
public static class CharExtensions
{
///
/// 是否数字或大小写字母
///
- ///
- ///
public static bool IsAsciiLetterOrDigit(this char me)
{
return (((uint)me - 'A') & ~0x20) < 26 || (uint)me - '0' < 10;
@@ -15,8 +16,6 @@ public static class CharExtensions
///
/// 是否base64字符
///
- ///
- ///
public static bool IsBase64Character(this char me)
{
return IsAsciiLetterOrDigit(me) || me is '+' or '/' or '=';
diff --git a/src/NSExt/Extensions/DateTimeExtensions.cs b/src/NSExt/Extensions/DateTimeExtensions.cs
index 2e904b2..1d486e8 100644
--- a/src/NSExt/Extensions/DateTimeExtensions.cs
+++ b/src/NSExt/Extensions/DateTimeExtensions.cs
@@ -1,31 +1,18 @@
+// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Global
+#pragma warning disable SA1300, IDE1006
namespace NSExt.Extensions;
+///
+/// DateTimeExtensions
+///
public static class DateTimeExtensions
{
- ///
- /// 将一个过去时间对象与当前时间相减转换成“xx以前”的字符串,如2秒以前,3天以前
- ///
- /// 时间对象
- /// 字符串
- public static string TimeAgo(this DateTime me)
- {
- var ts = DateTime.Now - me;
- if (ts.Days > 0) return ts.Days + "天前";
-
- if (ts.Hours > 0) return ts.Hours + "小时前";
-
- if (ts.Minutes > 0) return ts.Minutes + "分钟前";
-
- return ts.Seconds + "秒前";
- }
-
-
///
/// 指定时间的世界协调时的unix时间戳形式
///
- /// 指定时间
+ /// me
/// unix时间戳
public static long TimeUnixUtc(this DateTime me)
{
@@ -35,55 +22,89 @@ public static class DateTimeExtensions
///
/// 指定时间的世界协调时的unix时间戳形式(毫秒)
///
- ///
- ///
public static long TimeUnixUtcMs(this DateTime me)
{
return (me.ToUniversalTime().Ticks - 621355968000000000) / 10000;
}
+ ///
+ /// ToString 的 Invariant 版本
+ ///
+ public static string ToInvString(this DateTime me)
+ {
+ return me.ToString(CultureInfo.InvariantCulture);
+ }
- // ReSharper disable once InconsistentNaming
+ ///
+ /// 将一个过去时间对象与当前时间相减转换成“xx以前”的字符串, 如2秒以前, 3天以前
+ ///
+ /// me
+ /// 字符串
+ public static string UtcTimeAgo(this DateTime me)
+ {
+ var ts = DateTime.UtcNow - me;
+ return ts.Days switch {
+ > 0 => ts.Days + "天前"
+ , _ => ts.Hours switch {
+ > 0 => ts.Hours + "小时前"
+ , _ => ts.Minutes switch { > 0 => ts.Minutes + "分钟前", _ => ts.Seconds + "秒前" }
+ }
+ };
+ }
+
+ ///
+ /// yyyy_MM
+ ///
public static string yyyy_MM(this DateTime me)
{
- return me.ToString("yyyy-MM");
+ return me.ToString("yyyy-MM", CultureInfo.InvariantCulture);
}
- // ReSharper disable once InconsistentNaming
+ ///
+ /// yyyy_MM_dd
+ ///
public static string yyyy_MM_dd(this DateTime me)
{
- return me.ToString("yyyy-MM-dd");
+ return me.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}
- // ReSharper disable once InconsistentNaming
+ ///
+ /// yyyy_MM_dd_HH_mm
+ ///
public static string yyyy_MM_dd_HH_mm(this DateTime me)
{
- return me.ToString("yyyy-MM-dd HH:mm");
+ return me.ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
}
-
- // ReSharper disable once InconsistentNaming
+ ///
+ /// yyyy_MM_dd_HH_mm_ss
+ ///
public static string yyyy_MM_dd_HH_mm_ss(this DateTime me)
{
- return me.ToString("yyyy-MM-dd HH:mm:ss");
+ return me.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
}
- // ReSharper disable once InconsistentNaming
+ ///
+ /// yyyy_MM_dd_HH_mm_ss_fff
+ ///
public static string yyyy_MM_dd_HH_mm_ss_fff(this DateTime me)
{
- return me.ToString("yyyy-MM-dd HH:mm:ss.fff");
+ return me.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
}
-
- // ReSharper disable once InconsistentNaming
+ ///
+ /// yyyyMM
+ ///
public static string yyyyMM(this DateTime me)
{
- return me.ToString("yyyyMM");
+ return me.ToString("yyyyMM", CultureInfo.InvariantCulture);
}
- // ReSharper disable once InconsistentNaming
+ ///
+ /// yyyyMMdd
+ ///
public static string yyyyMMdd(this DateTime me)
{
- return me.ToString("yyyyMMdd");
+ return me.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
}
}
\ No newline at end of file
diff --git a/src/NSExt/Extensions/DbCommandExtensions.cs b/src/NSExt/Extensions/DbCommandExtensions.cs
index c7cc4c8..62b4521 100644
--- a/src/NSExt/Extensions/DbCommandExtensions.cs
+++ b/src/NSExt/Extensions/DbCommandExtensions.cs
@@ -1,28 +1,35 @@
namespace NSExt.Extensions;
+///
+/// DbCommandExtensions
+///
public static class DbCommandExtensions
{
///
/// 格式化参数拼接成完整的SQL语句
///
- ///
public static string ParameterFormat(this DbCommand me)
{
- //var aa = pars.ToDictionary(it => it.ParameterName, it => it.Value);
var sql = me.CommandText;
- //应逆向替换,否则由于 多个表的过滤器问题导致替换不完整 如 @TenantId1 @TenantId10
- for (var i = me.Parameters.Count - 1; i >= 0; i--)
+ // 应逆向替换,否则由于 多个表的过滤器问题导致替换不完整 如 @TenantId1 @TenantId10
+ for (var i = me.Parameters.Count - 1; i >= 0; i--) {
+ #pragma warning disable IDE0072
sql = me.Parameters[i].DbType switch {
+ #pragma warning restore IDE0072
DbType.String or DbType.DateTime or DbType.Date or DbType.Time or DbType.DateTime2
or DbType.DateTimeOffset or DbType.Guid or DbType.VarNumeric or DbType.AnsiStringFixedLength
- or DbType.AnsiString
- or DbType.StringFixedLength => sql.Replace(me.Parameters[i].ParameterName,
- "'" + me.Parameters[i].Value + "'"),
- DbType.Boolean => sql.Replace(me.Parameters[i].ParameterName,
- Convert.ToBoolean(me.Parameters[i].Value) ? "1" : "0"),
- _ => sql.Replace(me.Parameters[i].ParameterName, me.Parameters[i].Value?.ToString())
+ or DbType.AnsiString or DbType.StringFixedLength => sql.Replace( //
+ me.Parameters[i].ParameterName, "'" + me.Parameters[i].Value + "'")
+ , DbType.Boolean => sql.Replace( //
+ me.Parameters[i].ParameterName
+ , me.Parameters[i].Value != DBNull.Value &&
+ Convert.ToBoolean(me.Parameters[i].Value, CultureInfo.InvariantCulture)
+ ? "1"
+ : "0")
+ , _ => sql.Replace(me.Parameters[i].ParameterName, me.Parameters[i].Value?.ToString())
};
+ }
return sql;
}
diff --git a/src/NSExt/Extensions/DecimalExtensions.cs b/src/NSExt/Extensions/DecimalExtensions.cs
index 3695204..7f6f46e 100644
--- a/src/NSExt/Extensions/DecimalExtensions.cs
+++ b/src/NSExt/Extensions/DecimalExtensions.cs
@@ -1,16 +1,26 @@
namespace NSExt.Extensions;
+///
+/// DecimalExtensions
+///
public static class DecimalExtensions
{
///
/// 四舍五入后的近似值
///
- /// 数字
+ /// me
/// 小数点位数
/// 处理后的值
public static decimal Round(this decimal me, int place)
{
- var dec = Math.Round(me, place);
- return dec;
+ return Math.Round(me, place);
+ }
+
+ ///
+ /// ToString 的 Invariant 版本
+ ///
+ public static string ToInvString(this decimal me)
+ {
+ return me.ToString(CultureInfo.InvariantCulture);
}
}
\ No newline at end of file
diff --git a/src/NSExt/Extensions/EnumExtensions.cs b/src/NSExt/Extensions/EnumExtensions.cs
index a7e5827..b40f675 100644
--- a/src/NSExt/Extensions/EnumExtensions.cs
+++ b/src/NSExt/Extensions/EnumExtensions.cs
@@ -1,17 +1,41 @@
+using System.ComponentModel.DataAnnotations;
+using System.Reflection;
+using NSExt.Attributes;
+
namespace NSExt.Extensions;
+///
+/// EnumExtensions
+///
public static class EnumExtensions
{
///
- /// 获取枚举的description属性
+ /// 获取显示特性
///
- /// 枚举对象
- /// description属性
- public static string Desc(this Enum e)
+ public static DisplayAttribute GetDisplay(this Enum me)
{
- var t = e.GetType();
- var fi = t.GetField(Enum.GetName(t, e)!);
- var attrs = (DescriptionAttribute[])fi!.GetCustomAttributes(typeof(DescriptionAttribute), false);
- return (attrs.Length != 0 ? attrs[0].Description : Enum.GetName(t, e)) ?? "";
+ return me.GetAttributeOfType();
+ }
+
+ ///
+ /// 获取枚举的本地化资源描述
+ ///
+ public static string ResDesc(this Enum e)
+ {
+ var typeOfEnum = e.GetType();
+ var typeOfField = typeOfEnum.GetField(Enum.GetName(typeOfEnum, e)!);
+ var resDescAttr = typeOfField!.GetCustomAttribute>(true);
+ return resDescAttr is null
+ ? Enum.GetName(typeOfEnum, e)
+ : typeof(T).GetProperty(resDescAttr.ResourceName)?.GetValue(default) as string;
+ }
+
+ ///
+ /// 通过类泛型类型获取特性
+ ///
+ private static T GetAttributeOfType(this Enum me)
+ where T : Attribute
+ {
+ return me.GetType().GetMember(me.ToString())[0].GetCustomAttributes(false).FirstOrDefault();
}
}
\ No newline at end of file
diff --git a/src/NSExt/Extensions/EnumerableExtensions.cs b/src/NSExt/Extensions/EnumerableExtensions.cs
index 067f057..08eef9f 100644
--- a/src/NSExt/Extensions/EnumerableExtensions.cs
+++ b/src/NSExt/Extensions/EnumerableExtensions.cs
@@ -1,13 +1,13 @@
namespace NSExt.Extensions;
+///
+/// EnumerableExtensions
+///
public static class EnumerableExtensions
{
///
/// 将列表转成分隔符分隔的字符串
///
- ///
- ///
- ///
public static string Join(this IEnumerable