From bb8f7fa69c24725de0374be4e561c3a46992c30c Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Thu, 8 Jan 2026 15:33:03 +0800 Subject: [PATCH 01/14] =?UTF-8?q?chore:=20=E9=87=8D=E6=9E=84=E5=8C=85?= =?UTF-8?q?=E5=90=8D=E5=B9=B6=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC=E5=8F=B7?= =?UTF-8?q?=E8=87=B32026.0108.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重命名所有 NuGet 包为 Joce.EasyTool.* 前缀 - EasyTool.Core → Joce.EasyTool.Core - EasyTool.EmitMapper → Joce.EasyTool.EmitMapper - EasyTool.Web → Joce.EasyTool.Web - EasyTool.NPOI → Joce.EasyTool.NPOI - EasyTool.Image → Joce.EasyTool.Image - 更新所有项目版本号至 2026.0108.1 - 完善 NuGet 打包配置 - 为 EasyTool.Web 和 EasyTool.NPOI 添加完整的包元数据 - 统一所有项目的 PackageId、Authors、RepositoryUrl 等配置 --- .spec-workflow/templates/design-template.md | 96 ++++++++++++ .spec-workflow/templates/product-template.md | 51 ++++++ .../templates/requirements-template.md | 50 ++++++ .../templates/structure-template.md | 145 ++++++++++++++++++ .spec-workflow/templates/tasks-template.md | 139 +++++++++++++++++ .spec-workflow/templates/tech-template.md | 99 ++++++++++++ .spec-workflow/user-templates/README.md | 64 ++++++++ .../DictionaryExtension.cs | 4 +- EasyTool.Core/EasyTool.Core.csproj | 21 ++- EasyTool.Core/IOCategory/FileTypeUtil.cs | 11 +- EasyTool.Core/IOCategory/FileUtil.cs | 30 ++-- EasyTool.Core/IOCategory/WatchMonitor.cs | 6 +- EasyTool.Core/LanguageCategory/TreeUtil.cs | 15 +- EasyTool.Core/NetCategory/NetUtil.cs | 27 ++-- EasyTool.Core/ToolCategory/EnumUtil.cs | 14 +- EasyTool.Core/ToolCategory/IdcardUtil.cs | 4 +- EasyTool.Core/ToolCategory/MEFUtil.cs | 4 +- EasyTool.Core/ToolCategory/ObjectUtil.cs | 42 ++--- EasyTool.Core/ToolCategory/RuntimeUtil.cs | 20 ++- EasyTool.Core/ToolCategory/XmlUtil.cs | 4 +- EasyTool.CoreTests/EasyTool.CoreTests.csproj | 15 +- .../ToolCategory/IpUtilTests.cs | 20 ++- .../EasyTool.EmitMapper.csproj | 9 +- .../EasyTool.EmitMapperTests.csproj | 14 +- EasyTool.Image/EasyTool.Image.csproj | 11 +- .../EasyTool.ImageTests.csproj | 12 +- EasyTool.NPOI/EasyTool.NPOI.csproj | 67 ++++---- EasyTool.NPOITests/EasyTool.NPOITests.csproj | 12 +- EasyTool.Web/EasyTool.Web.csproj | 40 ++++- EasyTool.WebTests/EasyTool.WebTests.csproj | 12 +- 30 files changed, 904 insertions(+), 154 deletions(-) create mode 100644 .spec-workflow/templates/design-template.md create mode 100644 .spec-workflow/templates/product-template.md create mode 100644 .spec-workflow/templates/requirements-template.md create mode 100644 .spec-workflow/templates/structure-template.md create mode 100644 .spec-workflow/templates/tasks-template.md create mode 100644 .spec-workflow/templates/tech-template.md create mode 100644 .spec-workflow/user-templates/README.md diff --git a/.spec-workflow/templates/design-template.md b/.spec-workflow/templates/design-template.md new file mode 100644 index 0000000..1295d7b --- /dev/null +++ b/.spec-workflow/templates/design-template.md @@ -0,0 +1,96 @@ +# Design Document + +## Overview + +[High-level description of the feature and its place in the overall system] + +## Steering Document Alignment + +### Technical Standards (tech.md) +[How the design follows documented technical patterns and standards] + +### Project Structure (structure.md) +[How the implementation will follow project organization conventions] + +## Code Reuse Analysis +[What existing code will be leveraged, extended, or integrated with this feature] + +### Existing Components to Leverage +- **[Component/Utility Name]**: [How it will be used] +- **[Service/Helper Name]**: [How it will be extended] + +### Integration Points +- **[Existing System/API]**: [How the new feature will integrate] +- **[Database/Storage]**: [How data will connect to existing schemas] + +## Architecture + +[Describe the overall architecture and design patterns used] + +### Modular Design Principles +- **Single File Responsibility**: Each file should handle one specific concern or domain +- **Component Isolation**: Create small, focused components rather than large monolithic files +- **Service Layer Separation**: Separate data access, business logic, and presentation layers +- **Utility Modularity**: Break utilities into focused, single-purpose modules + +```mermaid +graph TD + A[Component A] --> B[Component B] + B --> C[Component C] +``` + +## Components and Interfaces + +### Component 1 +- **Purpose:** [What this component does] +- **Interfaces:** [Public methods/APIs] +- **Dependencies:** [What it depends on] +- **Reuses:** [Existing components/utilities it builds upon] + +### Component 2 +- **Purpose:** [What this component does] +- **Interfaces:** [Public methods/APIs] +- **Dependencies:** [What it depends on] +- **Reuses:** [Existing components/utilities it builds upon] + +## Data Models + +### Model 1 +``` +[Define the structure of Model1 in your language] +- id: [unique identifier type] +- name: [string/text type] +- [Additional properties as needed] +``` + +### Model 2 +``` +[Define the structure of Model2 in your language] +- id: [unique identifier type] +- [Additional properties as needed] +``` + +## Error Handling + +### Error Scenarios +1. **Scenario 1:** [Description] + - **Handling:** [How to handle] + - **User Impact:** [What user sees] + +2. **Scenario 2:** [Description] + - **Handling:** [How to handle] + - **User Impact:** [What user sees] + +## Testing Strategy + +### Unit Testing +- [Unit testing approach] +- [Key components to test] + +### Integration Testing +- [Integration testing approach] +- [Key flows to test] + +### End-to-End Testing +- [E2E testing approach] +- [User scenarios to test] diff --git a/.spec-workflow/templates/product-template.md b/.spec-workflow/templates/product-template.md new file mode 100644 index 0000000..82e60de --- /dev/null +++ b/.spec-workflow/templates/product-template.md @@ -0,0 +1,51 @@ +# Product Overview + +## Product Purpose +[Describe the core purpose of this product/project. What problem does it solve?] + +## Target Users +[Who are the primary users of this product? What are their needs and pain points?] + +## Key Features +[List the main features that deliver value to users] + +1. **Feature 1**: [Description] +2. **Feature 2**: [Description] +3. **Feature 3**: [Description] + +## Business Objectives +[What are the business goals this product aims to achieve?] + +- [Objective 1] +- [Objective 2] +- [Objective 3] + +## Success Metrics +[How will we measure the success of this product?] + +- [Metric 1]: [Target] +- [Metric 2]: [Target] +- [Metric 3]: [Target] + +## Product Principles +[Core principles that guide product decisions] + +1. **[Principle 1]**: [Explanation] +2. **[Principle 2]**: [Explanation] +3. **[Principle 3]**: [Explanation] + +## Monitoring & Visibility (if applicable) +[How do users track progress and monitor the system?] + +- **Dashboard Type**: [e.g., Web-based, CLI, Desktop app] +- **Real-time Updates**: [e.g., WebSocket, polling, push notifications] +- **Key Metrics Displayed**: [What information is most important to surface] +- **Sharing Capabilities**: [e.g., read-only links, exports, reports] + +## Future Vision +[Where do we see this product evolving in the future?] + +### Potential Enhancements +- **Remote Access**: [e.g., Tunnel features for sharing dashboards with stakeholders] +- **Analytics**: [e.g., Historical trends, performance metrics] +- **Collaboration**: [e.g., Multi-user support, commenting] diff --git a/.spec-workflow/templates/requirements-template.md b/.spec-workflow/templates/requirements-template.md new file mode 100644 index 0000000..1c80ca0 --- /dev/null +++ b/.spec-workflow/templates/requirements-template.md @@ -0,0 +1,50 @@ +# Requirements Document + +## Introduction + +[Provide a brief overview of the feature, its purpose, and its value to users] + +## Alignment with Product Vision + +[Explain how this feature supports the goals outlined in product.md] + +## Requirements + +### Requirement 1 + +**User Story:** As a [role], I want [feature], so that [benefit] + +#### Acceptance Criteria + +1. WHEN [event] THEN [system] SHALL [response] +2. IF [precondition] THEN [system] SHALL [response] +3. WHEN [event] AND [condition] THEN [system] SHALL [response] + +### Requirement 2 + +**User Story:** As a [role], I want [feature], so that [benefit] + +#### Acceptance Criteria + +1. WHEN [event] THEN [system] SHALL [response] +2. IF [precondition] THEN [system] SHALL [response] + +## Non-Functional Requirements + +### Code Architecture and Modularity +- **Single Responsibility Principle**: Each file should have a single, well-defined purpose +- **Modular Design**: Components, utilities, and services should be isolated and reusable +- **Dependency Management**: Minimize interdependencies between modules +- **Clear Interfaces**: Define clean contracts between components and layers + +### Performance +- [Performance requirements] + +### Security +- [Security requirements] + +### Reliability +- [Reliability requirements] + +### Usability +- [Usability requirements] diff --git a/.spec-workflow/templates/structure-template.md b/.spec-workflow/templates/structure-template.md new file mode 100644 index 0000000..1ab1fbc --- /dev/null +++ b/.spec-workflow/templates/structure-template.md @@ -0,0 +1,145 @@ +# Project Structure + +## Directory Organization + +``` +[Define your project's directory structure. Examples below - adapt to your project type] + +Example for a library/package: +project-root/ +├── src/ # Source code +├── tests/ # Test files +├── docs/ # Documentation +├── examples/ # Usage examples +└── [build/dist/out] # Build output + +Example for an application: +project-root/ +├── [src/app/lib] # Main source code +├── [assets/resources] # Static resources +├── [config/settings] # Configuration +├── [scripts/tools] # Build/utility scripts +└── [tests/spec] # Test files + +Common patterns: +- Group by feature/module +- Group by layer (UI, business logic, data) +- Group by type (models, controllers, views) +- Flat structure for simple projects +``` + +## Naming Conventions + +### Files +- **Components/Modules**: [e.g., `PascalCase`, `snake_case`, `kebab-case`] +- **Services/Handlers**: [e.g., `UserService`, `user_service`, `user-service`] +- **Utilities/Helpers**: [e.g., `dateUtils`, `date_utils`, `date-utils`] +- **Tests**: [e.g., `[filename]_test`, `[filename].test`, `[filename]Test`] + +### Code +- **Classes/Types**: [e.g., `PascalCase`, `CamelCase`, `snake_case`] +- **Functions/Methods**: [e.g., `camelCase`, `snake_case`, `PascalCase`] +- **Constants**: [e.g., `UPPER_SNAKE_CASE`, `SCREAMING_CASE`, `PascalCase`] +- **Variables**: [e.g., `camelCase`, `snake_case`, `lowercase`] + +## Import Patterns + +### Import Order +1. External dependencies +2. Internal modules +3. Relative imports +4. Style imports + +### Module/Package Organization +``` +[Describe your project's import/include patterns] +Examples: +- Absolute imports from project root +- Relative imports within modules +- Package/namespace organization +- Dependency management approach +``` + +## Code Structure Patterns + +[Define common patterns for organizing code within files. Below are examples - choose what applies to your project] + +### Module/Class Organization +``` +Example patterns: +1. Imports/includes/dependencies +2. Constants and configuration +3. Type/interface definitions +4. Main implementation +5. Helper/utility functions +6. Exports/public API +``` + +### Function/Method Organization +``` +Example patterns: +- Input validation first +- Core logic in the middle +- Error handling throughout +- Clear return points +``` + +### File Organization Principles +``` +Choose what works for your project: +- One class/module per file +- Related functionality grouped together +- Public API at the top/bottom +- Implementation details hidden +``` + +## Code Organization Principles + +1. **Single Responsibility**: Each file should have one clear purpose +2. **Modularity**: Code should be organized into reusable modules +3. **Testability**: Structure code to be easily testable +4. **Consistency**: Follow patterns established in the codebase + +## Module Boundaries +[Define how different parts of your project interact and maintain separation of concerns] + +Examples of boundary patterns: +- **Core vs Plugins**: Core functionality vs extensible plugins +- **Public API vs Internal**: What's exposed vs implementation details +- **Platform-specific vs Cross-platform**: OS-specific code isolation +- **Stable vs Experimental**: Production code vs experimental features +- **Dependencies direction**: Which modules can depend on which + +## Code Size Guidelines +[Define your project's guidelines for file and function sizes] + +Suggested guidelines: +- **File size**: [Define maximum lines per file] +- **Function/Method size**: [Define maximum lines per function] +- **Class/Module complexity**: [Define complexity limits] +- **Nesting depth**: [Maximum nesting levels] + +## Dashboard/Monitoring Structure (if applicable) +[How dashboard or monitoring components are organized] + +### Example Structure: +``` +src/ +└── dashboard/ # Self-contained dashboard subsystem + ├── server/ # Backend server components + ├── client/ # Frontend assets + ├── shared/ # Shared types/utilities + └── public/ # Static assets +``` + +### Separation of Concerns +- Dashboard isolated from core business logic +- Own CLI entry point for independent operation +- Minimal dependencies on main application +- Can be disabled without affecting core functionality + +## Documentation Standards +- All public APIs must have documentation +- Complex logic should include inline comments +- README files for major modules +- Follow language-specific documentation conventions diff --git a/.spec-workflow/templates/tasks-template.md b/.spec-workflow/templates/tasks-template.md new file mode 100644 index 0000000..be461de --- /dev/null +++ b/.spec-workflow/templates/tasks-template.md @@ -0,0 +1,139 @@ +# Tasks Document + +- [ ] 1. Create core interfaces in src/types/feature.ts + - File: src/types/feature.ts + - Define TypeScript interfaces for feature data structures + - Extend existing base interfaces from base.ts + - Purpose: Establish type safety for feature implementation + - _Leverage: src/types/base.ts_ + - _Requirements: 1.1_ + - _Prompt: Role: TypeScript Developer specializing in type systems and interfaces | Task: Create comprehensive TypeScript interfaces for the feature data structures following requirements 1.1, extending existing base interfaces from src/types/base.ts | Restrictions: Do not modify existing base interfaces, maintain backward compatibility, follow project naming conventions | Success: All interfaces compile without errors, proper inheritance from base types, full type coverage for feature requirements_ + +- [ ] 2. Create base model class in src/models/FeatureModel.ts + - File: src/models/FeatureModel.ts + - Implement base model extending BaseModel class + - Add validation methods using existing validation utilities + - Purpose: Provide data layer foundation for feature + - _Leverage: src/models/BaseModel.ts, src/utils/validation.ts_ + - _Requirements: 2.1_ + - _Prompt: Role: Backend Developer with expertise in Node.js and data modeling | Task: Create a base model class extending BaseModel and implementing validation following requirement 2.1, leveraging existing patterns from src/models/BaseModel.ts and src/utils/validation.ts | Restrictions: Must follow existing model patterns, do not bypass validation utilities, maintain consistent error handling | Success: Model extends BaseModel correctly, validation methods implemented and tested, follows project architecture patterns_ + +- [ ] 3. Add specific model methods to FeatureModel.ts + - File: src/models/FeatureModel.ts (continue from task 2) + - Implement create, update, delete methods + - Add relationship handling for foreign keys + - Purpose: Complete model functionality for CRUD operations + - _Leverage: src/models/BaseModel.ts_ + - _Requirements: 2.2, 2.3_ + - _Prompt: Role: Backend Developer with expertise in ORM and database operations | Task: Implement CRUD methods and relationship handling in FeatureModel.ts following requirements 2.2 and 2.3, extending patterns from src/models/BaseModel.ts | Restrictions: Must maintain transaction integrity, follow existing relationship patterns, do not duplicate base model functionality | Success: All CRUD operations work correctly, relationships are properly handled, database operations are atomic and efficient_ + +- [ ] 4. Create model unit tests in tests/models/FeatureModel.test.ts + - File: tests/models/FeatureModel.test.ts + - Write tests for model validation and CRUD methods + - Use existing test utilities and fixtures + - Purpose: Ensure model reliability and catch regressions + - _Leverage: tests/helpers/testUtils.ts, tests/fixtures/data.ts_ + - _Requirements: 2.1, 2.2_ + - _Prompt: Role: QA Engineer with expertise in unit testing and Jest/Mocha frameworks | Task: Create comprehensive unit tests for FeatureModel validation and CRUD methods covering requirements 2.1 and 2.2, using existing test utilities from tests/helpers/testUtils.ts and fixtures from tests/fixtures/data.ts | Restrictions: Must test both success and failure scenarios, do not test external dependencies directly, maintain test isolation | Success: All model methods are tested with good coverage, edge cases covered, tests run independently and consistently_ + +- [ ] 5. Create service interface in src/services/IFeatureService.ts + - File: src/services/IFeatureService.ts + - Define service contract with method signatures + - Extend base service interface patterns + - Purpose: Establish service layer contract for dependency injection + - _Leverage: src/services/IBaseService.ts_ + - _Requirements: 3.1_ + - _Prompt: Role: Software Architect specializing in service-oriented architecture and TypeScript interfaces | Task: Design service interface contract following requirement 3.1, extending base service patterns from src/services/IBaseService.ts for dependency injection | Restrictions: Must maintain interface segregation principle, do not expose internal implementation details, ensure contract compatibility with DI container | Success: Interface is well-defined with clear method signatures, extends base service appropriately, supports all required service operations_ + +- [ ] 6. Implement feature service in src/services/FeatureService.ts + - File: src/services/FeatureService.ts + - Create concrete service implementation using FeatureModel + - Add error handling with existing error utilities + - Purpose: Provide business logic layer for feature operations + - _Leverage: src/services/BaseService.ts, src/utils/errorHandler.ts, src/models/FeatureModel.ts_ + - _Requirements: 3.2_ + - _Prompt: Role: Backend Developer with expertise in service layer architecture and business logic | Task: Implement concrete FeatureService following requirement 3.2, using FeatureModel and extending BaseService patterns with proper error handling from src/utils/errorHandler.ts | Restrictions: Must implement interface contract exactly, do not bypass model validation, maintain separation of concerns from data layer | Success: Service implements all interface methods correctly, robust error handling implemented, business logic is well-encapsulated and testable_ + +- [ ] 7. Add service dependency injection in src/utils/di.ts + - File: src/utils/di.ts (modify existing) + - Register FeatureService in dependency injection container + - Configure service lifetime and dependencies + - Purpose: Enable service injection throughout application + - _Leverage: existing DI configuration in src/utils/di.ts_ + - _Requirements: 3.1_ + - _Prompt: Role: DevOps Engineer with expertise in dependency injection and IoC containers | Task: Register FeatureService in DI container following requirement 3.1, configuring appropriate lifetime and dependencies using existing patterns from src/utils/di.ts | Restrictions: Must follow existing DI container patterns, do not create circular dependencies, maintain service resolution efficiency | Success: FeatureService is properly registered and resolvable, dependencies are correctly configured, service lifetime is appropriate for use case_ + +- [ ] 8. Create service unit tests in tests/services/FeatureService.test.ts + - File: tests/services/FeatureService.test.ts + - Write tests for service methods with mocked dependencies + - Test error handling scenarios + - Purpose: Ensure service reliability and proper error handling + - _Leverage: tests/helpers/testUtils.ts, tests/mocks/modelMocks.ts_ + - _Requirements: 3.2, 3.3_ + - _Prompt: Role: QA Engineer with expertise in service testing and mocking frameworks | Task: Create comprehensive unit tests for FeatureService methods covering requirements 3.2 and 3.3, using mocked dependencies from tests/mocks/modelMocks.ts and test utilities | Restrictions: Must mock all external dependencies, test business logic in isolation, do not test framework code | Success: All service methods tested with proper mocking, error scenarios covered, tests verify business logic correctness and error handling_ + +- [ ] 4. Create API endpoints + - Design API structure + - _Leverage: src/api/baseApi.ts, src/utils/apiUtils.ts_ + - _Requirements: 4.0_ + - _Prompt: Role: API Architect specializing in RESTful design and Express.js | Task: Design comprehensive API structure following requirement 4.0, leveraging existing patterns from src/api/baseApi.ts and utilities from src/utils/apiUtils.ts | Restrictions: Must follow REST conventions, maintain API versioning compatibility, do not expose internal data structures directly | Success: API structure is well-designed and documented, follows existing patterns, supports all required operations with proper HTTP methods and status codes_ + +- [ ] 4.1 Set up routing and middleware + - Configure application routes + - Add authentication middleware + - Set up error handling middleware + - _Leverage: src/middleware/auth.ts, src/middleware/errorHandler.ts_ + - _Requirements: 4.1_ + - _Prompt: Role: Backend Developer with expertise in Express.js middleware and routing | Task: Configure application routes and middleware following requirement 4.1, integrating authentication from src/middleware/auth.ts and error handling from src/middleware/errorHandler.ts | Restrictions: Must maintain middleware order, do not bypass security middleware, ensure proper error propagation | Success: Routes are properly configured with correct middleware chain, authentication works correctly, errors are handled gracefully throughout the request lifecycle_ + +- [ ] 4.2 Implement CRUD endpoints + - Create API endpoints + - Add request validation + - Write API integration tests + - _Leverage: src/controllers/BaseController.ts, src/utils/validation.ts_ + - _Requirements: 4.2, 4.3_ + - _Prompt: Role: Full-stack Developer with expertise in API development and validation | Task: Implement CRUD endpoints following requirements 4.2 and 4.3, extending BaseController patterns and using validation utilities from src/utils/validation.ts | Restrictions: Must validate all inputs, follow existing controller patterns, ensure proper HTTP status codes and responses | Success: All CRUD operations work correctly, request validation prevents invalid data, integration tests pass and cover all endpoints_ + +- [ ] 5. Add frontend components + - Plan component architecture + - _Leverage: src/components/BaseComponent.tsx, src/styles/theme.ts_ + - _Requirements: 5.0_ + - _Prompt: Role: Frontend Architect with expertise in React component design and architecture | Task: Plan comprehensive component architecture following requirement 5.0, leveraging base patterns from src/components/BaseComponent.tsx and theme system from src/styles/theme.ts | Restrictions: Must follow existing component patterns, maintain design system consistency, ensure component reusability | Success: Architecture is well-planned and documented, components are properly organized, follows existing patterns and theme system_ + +- [ ] 5.1 Create base UI components + - Set up component structure + - Implement reusable components + - Add styling and theming + - _Leverage: src/components/BaseComponent.tsx, src/styles/theme.ts_ + - _Requirements: 5.1_ + - _Prompt: Role: Frontend Developer specializing in React and component architecture | Task: Create reusable UI components following requirement 5.1, extending BaseComponent patterns and using existing theme system from src/styles/theme.ts | Restrictions: Must use existing theme variables, follow component composition patterns, ensure accessibility compliance | Success: Components are reusable and properly themed, follow existing architecture, accessible and responsive_ + +- [ ] 5.2 Implement feature-specific components + - Create feature components + - Add state management + - Connect to API endpoints + - _Leverage: src/hooks/useApi.ts, src/components/BaseComponent.tsx_ + - _Requirements: 5.2, 5.3_ + - _Prompt: Role: React Developer with expertise in state management and API integration | Task: Implement feature-specific components following requirements 5.2 and 5.3, using API hooks from src/hooks/useApi.ts and extending BaseComponent patterns | Restrictions: Must use existing state management patterns, handle loading and error states properly, maintain component performance | Success: Components are fully functional with proper state management, API integration works smoothly, user experience is responsive and intuitive_ + +- [ ] 6. Integration and testing + - Plan integration approach + - _Leverage: src/utils/integrationUtils.ts, tests/helpers/testUtils.ts_ + - _Requirements: 6.0_ + - _Prompt: Role: Integration Engineer with expertise in system integration and testing strategies | Task: Plan comprehensive integration approach following requirement 6.0, leveraging integration utilities from src/utils/integrationUtils.ts and test helpers | Restrictions: Must consider all system components, ensure proper test coverage, maintain integration test reliability | Success: Integration plan is comprehensive and feasible, all system components work together correctly, integration points are well-tested_ + +- [ ] 6.1 Write end-to-end tests + - Set up E2E testing framework + - Write user journey tests + - Add test automation + - _Leverage: tests/helpers/testUtils.ts, tests/fixtures/data.ts_ + - _Requirements: All_ + - _Prompt: Role: QA Automation Engineer with expertise in E2E testing and test frameworks like Cypress or Playwright | Task: Implement comprehensive end-to-end tests covering all requirements, setting up testing framework and user journey tests using test utilities and fixtures | Restrictions: Must test real user workflows, ensure tests are maintainable and reliable, do not test implementation details | Success: E2E tests cover all critical user journeys, tests run reliably in CI/CD pipeline, user experience is validated from end-to-end_ + +- [ ] 6.2 Final integration and cleanup + - Integrate all components + - Fix any integration issues + - Clean up code and documentation + - _Leverage: src/utils/cleanup.ts, docs/templates/_ + - _Requirements: All_ + - _Prompt: Role: Senior Developer with expertise in code quality and system integration | Task: Complete final integration of all components and perform comprehensive cleanup covering all requirements, using cleanup utilities and documentation templates | Restrictions: Must not break existing functionality, ensure code quality standards are met, maintain documentation consistency | Success: All components are fully integrated and working together, code is clean and well-documented, system meets all requirements and quality standards_ diff --git a/.spec-workflow/templates/tech-template.md b/.spec-workflow/templates/tech-template.md new file mode 100644 index 0000000..57cd538 --- /dev/null +++ b/.spec-workflow/templates/tech-template.md @@ -0,0 +1,99 @@ +# Technology Stack + +## Project Type +[Describe what kind of project this is: web application, CLI tool, desktop application, mobile app, library, API service, embedded system, game, etc.] + +## Core Technologies + +### Primary Language(s) +- **Language**: [e.g., Python 3.11, Go 1.21, TypeScript, Rust, C++] +- **Runtime/Compiler**: [if applicable] +- **Language-specific tools**: [package managers, build tools, etc.] + +### Key Dependencies/Libraries +[List the main libraries and frameworks your project depends on] +- **[Library/Framework name]**: [Purpose and version] +- **[Library/Framework name]**: [Purpose and version] + +### Application Architecture +[Describe how your application is structured - this could be MVC, event-driven, plugin-based, client-server, standalone, microservices, monolithic, etc.] + +### Data Storage (if applicable) +- **Primary storage**: [e.g., PostgreSQL, files, in-memory, cloud storage] +- **Caching**: [e.g., Redis, in-memory, disk cache] +- **Data formats**: [e.g., JSON, Protocol Buffers, XML, binary] + +### External Integrations (if applicable) +- **APIs**: [External services you integrate with] +- **Protocols**: [e.g., HTTP/REST, gRPC, WebSocket, TCP/IP] +- **Authentication**: [e.g., OAuth, API keys, certificates] + +### Monitoring & Dashboard Technologies (if applicable) +- **Dashboard Framework**: [e.g., React, Vue, vanilla JS, terminal UI] +- **Real-time Communication**: [e.g., WebSocket, Server-Sent Events, polling] +- **Visualization Libraries**: [e.g., Chart.js, D3, terminal graphs] +- **State Management**: [e.g., Redux, Vuex, file system as source of truth] + +## Development Environment + +### Build & Development Tools +- **Build System**: [e.g., Make, CMake, Gradle, npm scripts, cargo] +- **Package Management**: [e.g., pip, npm, cargo, go mod, apt, brew] +- **Development workflow**: [e.g., hot reload, watch mode, REPL] + +### Code Quality Tools +- **Static Analysis**: [Tools for code quality and correctness] +- **Formatting**: [Code style enforcement tools] +- **Testing Framework**: [Unit, integration, and/or end-to-end testing tools] +- **Documentation**: [Documentation generation tools] + +### Version Control & Collaboration +- **VCS**: [e.g., Git, Mercurial, SVN] +- **Branching Strategy**: [e.g., Git Flow, GitHub Flow, trunk-based] +- **Code Review Process**: [How code reviews are conducted] + +### Dashboard Development (if applicable) +- **Live Reload**: [e.g., Hot module replacement, file watchers] +- **Port Management**: [e.g., Dynamic allocation, configurable ports] +- **Multi-Instance Support**: [e.g., Running multiple dashboards simultaneously] + +## Deployment & Distribution (if applicable) +- **Target Platform(s)**: [Where/how the project runs: cloud, on-premise, desktop, mobile, embedded] +- **Distribution Method**: [How users get your software: download, package manager, app store, SaaS] +- **Installation Requirements**: [Prerequisites, system requirements] +- **Update Mechanism**: [How updates are delivered] + +## Technical Requirements & Constraints + +### Performance Requirements +- [e.g., response time, throughput, memory usage, startup time] +- [Specific benchmarks or targets] + +### Compatibility Requirements +- **Platform Support**: [Operating systems, architectures, versions] +- **Dependency Versions**: [Minimum/maximum versions of dependencies] +- **Standards Compliance**: [Industry standards, protocols, specifications] + +### Security & Compliance +- **Security Requirements**: [Authentication, encryption, data protection] +- **Compliance Standards**: [GDPR, HIPAA, SOC2, etc. if applicable] +- **Threat Model**: [Key security considerations] + +### Scalability & Reliability +- **Expected Load**: [Users, requests, data volume] +- **Availability Requirements**: [Uptime targets, disaster recovery] +- **Growth Projections**: [How the system needs to scale] + +## Technical Decisions & Rationale +[Document key architectural and technology choices] + +### Decision Log +1. **[Technology/Pattern Choice]**: [Why this was chosen, alternatives considered] +2. **[Architecture Decision]**: [Rationale, trade-offs accepted] +3. **[Tool/Library Selection]**: [Reasoning, evaluation criteria] + +## Known Limitations +[Document any technical debt, limitations, or areas for improvement] + +- [Limitation 1]: [Impact and potential future solutions] +- [Limitation 2]: [Why it exists and when it might be addressed] diff --git a/.spec-workflow/user-templates/README.md b/.spec-workflow/user-templates/README.md new file mode 100644 index 0000000..ad36a48 --- /dev/null +++ b/.spec-workflow/user-templates/README.md @@ -0,0 +1,64 @@ +# User Templates + +This directory allows you to create custom templates that override the default Spec Workflow templates. + +## How to Use Custom Templates + +1. **Create your custom template file** in this directory with the exact same name as the default template you want to override: + - `requirements-template.md` - Override requirements document template + - `design-template.md` - Override design document template + - `tasks-template.md` - Override tasks document template + - `product-template.md` - Override product steering template + - `tech-template.md` - Override tech steering template + - `structure-template.md` - Override structure steering template + +2. **Template Loading Priority**: + - The system first checks this `user-templates/` directory + - If a matching template is found here, it will be used + - Otherwise, the default template from `templates/` will be used + +## Example Custom Template + +To create a custom requirements template: + +1. Create a file named `requirements-template.md` in this directory +2. Add your custom structure, for example: + +```markdown +# Requirements Document + +## Executive Summary +[Your custom section] + +## Business Requirements +[Your custom structure] + +## Technical Requirements +[Your custom fields] + +## Custom Sections +[Add any sections specific to your workflow] +``` + +## Template Variables + +Templates can include placeholders that will be replaced when documents are created: +- `{{projectName}}` - The name of your project +- `{{featureName}}` - The name of the feature being specified +- `{{date}}` - The current date +- `{{author}}` - The document author + +## Best Practices + +1. **Start from defaults**: Copy a default template from `../templates/` as a starting point +2. **Keep structure consistent**: Maintain similar section headers for tool compatibility +3. **Document changes**: Add comments explaining why sections were added/modified +4. **Version control**: Track your custom templates in version control +5. **Test thoroughly**: Ensure custom templates work with the spec workflow tools + +## Notes + +- Custom templates are project-specific and not included in the package distribution +- The `templates/` directory contains the default templates which are updated with each version +- Your custom templates in this directory are preserved during updates +- If a custom template has errors, the system will fall back to the default template diff --git a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs index e3f7900..e681ef2 100644 --- a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs +++ b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -14,7 +14,7 @@ public static class DictionaryExtension /// 要获取值的键 /// 如果字典中不存在该键,则返回的默认值 /// 指定键的值,如果字典中不存在该键,则返回默认值 - public static TValue GetValueOrDefault(this IDictionary dictionary, TKey key, TValue defaultValue = default) + public static TValue? GetValueOrDefault(this IDictionary dictionary, TKey key, TValue? defaultValue = default) { if (dictionary.TryGetValue(key, out TValue value)) { diff --git a/EasyTool.Core/EasyTool.Core.csproj b/EasyTool.Core/EasyTool.Core.csproj index 821383f..405fa8d 100644 --- a/EasyTool.Core/EasyTool.Core.csproj +++ b/EasyTool.Core/EasyTool.Core.csproj @@ -1,13 +1,16 @@ - + - netstandard2.1;.net6.0 - 11 + netstandard2.1;net10.0 + latest enable + true + $(NoWarn); $(MSBuildProjectName.Replace(" ", "_").Replace(".Core", "")) + Joce.EasyTool.Core 一个大西瓜,TimChen - 2023.0908.1 + 2026.0108.1 A open source C# tool to make .NET easy @@ -19,6 +22,10 @@ logo.png + + + + True @@ -36,9 +43,9 @@ - - - + + + diff --git a/EasyTool.Core/IOCategory/FileTypeUtil.cs b/EasyTool.Core/IOCategory/FileTypeUtil.cs index d2fb426..5cb71cb 100644 --- a/EasyTool.Core/IOCategory/FileTypeUtil.cs +++ b/EasyTool.Core/IOCategory/FileTypeUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -25,10 +25,11 @@ public static string GetType(FileInfo file) byte[] buffer = new byte[256]; using (FileStream fs = file.OpenRead()) { - if (fs.Length >= 256) - fs.Read(buffer, 0, 256); - else - fs.Read(buffer, 0, (int)fs.Length); + int readLength = fs.Read(buffer, 0, buffer.Length); + if (readLength < buffer.Length) + { + // 处理读取不足的情况,虽然对于头部检测通常前几个字节就够了,但为了严谨性 + } } string header = ""; diff --git a/EasyTool.Core/IOCategory/FileUtil.cs b/EasyTool.Core/IOCategory/FileUtil.cs index c46db33..772ca51 100644 --- a/EasyTool.Core/IOCategory/FileUtil.cs +++ b/EasyTool.Core/IOCategory/FileUtil.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Text; using System.Web; @@ -884,7 +885,7 @@ public static string GetFileSuffix(string filePath) /// /// 文件 /// 文件名 - public static string GetFilePrefix(FileInfo file) + public static string? GetFilePrefix(FileInfo file) { if (file == null) { @@ -921,7 +922,7 @@ public static string GetFilePrefix(string filePath) /// /// 文件 /// 类型,文件的扩展名,未找到为null - public static string GetType(FileInfo file) + public static string? GetType(FileInfo file) { return FileTypeUtil.GetType(file); } @@ -954,7 +955,7 @@ public static Stream GetInputStream(string path) /// 文件 /// 编码格式,默认为UTF-8 /// BOM输入流 - public static StreamReader GetBOMInputStream(FileInfo file, Encoding encoding = null) + public static StreamReader GetBOMInputStream(FileInfo file, Encoding? encoding = null) { if (encoding == null) { @@ -998,7 +999,7 @@ public static StreamReader GetBOMInputStream(FileInfo file, Encoding encoding = /// 文件 /// 编码格式,默认为UTF-8 /// 文件读取流 - public static StreamReader GetReader(FileInfo file, Encoding encoding = null) + public static StreamReader GetReader(FileInfo file, Encoding? encoding = null) { if (encoding == null) { @@ -1078,7 +1079,7 @@ public static byte[] ReadBytes(string path) /// 文件 /// 编码格式,默认为UTF-8 /// 内容 - public static string ReadString(FileInfo file, Encoding encoding = null) + public static string ReadString(FileInfo file, Encoding? encoding = null) { if (encoding == null) @@ -1101,7 +1102,7 @@ public static string ReadString(FileInfo file, Encoding encoding = null) /// 文件路径 /// 编码格式,默认为UTF-8 /// 内容 - public static string ReadString(string path, Encoding encoding = null) + public static string ReadString(string path, Encoding? encoding = null) { return ReadString(new FileInfo(path), encoding); // 直接调用另一个重载方法 } @@ -1113,7 +1114,7 @@ public static string ReadString(string path, Encoding encoding = null) /// 网络文件地址 /// 编码格式,默认为UTF-8 /// 内容 - public static string ReadString(Uri url, Encoding encoding = null) + public static string ReadString(Uri url, Encoding? encoding = null) { // 如果未指定编码格式,则默认为UTF-8 if (encoding == null) @@ -1124,11 +1125,12 @@ public static string ReadString(Uri url, Encoding encoding = null) string result; try { - // 创建WebClient对象 - using (WebClient client = new WebClient()) + // 创建HttpClient对象 + using (HttpClient client = new HttpClient()) { // 下载指定地址的文件,并转换为字节数组 - byte[] data = client.DownloadData(url); + // 注意:为了保持同步方法签名,这里使用了同步等待,这在某些上下文中可能会导致死锁 + byte[] data = client.GetByteArrayAsync(url).GetAwaiter().GetResult(); // 将字节数组转换为字符串,并使用指定编码格式解码 result = encoding.GetString(data); } @@ -1148,7 +1150,7 @@ public static string ReadString(Uri url, Encoding encoding = null) /// 文件路径 /// 编码格式,默认为UTF-8 /// - public static string[] ReadAllLines(string path, Encoding encoding = null) + public static string[] ReadAllLines(string path, Encoding? encoding = null) { // 如果未指定编码格式,则默认为 UTF-8 if (encoding == null) @@ -1206,7 +1208,7 @@ public static string GetLineSeparator() /// 文件路径 /// 编码格式,默认为UTF-8 /// 文件信息 - public static FileInfo WriteString(string content, string path, Encoding encoding = null) + public static FileInfo WriteString(string content, string path, Encoding? encoding = null) { if (encoding == null) { @@ -1274,7 +1276,7 @@ public static FileInfo WriteLines(List list, string path, Encoding encod /// 文件路径 /// 编码格式,默认为UTF-8 /// 文件信息 - public static FileInfo AppendLines(List list, string path, Encoding encoding = null) + public static FileInfo AppendLines(List list, string path, Encoding? encoding = null) { if (encoding == null) { diff --git a/EasyTool.Core/IOCategory/WatchMonitor.cs b/EasyTool.Core/IOCategory/WatchMonitor.cs index 0db8a47..d9a6634 100644 --- a/EasyTool.Core/IOCategory/WatchMonitor.cs +++ b/EasyTool.Core/IOCategory/WatchMonitor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -132,8 +132,8 @@ public void Dispose() /// public class FileEventArgs : EventArgs { - public string FilePath { get; } - public Exception Exception { get; } + public string? FilePath { get; } + public Exception? Exception { get; } public FileEventArgs(string path) { diff --git a/EasyTool.Core/LanguageCategory/TreeUtil.cs b/EasyTool.Core/LanguageCategory/TreeUtil.cs index 852a4ce..f437583 100644 --- a/EasyTool.Core/LanguageCategory/TreeUtil.cs +++ b/EasyTool.Core/LanguageCategory/TreeUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -168,10 +168,10 @@ public int GetMinDepth() /// /// 节点 /// 下一个兄弟节点 - public TreeNode GetNextSibling(TreeNode node) + public TreeNode? GetNextSibling(TreeNode node) { var siblings = GetSiblings(node); - var index = siblings.FindIndex(n => n.Id.Equals(node.Id)); + var index = siblings.FindIndex(n => n.Id!.Equals(node.Id)); return index + 1 < siblings.Count ? siblings[index + 1] : null; } @@ -180,10 +180,10 @@ public TreeNode GetNextSibling(TreeNode node) /// /// 节点 /// 上一个兄弟节点 - public TreeNode GetPreviousSibling(TreeNode node) + public TreeNode? GetPreviousSibling(TreeNode node) { var siblings = GetSiblings(node); - var index = siblings.FindIndex(n => n.Id.Equals(node.Id)); + var index = siblings.FindIndex(n => n.Id!.Equals(node.Id)); return index - 1 >= 0 ? siblings[index - 1] : null; } @@ -192,7 +192,7 @@ public TreeNode GetPreviousSibling(TreeNode node) /// /// 节点 /// 首个子节点 - public TreeNode GetFirstChild(TreeNode node) + public TreeNode? GetFirstChild(TreeNode node) { return node.Children.Count > 0 ? node.Children[0] : null; } @@ -202,7 +202,7 @@ public TreeNode GetFirstChild(TreeNode node) /// /// 节点 /// 最后一个子节点 - public TreeNode GetLastChild(TreeNode node) + public TreeNode? GetLastChild(TreeNode node) { return node.Children.Count > 0 ? node.Children[node.Children.Count - 1] : null; } @@ -296,6 +296,7 @@ public TreeNode(T id, T parentId, string name, int weight, D data) this.Name = name; this.Weight = weight; this.Data = data; + this.Children = new List>(); } } } diff --git a/EasyTool.Core/NetCategory/NetUtil.cs b/EasyTool.Core/NetCategory/NetUtil.cs index 3f41d78..9f5460b 100644 --- a/EasyTool.Core/NetCategory/NetUtil.cs +++ b/EasyTool.Core/NetCategory/NetUtil.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Net; @@ -38,7 +39,7 @@ public static bool Ping(string host) // Resolve the IP address of a host // 获取指定主机的IP地址 - public static IPAddress GetIpAddress(string host) + public static IPAddress? GetIpAddress(string host) { try { @@ -91,13 +92,14 @@ public static bool IsPortOpen(string host, int port) // Send an HTTP GET request and return the response // 发送HTTP GET请求并返回响应 - [Obsolete("建议使用HttpClient替代此方法")] - public static string HttpGet(string url) + public static string? HttpGet(string url) { try { - WebClient client = new WebClient(); - return client.DownloadString(url); + using (HttpClient client = new HttpClient()) + { + return client.GetStringAsync(url).GetAwaiter().GetResult(); + } } catch { @@ -107,15 +109,16 @@ public static string HttpGet(string url) // Send an HTTP POST request and return the response // 发送HTTP POST请求并返回响应 - [Obsolete("建议使用HttpClient替代此方法")] - public static string HttpPost(string url, string data) + public static string? HttpPost(string url, string data) { try { - WebClient client = new WebClient(); - // 设置请求头的内容类型 - client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; - return client.UploadString(url, data); + using (HttpClient client = new HttpClient()) + { + StringContent content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded"); + HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult(); + return response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + } } catch { diff --git a/EasyTool.Core/ToolCategory/EnumUtil.cs b/EasyTool.Core/ToolCategory/EnumUtil.cs index 0079cff..bd3dc3e 100644 --- a/EasyTool.Core/ToolCategory/EnumUtil.cs +++ b/EasyTool.Core/ToolCategory/EnumUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; @@ -197,9 +197,9 @@ public static TEnum GetValueByName(string name) /// 枚举类型 /// 枚举值 /// 与值对应的名称,如果值不存在,则返回null - public static string GetNameByValue(TEnum value) + public static string? GetNameByValue(TEnum value) { - return Enum.GetName(typeof(TEnum), value); + return Enum.GetName(typeof(TEnum), value!); } /// @@ -208,7 +208,7 @@ public static string GetNameByValue(TEnum value) /// 枚举类型 /// 枚举值 /// 与值对应的注释,如果值不存在或未设置注释,则返回null - public static string GetDescriptionByValue(TEnum value) + public static string? GetDescriptionByValue(TEnum value) { var name = GetNameByValue(value); if (string.IsNullOrEmpty(name)) @@ -216,7 +216,7 @@ public static string GetDescriptionByValue(TEnum value) return null; } - return GetDescription(GetValueByName(name)); + return GetDescription(GetValueByName(name!)); } /// @@ -225,7 +225,7 @@ public static string GetDescriptionByValue(TEnum value) /// 枚举类型 /// 枚举值 /// 与值对应的Display名称,如果值不存在或未设置Display名称,则返回null - public static string GetDisplayNameByValue(TEnum value) + public static string? GetDisplayNameByValue(TEnum value) { var name = GetNameByValue(value); if (string.IsNullOrEmpty(name)) @@ -233,7 +233,7 @@ public static string GetDisplayNameByValue(TEnum value) return null; } - return GetDisplayName(GetValueByName(name)); + return GetDisplayName(GetValueByName(name!)); } } } diff --git a/EasyTool.Core/ToolCategory/IdcardUtil.cs b/EasyTool.Core/ToolCategory/IdcardUtil.cs index 015c24c..eee852e 100644 --- a/EasyTool.Core/ToolCategory/IdcardUtil.cs +++ b/EasyTool.Core/ToolCategory/IdcardUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -378,7 +378,7 @@ public static string ReplaceBirthday(string idcard, DateTime birthday) /// 身份证号码 /// 新的性别 /// 新的身份证号码 - public static string ReplaceGender(string idcard, Gender gender) + public static string? ReplaceGender(string idcard, Gender gender) { if (string.IsNullOrEmpty(idcard)) { diff --git a/EasyTool.Core/ToolCategory/MEFUtil.cs b/EasyTool.Core/ToolCategory/MEFUtil.cs index 24d3aeb..481bfa5 100644 --- a/EasyTool.Core/ToolCategory/MEFUtil.cs +++ b/EasyTool.Core/ToolCategory/MEFUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.Composition.Hosting; using System.Reflection; @@ -21,7 +21,7 @@ public class MEFUtil /// 导出部件的类型 /// 目录路径 /// 导出部件的列表 - public static IEnumerable LoadExportParts(string directory = null) + public static IEnumerable LoadExportParts(string? directory = null) { // 如果目录为空,则使用默认目录 directory ??= DefaultDirectory; diff --git a/EasyTool.Core/ToolCategory/ObjectUtil.cs b/EasyTool.Core/ToolCategory/ObjectUtil.cs index a17c076..a2f3bcd 100644 --- a/EasyTool.Core/ToolCategory/ObjectUtil.cs +++ b/EasyTool.Core/ToolCategory/ObjectUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Dynamic; @@ -22,7 +22,7 @@ public class ObjectUtil /// /// 检查对象是否为 null /// - public static bool IsNull(object obj) + public static bool IsNull(object? obj) { return obj == null; } @@ -30,7 +30,7 @@ public static bool IsNull(object obj) /// /// 检查对象是否不为 null /// - public static bool IsNotNull(object obj) + public static bool IsNotNull(object? obj) { return obj != null; } @@ -38,7 +38,7 @@ public static bool IsNotNull(object obj) /// /// 检查对象是否为空(null 或者 空字符串或空白字符) /// - public static bool IsNullOrEmpty(object obj) + public static bool IsNullOrEmpty(object? obj) { if (IsNull(obj)) { @@ -61,7 +61,7 @@ public static bool IsNullOrEmpty(object obj) /// /// 检查对象是否不为空(非 null 且 非空字符串 或者 非空集合) /// - public static bool IsNotNullOrEmpty(object obj) + public static bool IsNotNullOrEmpty(object? obj) { return !IsNullOrEmpty(obj); } @@ -103,7 +103,7 @@ public static T Convert(object obj) /// /// 将对象转换为指定类型 /// - public static object Convert(object obj, Type targetType) + public static object? Convert(object obj, Type targetType) { if (IsNull(obj)) { @@ -136,7 +136,7 @@ public static IEnumerable GetProperties(object obj) /// /// 获取对象的属性值 /// - public static object GetPropertyValue(object obj, string propertyName) + public static object? GetPropertyValue(object obj, string propertyName) { return obj.GetType().GetProperty(propertyName)?.GetValue(obj); } @@ -144,7 +144,7 @@ public static object GetPropertyValue(object obj, string propertyName) /// /// 设置对象的属性值 /// - public static void SetPropertyValue(object obj, string propertyName, object value) + public static void SetPropertyValue(object obj, string propertyName, object? value) { obj.GetType().GetProperty(propertyName)?.SetValue(obj, value); } @@ -160,7 +160,7 @@ public static IEnumerable GetFields(object obj) /// /// 获取对象的字段值 /// - public static object GetFieldValue(object obj, string fieldName) + public static object? GetFieldValue(object obj, string fieldName) { return obj.GetType().GetField(fieldName)?.GetValue(obj); } @@ -168,7 +168,7 @@ public static object GetFieldValue(object obj, string fieldName) /// /// 设置对象的字段值 /// - public static void SetFieldValue(object obj, string fieldName, object value) + public static void SetFieldValue(object obj, string fieldName, object? value) { obj.GetType().GetField(fieldName)?.SetValue(obj, value); } @@ -307,7 +307,7 @@ public static void ProcessPropertyValue(object obj, string propertyName, Action< /// /// 将对象序列化为 JSON 字符串 /// - public static string ToJson(object obj) + public static string? ToJson(object obj) { if (IsNull(obj)) { @@ -342,7 +342,7 @@ public static T FromJson(string json) /// /// 将对象序列化为 XML 字符串 /// - public static string ToXml(object obj) + public static string? ToXml(object obj) { if (IsNull(obj)) { @@ -377,7 +377,7 @@ public static T FromXml(string xml) /// /// 将对象转换为字典 /// - public static Dictionary ToDictionary(object obj) + public static Dictionary? ToDictionary(object obj) { if (IsNull(obj)) { @@ -488,7 +488,7 @@ public static int GetHashCode(object obj) /// /// 深拷贝对象 /// - public static T DeepClone(T obj) + public static T? DeepClone(T obj) { if (IsNull(obj)) { @@ -546,7 +546,7 @@ public static IEnumerable> ToKeyValuePairs(object o /// /// 深度复制对象 /// - public static object DeepCopy(object obj) + public static object? DeepCopy(object obj) { if (obj == null) { @@ -837,7 +837,7 @@ public static string GetAssemblyQualifiedName(Type type) /// /// 获取指定类型的默认值 /// - public static object GetDefault(Type type) + public static object? GetDefault(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } @@ -961,7 +961,7 @@ public static bool IsEnumerableType(Type type) /// /// 将对象转换为动态扩展对象 /// - public static dynamic ToDynamic(object obj) + public static dynamic? ToDynamic(object obj) { if (obj == null) { @@ -1027,7 +1027,7 @@ public static string SerializeToXml(object obj) /// /// 将 XML 字符串反序列化为指定类型的对象 /// - public static object DeserializeFromXml(string xml, Type type) + public static object? DeserializeFromXml(string xml, Type type) { XmlSerializer serializer = new XmlSerializer(type); @@ -1040,8 +1040,10 @@ public static object DeserializeFromXml(string xml, Type type) /// /// 将对象序列化为二进制数据 /// + [Obsolete("BinaryFormatter is obsolete and unsafe. Use SerializeToJson or SerializeToXml instead.")] public static byte[] SerializeToBinary(object obj) { +#pragma warning disable SYSLIB0011 // 类型或成员已过时 BinaryFormatter formatter = new BinaryFormatter(); using (MemoryStream stream = new MemoryStream()) @@ -1049,19 +1051,23 @@ public static byte[] SerializeToBinary(object obj) formatter.Serialize(stream, obj); return stream.ToArray(); } +#pragma warning restore SYSLIB0011 // 类型或成员已过时 } /// /// 将二进制数据反序列化为指定类型的对象 /// + [Obsolete("BinaryFormatter is obsolete and unsafe. Use DeserializeFromJson or DeserializeFromXml instead.")] public static object DeserializeFromBinary(byte[] data, Type type) { +#pragma warning disable SYSLIB0011 // 类型或成员已过时 BinaryFormatter formatter = new BinaryFormatter(); using (MemoryStream stream = new MemoryStream(data)) { return formatter.Deserialize(stream); } +#pragma warning restore SYSLIB0011 // 类型或成员已过时 } } } diff --git a/EasyTool.Core/ToolCategory/RuntimeUtil.cs b/EasyTool.Core/ToolCategory/RuntimeUtil.cs index e586afc..eb37da6 100644 --- a/EasyTool.Core/ToolCategory/RuntimeUtil.cs +++ b/EasyTool.Core/ToolCategory/RuntimeUtil.cs @@ -1,7 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +#if NET5_0_OR_GREATER +using System.Runtime.Versioning; +#endif using System.Text; namespace EasyTool @@ -72,6 +75,9 @@ public static void ExitApplication() /// 获取当前系统的物理内存总量 /// /// 物理内存总量(字节) +#if NET5_0_OR_GREATER + [SupportedOSPlatform("windows")] +#endif public static long GetTotalPhysicalMemory() { PerformanceCounter pc = new PerformanceCounter("Memory", "Available Bytes"); @@ -82,6 +88,9 @@ public static long GetTotalPhysicalMemory() /// 获取当前系统的可用物理内存量 /// /// 可用物理内存量(字节) +#if NET5_0_OR_GREATER + [SupportedOSPlatform("windows")] +#endif public static float GetAvailablePhysicalMemory() { PerformanceCounter pc = new PerformanceCounter("Memory", "Available Bytes"); @@ -92,6 +101,9 @@ public static float GetAvailablePhysicalMemory() /// 获取当前系统的虚拟内存总量 /// /// 虚拟内存总量(字节) +#if NET5_0_OR_GREATER + [SupportedOSPlatform("windows")] +#endif public static long GetTotalVirtualMemory() { PerformanceCounter pc = new PerformanceCounter("Memory", "Committed Bytes"); @@ -102,6 +114,9 @@ public static long GetTotalVirtualMemory() /// 获取当前系统的可用虚拟内存量 /// /// 可用虚拟内存量(字节) +#if NET5_0_OR_GREATER + [SupportedOSPlatform("windows")] +#endif public static float GetAvailableVirtualMemory() { PerformanceCounter pc = new PerformanceCounter("Memory", "Committed Bytes"); @@ -116,6 +131,9 @@ public static float GetAvailableVirtualMemory() /// 获取当前系统的实际物理内存总量 /// /// 实际物理内存总量(字节) +#if NET5_0_OR_GREATER + [SupportedOSPlatform("windows")] +#endif public static long GetRealTotalPhysicalMemory() { GetPhysicallyInstalledSystemMemory(out long memoryInBytes); diff --git a/EasyTool.Core/ToolCategory/XmlUtil.cs b/EasyTool.Core/ToolCategory/XmlUtil.cs index 0db77ed..92a081d 100644 --- a/EasyTool.Core/ToolCategory/XmlUtil.cs +++ b/EasyTool.Core/ToolCategory/XmlUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; using System.Xml; @@ -52,7 +52,7 @@ public static XmlDocument CreateNewXmlDocument() /// 元素的名称。 /// 元素的值。 /// 新创建的XML元素。 - public static XmlElement CreateXmlElement(string name, string value = null) + public static XmlElement CreateXmlElement(string name, string? value = null) { var document = new XmlDocument(); var element = document.CreateElement(name); diff --git a/EasyTool.CoreTests/EasyTool.CoreTests.csproj b/EasyTool.CoreTests/EasyTool.CoreTests.csproj index 89de440..3726861 100644 --- a/EasyTool.CoreTests/EasyTool.CoreTests.csproj +++ b/EasyTool.CoreTests/EasyTool.CoreTests.csproj @@ -1,8 +1,9 @@ - + - .net6.0 - 11 + net10.0 + true + latest enable enable enable @@ -12,10 +13,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs b/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs index e79f0a3..f54d0a5 100644 --- a/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs +++ b/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs @@ -101,10 +101,16 @@ public void IsPrivateIpv4_ValidPrivateIps_ReturnsTrue() /// 验证无效的 IPv4 地址,应引发 ArgumentException 异常。 /// [TestMethod] - [ExpectedException(typeof(ArgumentException))] public void IsPrivateIpv4_InvalidIp_ThrowsException() { - IpUtil.IsPrivateIpv4("256.256.256.256"); + try + { + IpUtil.IsPrivateIpv4("256.256.256.256"); + Assert.Fail("Expected ArgumentException was not thrown."); + } + catch (ArgumentException) + { + } } /// @@ -120,10 +126,16 @@ public void IsPrivateIpv6_ValidPrivateIps_ReturnsTrue() /// 验证无效的 IPv6 地址,应引发 ArgumentException 异常。 /// [TestMethod] - [ExpectedException(typeof(ArgumentException))] public void IsPrivateIpv6_InvalidIp_ThrowsException() { - IpUtil.IsPrivateIpv6("2001::1::2"); + try + { + IpUtil.IsPrivateIpv6("2001::1::2"); + Assert.Fail("Expected ArgumentException was not thrown."); + } + catch (ArgumentException) + { + } } /// diff --git a/EasyTool.EmitMapper/EasyTool.EmitMapper.csproj b/EasyTool.EmitMapper/EasyTool.EmitMapper.csproj index 0996f96..9e2e716 100644 --- a/EasyTool.EmitMapper/EasyTool.EmitMapper.csproj +++ b/EasyTool.EmitMapper/EasyTool.EmitMapper.csproj @@ -1,13 +1,14 @@ - + - netstandard2.1;.net6.0 - 11 + netstandard2.1;net10.0 + latest enable $(MSBuildProjectName.Replace(" ", "_").Replace(".EmitMapper", "")) + Joce.EasyTool.EmitMapper 一个大西瓜,TimChen - 2023.0914.1 + 2026.0108.1 A open source C# tool to make .NET easy diff --git a/EasyTool.EmitMapperTests/EasyTool.EmitMapperTests.csproj b/EasyTool.EmitMapperTests/EasyTool.EmitMapperTests.csproj index 1c32465..28315f7 100644 --- a/EasyTool.EmitMapperTests/EasyTool.EmitMapperTests.csproj +++ b/EasyTool.EmitMapperTests/EasyTool.EmitMapperTests.csproj @@ -1,19 +1,21 @@ - + - net6.0 + net10.0 + true enable enable + latest false true - - - - + + + + diff --git a/EasyTool.Image/EasyTool.Image.csproj b/EasyTool.Image/EasyTool.Image.csproj index 6a444e4..869faaa 100644 --- a/EasyTool.Image/EasyTool.Image.csproj +++ b/EasyTool.Image/EasyTool.Image.csproj @@ -1,13 +1,14 @@ - netstandard2.1;.net6.0 - 11 + netstandard2.1;net10.0 + latest enable $(MSBuildProjectName.Replace(" ", "_").Replace(".Core", "")) + Joce.EasyTool.Image 一个大西瓜,TimChen - 2023.0908.1 + 2026.0108.1 A open source C# tool to make .NET easy @@ -35,8 +36,8 @@ - - + + diff --git a/EasyTool.ImageTests/EasyTool.ImageTests.csproj b/EasyTool.ImageTests/EasyTool.ImageTests.csproj index 4c22132..1b36049 100644 --- a/EasyTool.ImageTests/EasyTool.ImageTests.csproj +++ b/EasyTool.ImageTests/EasyTool.ImageTests.csproj @@ -1,19 +1,21 @@ - net6.0 + net10.0 + true enable enable + latest false true - - - - + + + + diff --git a/EasyTool.NPOI/EasyTool.NPOI.csproj b/EasyTool.NPOI/EasyTool.NPOI.csproj index 2b8deec..b585e55 100644 --- a/EasyTool.NPOI/EasyTool.NPOI.csproj +++ b/EasyTool.NPOI/EasyTool.NPOI.csproj @@ -1,30 +1,45 @@ - + - - netstandard2.1;.net6.0 - enable - 11 - - 依赖于NPOI 2.6.2 - 支持通过文件地址或者流的方式读取Excel文件获取工作簿对象IWorkbook, - 通过IWorkbook工作簿对象可以转化成Dataset对象 - 通过ISheet工作表对象可以转化成DataTable对象和List对象 - 以下是一些示例: - 获取数据集对象: - var dateSet = NPOIUtil.OpenWorkbook(path).ConvertToDataSet(); - var dateSet = NPOIUtil.ConvertToDataSet(path); - 获取工作表对象: - var sheet = NPOIUtil.OpenWorkbook(path).GetSheetAt(0); - 获取单表数据对象: - var dataTable = NPOIUtil.OpenWorkbook(path).GetSheetAt(0).ConvertToDataTable(); - List《T》 dataList = NPOIUtil.OpenWorkbook(path).GetSheetAt(0).ConvertToList《List《T》》(); - 从流读取工作簿对象(从流读取需要指定文件类型,缺省值为XLSX): - var workbook = NPOIUtil.OpenWorkbook(stream,ExcelWorkbookType.XLS); - - + + netstandard2.1;net10.0 + enable + latest + $(MSBuildProjectName.Replace(" ", "_").Replace(".NPOI", "")) - - - + Joce.EasyTool.NPOI + 一个大西瓜,TimChen + 2026.0108.1 + + 依赖于NPOI 2.7.5 + 支持通过文件地址或者流的方式读取Excel文件获取工作簿对象IWorkbook, + 通过IWorkbook工作簿对象可以转化成Dataset对象 + 通过ISheet工作表对象可以转化成DataTable对象和List对象 + + Tool Power NPOI Excel + https://github.com/dotnet-easy/easytool + https://easy-dotnet.com + README.md + LICENSE + logo.png + + + + + True + \ + + + True + \ + + + True + \ + + + + + + diff --git a/EasyTool.NPOITests/EasyTool.NPOITests.csproj b/EasyTool.NPOITests/EasyTool.NPOITests.csproj index 6a5a733..167c9e3 100644 --- a/EasyTool.NPOITests/EasyTool.NPOITests.csproj +++ b/EasyTool.NPOITests/EasyTool.NPOITests.csproj @@ -1,19 +1,21 @@ - net6.0 + net10.0 + true enable enable + latest false true - - - - + + + + diff --git a/EasyTool.Web/EasyTool.Web.csproj b/EasyTool.Web/EasyTool.Web.csproj index b8b10a5..3b58b12 100644 --- a/EasyTool.Web/EasyTool.Web.csproj +++ b/EasyTool.Web/EasyTool.Web.csproj @@ -1,9 +1,39 @@ - + - - net6.0;netcoreapp3.1 - enable - + + net10.0 + enable + latest + $(MSBuildProjectName.Replace(" ", "_").Replace(".Web", "")) + + Joce.EasyTool.Web + 一个大西瓜,TimChen + 2026.0108.1 + + A open source C# tool to make .NET easy + + Tool Power Web + https://github.com/dotnet-easy/easytool + https://easy-dotnet.com + README.md + LICENSE + logo.png + + + + + True + \ + + + True + \ + + + True + \ + + diff --git a/EasyTool.WebTests/EasyTool.WebTests.csproj b/EasyTool.WebTests/EasyTool.WebTests.csproj index b1bd052..b4f5d54 100644 --- a/EasyTool.WebTests/EasyTool.WebTests.csproj +++ b/EasyTool.WebTests/EasyTool.WebTests.csproj @@ -1,19 +1,21 @@ - net6.0 + net10.0 + true enable enable + latest false true - - - - + + + + From 2eead635602323837aeeefe8f0c16893ab3d2023 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 11:21:25 +0800 Subject: [PATCH 02/14] =?UTF-8?q?refactor:=20=E6=A0=87=E8=AE=B0=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E7=9A=84=E5=8C=85=E8=A3=85=E6=96=B9=E6=B3=95=E4=B8=BA?= =?UTF-8?q?=20Obsolete=20=E4=BB=A5=E5=BC=95=E5=AF=BC=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=86=85=E7=BD=AE=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 主要更改 ### 标记 Obsolete 的方法 (~140+ 个) 将简单包装 .NET 内置 API 的方法标记为 [Obsolete],引导开发者直接使用内置 API: **IO/网络/运行时类:** - IoUtil.cs: 8 个 (File.ReadAllLines, ReadAllText, WriteAllLines 等) - URLUtil.cs: 5 个 (ExtractDomain, ExtractPath, ExtractQueryString 等) - RuntimeUtil.cs: 2 个 (GetDotNetVersion, GetOSVersion) - RegexUtil.cs: 2 个 (IsMatch, Replace) - TimerUtil.cs: 2 个 (StartNew, Wait) - TimestampUtil.cs: 6 个 (时间戳转换方法) **反射/类型/枚举类:** - TypeUtil.cs: 15 个 (IsEnum, GetProperties, GetMethods 等) - ReflectUtil.cs: 10 个 (GetType, GetAttribute, GetAssembly 等) - EnumUtil.cs: 7 个 (GetNames, GetValues, Parse 等) - ClassUtil.cs: 部分方法 **集合/列表/队列/栈类:** - ListUtil.cs: 13 个 (IndexOf, AddRange, ForEach, Sort 等) - QueueUtil.cs: 7 个 (Enqueue, Dequeue, Peek, Contains 等) - StackUtil.cs: 7 个 (Push, Pop, Peek, Contains 等) - LinkedListUtil.cs: 8 个 (AddFirst, AddLast, Remove 等) - IteratorUtil.cs: 7 个 (Filter, Map, Take, Skip 等 LINQ 包装) **工具类:** - ArrayUtil.cs: 部分方法 - StrUtil.cs: 部分方法 - HexUtil.cs: 部分方法 - ObjectUtil.cs: 多个方法 - FileUtil.cs: 部分方法 - EnvUtil.cs: 10 个 - EscapeUtil.cs: 5 个 - ProcessUtil.cs: 7 个 - DLLUtil.cs: 5 个 - MathUtil.cs: 1 个 (Average) - NumberUtil.cs: 1 个 (DecimalFormat) - Base64Util.cs: 2 个 (Encode, Decode) ### 新增扩展类文件 添加了多个独立的扩展类文件: - ArrayExtension.cs - 数组扩展 - ByteExtension.cs - 字节扩展 - ConvertExtension.cs - 转换扩展 - EnumExtension.cs - 枚举扩展 - ExceptionExtension.cs - 异常扩展 - FileSystemExtension.cs - 文件系统扩展 - StreamExtension.cs - 流扩展 - PropertyInfoExtension.cs - 属性信息扩展 - StringBuilderExtension.cs - 字符串构建器扩展 - StringComparisonExtension.cs - 字符串比较扩展 - TaskExtension.cs - 任务扩展 - TypeExtension.cs - 类型扩展 - GuidExtension.cs - GUID 扩展 - ObjectExtension.cs - 对象扩展 - DelegateExtension.cs - 委托扩展 - ColorExtension.cs - 颜色扩展 ### 删除文件 - Extension.Convert.cs - 合并到 ConvertExtension.cs ## 保持不变的文件 以下文件包含自定义业务逻辑,所有方法均保留: - IdUtil.cs (UUID/雪花ID生成) - AesUtil.cs, DesUtil.cs (加密算法) - Base32Util.cs, Base62Util.cs (编码算法) - ImgUtil.cs (图像处理) - NPOIUtil.cs (Excel 处理) - DateTimeUtil.cs, LunarCalendarUtil.cs (日期处理) - ZipUtil.cs, HashUtil.cs (压缩/哈希) - 等等... ## 兼容性 - 所有标记的方法仍可正常使用 - 使用 `false` 参数,仅警告不报错 - 保留向后兼容性 编译状态: ✅ 0 错误, 458 警告 --- .idea/.idea.EasyTool/.idea/.gitignore | 15 + .idea/.idea.EasyTool/.idea/encodings.xml | 4 + .idea/.idea.EasyTool/.idea/indexLayout.xml | 8 + .idea/.idea.EasyTool/.idea/vcs.xml | 6 + EasyTool.Core/CloneCategory/CloneExtension.cs | 4 +- EasyTool.Core/CloneCategory/CloneUtil.cs | 14 +- EasyTool.Core/CodeCategory/Base64Util.cs | 4 + .../CollectionsCategory/ArrayExtension.cs | 430 +++++++++++++ .../DictionaryExtension.cs | 4 + .../CollectionsCategory/IteratorUtil.cs | 19 +- .../CollectionsCategory/LinkedListUtil.cs | 16 + EasyTool.Core/CollectionsCategory/ListUtil.cs | 30 +- .../CollectionsCategory/QueueUtil.cs | 23 +- .../CollectionsCategory/StackUtil.cs | 23 +- .../ConvertCategory/ByteExtension.cs | 584 ++++++++++++++++++ ...tension.Convert.cs => ConvertExtension.cs} | 13 +- EasyTool.Core/ConvertCategory/ConvertUtil.cs | 28 +- .../ConvertCategory/NumberExtension.cs | 546 ++++++++++++++++ .../DateTimeCategory/DateTimeExtension.cs | 350 +++++++++++ EasyTool.Core/DateTimeCategory/TimerUtil.cs | 4 + .../DateTimeCategory/TimestampUtil.cs | 12 + EasyTool.Core/EasyTool.Core.csproj | 6 +- .../IEnumerableExtensions.cs | 441 ++++++++++++- .../IOCategory/FileSystemExtension.cs | 522 ++++++++++++++++ EasyTool.Core/IOCategory/FileTypeUtil.cs | 2 +- EasyTool.Core/IOCategory/FileUtil.cs | 22 +- EasyTool.Core/IOCategory/IoUtil.cs | 16 + EasyTool.Core/IOCategory/StreamExtension.cs | 365 +++++++++++ EasyTool.Core/IOCategory/Tailer.cs | 6 +- EasyTool.Core/IOCategory/WatchMonitor.cs | 12 +- EasyTool.Core/LanguageCategory/TreeUtil.cs | 26 +- EasyTool.Core/MathCategory/MathUtil.cs | 2 + EasyTool.Core/MathCategory/NumberUtil.cs | 2 + .../NetCategory/HttpClientExtension.cs | 2 +- EasyTool.Core/ToolCategory/ArrayUtil.cs | 12 + EasyTool.Core/ToolCategory/ClassUtil.cs | 16 + EasyTool.Core/ToolCategory/ColorExtension.cs | 348 +++++++++++ EasyTool.Core/ToolCategory/CreditCodeUtil.cs | 17 +- EasyTool.Core/ToolCategory/DLLUtil.cs | 24 +- .../ToolCategory/DelegateExtension.cs | 428 +++++++++++++ EasyTool.Core/ToolCategory/EnumExtension.cs | 357 +++++++++++ EasyTool.Core/ToolCategory/EnumUtil.cs | 14 + EasyTool.Core/ToolCategory/EnvUtil.cs | 20 + EasyTool.Core/ToolCategory/EscapeUtil.cs | 10 + .../ToolCategory/ExceptionExtension.cs | 338 ++++++++++ EasyTool.Core/ToolCategory/GuidExtension.cs | 288 +++++++++ EasyTool.Core/ToolCategory/HexUtil.cs | 4 + EasyTool.Core/ToolCategory/IdcardUtil.cs | 4 +- EasyTool.Core/ToolCategory/ObjectExtension.cs | 479 ++++++++++++++ EasyTool.Core/ToolCategory/ObjectUtil.cs | 130 +++- EasyTool.Core/ToolCategory/ProcessUtil.cs | 14 + .../ToolCategory/PropertyInfoExtension.cs | 390 ++++++++++++ EasyTool.Core/ToolCategory/ReflectUtil.cs | 18 + EasyTool.Core/ToolCategory/RegexUtil.cs | 18 +- EasyTool.Core/ToolCategory/RuntimeUtil.cs | 4 + EasyTool.Core/ToolCategory/StrExtension.cs | 318 +++++++++- EasyTool.Core/ToolCategory/StrUtil.cs | 20 + .../ToolCategory/StringBuilderExtension.cs | 417 +++++++++++++ .../ToolCategory/StringComparisonExtension.cs | 403 ++++++++++++ EasyTool.Core/ToolCategory/TaskExtension.cs | 381 ++++++++++++ EasyTool.Core/ToolCategory/TypeExtension.cs | 432 +++++++++++++ EasyTool.Core/ToolCategory/TypeUtil.cs | 28 + EasyTool.Core/ToolCategory/URLUtil.cs | 8 + .../DevelopmentCategory/BuildDtoToTS.cs | 100 ++- 64 files changed, 8490 insertions(+), 111 deletions(-) create mode 100644 .idea/.idea.EasyTool/.idea/.gitignore create mode 100644 .idea/.idea.EasyTool/.idea/encodings.xml create mode 100644 .idea/.idea.EasyTool/.idea/indexLayout.xml create mode 100644 .idea/.idea.EasyTool/.idea/vcs.xml create mode 100644 EasyTool.Core/CollectionsCategory/ArrayExtension.cs create mode 100644 EasyTool.Core/ConvertCategory/ByteExtension.cs rename EasyTool.Core/ConvertCategory/{Extension.Convert.cs => ConvertExtension.cs} (95%) create mode 100644 EasyTool.Core/ConvertCategory/NumberExtension.cs create mode 100644 EasyTool.Core/IOCategory/FileSystemExtension.cs create mode 100644 EasyTool.Core/IOCategory/StreamExtension.cs create mode 100644 EasyTool.Core/ToolCategory/ColorExtension.cs create mode 100644 EasyTool.Core/ToolCategory/DelegateExtension.cs create mode 100644 EasyTool.Core/ToolCategory/EnumExtension.cs create mode 100644 EasyTool.Core/ToolCategory/ExceptionExtension.cs create mode 100644 EasyTool.Core/ToolCategory/GuidExtension.cs create mode 100644 EasyTool.Core/ToolCategory/ObjectExtension.cs create mode 100644 EasyTool.Core/ToolCategory/PropertyInfoExtension.cs create mode 100644 EasyTool.Core/ToolCategory/StringBuilderExtension.cs create mode 100644 EasyTool.Core/ToolCategory/StringComparisonExtension.cs create mode 100644 EasyTool.Core/ToolCategory/TaskExtension.cs create mode 100644 EasyTool.Core/ToolCategory/TypeExtension.cs diff --git a/.idea/.idea.EasyTool/.idea/.gitignore b/.idea/.idea.EasyTool/.idea/.gitignore new file mode 100644 index 0000000..2e97252 --- /dev/null +++ b/.idea/.idea.EasyTool/.idea/.gitignore @@ -0,0 +1,15 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# Rider 忽略的文件 +/projectSettingsUpdater.xml +/.idea.EasyTool.iml +/contentModel.xml +/modules.xml +# 已忽略包含查询文件的默认文件夹 +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ diff --git a/.idea/.idea.EasyTool/.idea/encodings.xml b/.idea/.idea.EasyTool/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.EasyTool/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.EasyTool/.idea/indexLayout.xml b/.idea/.idea.EasyTool/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.EasyTool/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.EasyTool/.idea/vcs.xml b/.idea/.idea.EasyTool/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.EasyTool/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/EasyTool.Core/CloneCategory/CloneExtension.cs b/EasyTool.Core/CloneCategory/CloneExtension.cs index 43f892d..aa32c1a 100644 --- a/EasyTool.Core/CloneCategory/CloneExtension.cs +++ b/EasyTool.Core/CloneCategory/CloneExtension.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; @@ -7,6 +7,6 @@ namespace EasyTool.Extension public static class CloneExtension { //定义一个泛型方法,接受一个泛型参数 T,并返回一个 T 类型的对象 - public static T Clone(this T obj)=> CloneUtil.Clone(obj); + public static T? Clone(this T? obj) => CloneUtil.Clone(obj); } } diff --git a/EasyTool.Core/CloneCategory/CloneUtil.cs b/EasyTool.Core/CloneCategory/CloneUtil.cs index 646e98e..7483124 100644 --- a/EasyTool.Core/CloneCategory/CloneUtil.cs +++ b/EasyTool.Core/CloneCategory/CloneUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; @@ -12,7 +12,7 @@ namespace EasyTool public static class CloneUtil { // 定义一个泛型方法,接受一个泛型参数 T,并返回一个 T 类型的对象 - public static T Clone(T obj) + public static T? Clone(T? obj) { // 检查类型是否可序列化 if (!typeof(T).IsSerializable) @@ -23,7 +23,7 @@ public static T Clone(T obj) // 如果对象为 null,则返回 null if (ReferenceEquals(obj, null)) { - return default(T); + return default; } // 创建一个二进制序列化器 @@ -39,7 +39,7 @@ public static T Clone(T obj) stream.Seek(0, SeekOrigin.Begin); // 使用反序列化从内存流中读取并返回克隆的对象 - return (T)formatter.Deserialize(stream); + return (T)formatter.Deserialize(stream)!; } } @@ -50,7 +50,7 @@ public static T Clone(T obj) /// /// /// - public static async Task CloneAsync(T obj) + public static async Task CloneAsync(T? obj) { // 检查类型是否可序列化 if (!typeof(T).IsSerializable) @@ -61,7 +61,7 @@ public static async Task CloneAsync(T obj) // 如果对象为 null,则返回 null if (ReferenceEquals(obj, null)) { - return default(T); + return default; } // 创建一个二进制序列化器 @@ -77,7 +77,7 @@ public static async Task CloneAsync(T obj) stream.Seek(0, SeekOrigin.Begin); // 使用反序列化从内存流中读取并返回克隆的对象 - return (T)formatter.Deserialize(stream); + return await Task.FromResult((T?)formatter.Deserialize(stream)); } } } diff --git a/EasyTool.Core/CodeCategory/Base64Util.cs b/EasyTool.Core/CodeCategory/Base64Util.cs index 7a84f8c..20098c5 100644 --- a/EasyTool.Core/CodeCategory/Base64Util.cs +++ b/EasyTool.Core/CodeCategory/Base64Util.cs @@ -18,9 +18,11 @@ public static class Base64Util /// /// 将给定的字节数组转换为 Base64 编码字符串。 + /// [Obsolete("请直接使用 Convert.ToBase64String(bytes)")] /// /// 要转换的字节数组 /// 转换后的 Base64 编码字符串 + [Obsolete("请直接使用 Convert.ToBase64String(bytes)", false)] public static string Encode(byte[] bytes) { if (bytes == null) @@ -61,9 +63,11 @@ public static string Encode(byte[] bytes) /// /// 将给定的 Base64 编码字符串转换为字节数组。 + /// [Obsolete("请直接使用 Convert.FromBase64String(str)")] /// /// 要转换的 Base64 编码字符串 /// 转换后的字节数组 + [Obsolete("请直接使用 Convert.FromBase64String(str)", false)] public static byte[] Decode(string str) { if (string.IsNullOrEmpty(str)) diff --git a/EasyTool.Core/CollectionsCategory/ArrayExtension.cs b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs new file mode 100644 index 0000000..f482e24 --- /dev/null +++ b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs @@ -0,0 +1,430 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace EasyTool.Extension +{ + /// + /// 数组扩展方法 + /// + public static class ArrayExtension + { + #region 空值判断 + + /// + /// 判断数组是否为空或 null + /// + public static bool IsEmpty(this T[]? array) + { + return array == null || array.Length == 0; + } + + /// + /// 判断数组是否非空 + /// + public static bool IsNotEmpty(this T[]? array) + { + return array != null && array.Length > 0; + } + + #endregion + + #region 数组操作 + + /// + /// 随机打乱数组顺序(Fisher-Yates 洗牌算法) + /// + public static T[]? Shuffle(this T[]? array) + { + if (array == null || array.Length < 2) + return array; + + var random = new Random(); + var result = (T[])array.Clone(); + + for (int i = result.Length - 1; i > 0; i--) + { + int j = random.Next(i + 1); + (result[i], result[j]) = (result[j], result[i]); + } + + return result; + } + + /// + /// 将数组分割成指定大小的块 + /// + /// 原始数组 + /// 每块的大小 + public static IEnumerable Chunk(this T[]? array, int chunkSize) + { + if (array == null) + yield break; + + if (chunkSize <= 0) + throw new ArgumentException("chunkSize must be greater than 0", nameof(chunkSize)); + + for (int i = 0; i < array.Length; i += chunkSize) + { + int remaining = array.Length - i; + int size = Math.Min(chunkSize, remaining); + var chunk = new T[size]; + Array.Copy(array, i, chunk, 0, size); + yield return chunk; + } + } + + /// + /// 将数组的元素连接成字符串 + /// [Obsolete("请直接使用 string.Join(separator, array)")] + /// + /// 数组 + /// 分隔符,默认为逗号 + [Obsolete("请直接使用 string.Join(separator, array)", false)] + public static string Join(this T[]? array, string separator = ",") + { + if (array == null || array.Length == 0) + return string.Empty; + + return string.Join(separator, array); + } + + /// + /// 清除数组中的重复元素 + /// [Obsolete("请直接使用 array.Distinct().ToArray() (LINQ)")] + /// + [Obsolete("请直接使用 array.Distinct().ToArray() (LINQ)", false)] + public static T[]? Distinct(this T[]? array) + { + if (array == null) + return null; + + return array.Distinct().ToArray(); + } + + /// + /// 按指定键清除数组中的重复元素 + /// + public static T[]? DistinctBy(this T[]? array, Func keySelector) + { + if (array == null) + return null; + + return array.GroupBy(keySelector).Select(g => g.First()).ToArray(); + } + + /// + /// 将数组元素拼接成字符串(支持格式化) + /// + /// 数组 + /// 分隔符 + /// 格式化字符串 + public static string JoinFormat(this T[]? array, string separator, string format) + { + if (array == null || array.Length == 0) + return string.Empty; + + var formatted = array.Select(item => string.Format(format, item)); + return string.Join(separator, formatted); + } + + #endregion + + #region 数组查找 + + /// + /// 查找数组中满足条件的第一个元素的索引 + /// [Obsolete("请直接使用 Array.FindIndex(array, predicate)")] + /// + [Obsolete("请直接使用 Array.FindIndex(array, predicate)", false)] + public static int FindIndex(this T[]? array, Predicate predicate) + { + if (array == null) + return -1; + + return Array.FindIndex(array, predicate); + } + + /// + /// 查找数组中满足条件的所有元素的索引 + /// + public static int[] FindAllIndexes(this T[]? array, Func predicate) + { + if (array == null) + return Array.Empty(); + + var indexes = new List(); + for (int i = 0; i < array.Length; i++) + { + if (predicate(array[i])) + { + indexes.Add(i); + } + } + return indexes.ToArray(); + } + + /// + /// 判断数组是否包含指定元素(使用自定义比较器) + /// + public static bool Contains(this T[]? array, T value, IEqualityComparer comparer) + { + if (array == null) + return false; + + return Array.Exists(array, item => comparer.Equals(item, value)); + } + + #endregion + + #region 数组转换 + + /// + /// 将数组转换为 HashSet + /// [Obsolete("请直接使用 new HashSet(array)")] + /// + [Obsolete("请直接使用 new HashSet(array)", false)] + public static HashSet ToHashSet(this T[]? array) + { + if (array == null) + return new HashSet(); + + return new HashSet(array); + } + + /// + /// 将数组转换为 Queue + /// [Obsolete("请直接使用 new Queue(array)")] + /// + [Obsolete("请直接使用 new Queue(array)", false)] + public static Queue ToQueue(this T[]? array) + { + if (array == null) + return new Queue(); + + return new Queue(array); + } + + /// + /// 将数组转换为 Stack + /// [Obsolete("请直接使用 new Stack(array)")] + /// + [Obsolete("请直接使用 new Stack(array)", false)] + public static Stack ToStack(this T[]? array) + { + if (array == null) + return new Stack(); + + return new Stack(array); + } + + /// + /// 将数组转换为 LinkedList + /// [Obsolete("请直接使用 new LinkedList(array)")] + /// + [Obsolete("请直接使用 new LinkedList(array)", false)] + public static LinkedList ToLinkedList(this T[]? array) + { + if (array == null) + return new LinkedList(); + + return new LinkedList(array); + } + + /// + /// 将二维数组展平为一维数组 + /// + public static T[]? Flatten(this T[,]? array) + { + if (array == null) + return null; + + int width = array.GetLength(0); + int height = array.GetLength(1); + var result = new T[width * height]; + + int index = 0; + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + result[index++] = array[i, j]; + } + } + + return result; + } + + #endregion + + #region 数组切片 + + /// + /// 获取数组中指定范围的元素 + /// + /// 数组 + /// 起始索引 + /// 长度 + public static T[]? Slice(this T[]? array, int startIndex, int length) + { + if (array == null) + return null; + + if (startIndex < 0 || startIndex >= array.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex)); + + if (length < 0 || startIndex + length > array.Length) + throw new ArgumentOutOfRangeException(nameof(length)); + + var result = new T[length]; + Array.Copy(array, startIndex, result, 0, length); + return result; + } + + /// + /// 获取数组从指定索引开始到末尾的元素 + /// + public static T[]? Slice(this T[]? array, int startIndex) + { + if (array == null) + return null; + + if (startIndex < 0) + startIndex = 0; + + if (startIndex >= array.Length) + return Array.Empty(); + + int length = array.Length - startIndex; + return Slice(array, startIndex, length); + } + + #endregion + + #region 数组合并 + + /// + /// 合并多个数组 + /// + public static T[] Merge(params T[][]? arrays) + { + if (arrays == null || arrays.Length == 0) + return Array.Empty(); + + int totalLength = 0; + foreach (var array in arrays) + { + if (array != null) + totalLength += array.Length; + } + + var result = new T[totalLength]; + int offset = 0; + + foreach (var array in arrays) + { + if (array != null && array.Length > 0) + { + Array.Copy(array, 0, result, offset, array.Length); + offset += array.Length; + } + } + + return result; + } + + /// + /// 在数组开头添加元素 + /// + public static T[] Prepend(this T[]? array, params T[]? items) + { + if (array == null) + return items ?? Array.Empty(); + + if (items == null || items.Length == 0) + return array; + + var result = new T[array.Length + items.Length]; + Array.Copy(items, 0, result, 0, items.Length); + Array.Copy(array, 0, result, items.Length, array.Length); + return result; + } + + /// + /// 在数组末尾添加元素 + /// + public static T[] Append(this T[]? array, params T[]? items) + { + if (array == null) + return items ?? Array.Empty(); + + if (items == null || items.Length == 0) + return array; + + var result = new T[array.Length + items.Length]; + Array.Copy(array, 0, result, 0, array.Length); + Array.Copy(items, 0, result, array.Length, items.Length); + return result; + } + + #endregion + + #region 数组遍历 + + /// + /// 遍历数组并对每个元素执行指定操作 + /// [Obsolete("请直接使用 Array.ForEach(array, action) 或 foreach 循环")] + /// + [Obsolete("请直接使用 Array.ForEach(array, action) 或 foreach 循环", false)] + public static void ForEach(this T[]? array, Action action) + { + if (array == null || action == null) + return; + + foreach (var item in array) + { + action(item); + } + } + + /// + /// 遍历数组并对每个元素及其索引执行指定操作 + /// + public static void ForEach(this T[]? array, Action action) + { + if (array == null || action == null) + return; + + for (int i = 0; i < array.Length; i++) + { + action(array[i], i); + } + } + + #endregion + + #region 数组统计 + + /// + /// 统计数组中满足条件的元素数量 + /// [Obsolete("请直接使用 array.Count(predicate) (LINQ)")] + /// + [Obsolete("请直接使用 array.Count(predicate) (LINQ)", false)] + public static int Count(this T[]? array, Func predicate) + { + if (array == null) + return 0; + + int count = 0; + foreach (var item in array) + { + if (predicate(item)) + count++; + } + return count; + } + + #endregion + } +} diff --git a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs index e681ef2..eaaa2ea 100644 --- a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs +++ b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs @@ -44,9 +44,11 @@ public static void AddRange(this IDictionary destina /// /// 返回字典中键的集合 + /// [Obsolete("请直接使用 dictionary.Keys")] /// /// 要获取键的字典 /// 字典中所有键的集合 + [Obsolete("请直接使用 dictionary.Keys", false)] public static IEnumerable GetKeys(this IDictionary dictionary) { return dictionary.Keys; @@ -54,9 +56,11 @@ public static IEnumerable GetKeys(this IDictionary /// 返回字典中值的集合 + /// [Obsolete("请直接使用 dictionary.Values")] /// /// 要获取值的字典 /// 字典中所有值的集合 + [Obsolete("请直接使用 dictionary.Values", false)] public static IEnumerable GetValues(this IDictionary dictionary) { return dictionary.Values; diff --git a/EasyTool.Core/CollectionsCategory/IteratorUtil.cs b/EasyTool.Core/CollectionsCategory/IteratorUtil.cs index b707296..f4aa6e6 100644 --- a/EasyTool.Core/CollectionsCategory/IteratorUtil.cs +++ b/EasyTool.Core/CollectionsCategory/IteratorUtil.cs @@ -5,9 +5,12 @@ namespace EasyTool { - //TODO:疑问,这些功能Linq支持吗? /// /// 迭代器工具类 + /// + /// 注意:此类中的方法与 System.Linq 提供的功能高度相似。 + /// 对于新代码,建议优先使用 LINQ 标准查询运算符(如 Where、Select、Take、Skip、OrderBy、GroupBy 等)。 + /// 此类保留用于向后兼容和特定场景需求。 /// public static class IteratorUtil { @@ -24,7 +27,9 @@ public static IEnumerable AsIterator(this T[] array) /// /// 过滤掉一个迭代器中不符合条件的元素 + /// [Obsolete("请直接使用 source.Where(predicate) (LINQ)")] /// + [Obsolete("请直接使用 source.Where(predicate) (LINQ)", false)] public static IEnumerable Filter(this IEnumerable source, Func predicate) { foreach (var item in source) @@ -38,7 +43,9 @@ public static IEnumerable Filter(this IEnumerable source, Func /// /// 对一个迭代器中的每个元素进行转换 + /// [Obsolete("请直接使用 source.Select(selector) (LINQ)")] /// + [Obsolete("请直接使用 source.Select(selector) (LINQ)", false)] public static IEnumerable Map(this IEnumerable source, Func selector) { foreach (var item in source) @@ -49,7 +56,9 @@ public static IEnumerable Map(this IEnumerable /// 从一个迭代器中取出前 n 个元素 + /// [Obsolete("请直接使用 source.Take(count) (LINQ)")] /// + [Obsolete("请直接使用 source.Take(count) (LINQ)", false)] public static IEnumerable Take(this IEnumerable source, int count) { foreach (var item in source) @@ -67,7 +76,9 @@ public static IEnumerable Take(this IEnumerable source, int count) /// /// 跳过一个迭代器中的前 n 个元素 + /// [Obsolete("请直接使用 source.Skip(count) (LINQ)")] /// + [Obsolete("请直接使用 source.Skip(count) (LINQ)", false)] public static IEnumerable Skip(this IEnumerable source, int count) { foreach (var item in source) @@ -85,7 +96,9 @@ public static IEnumerable Skip(this IEnumerable source, int count) /// /// 将一个迭代器的元素分组 + /// [Obsolete("请直接使用 source.GroupBy(keySelector, x => x) (LINQ)")] /// + [Obsolete("请直接使用 source.GroupBy(keySelector, x => x) (LINQ)", false)] public static IEnumerable> GroupBy(this IEnumerable source, Func keySelector) { return source.GroupBy(keySelector, x => x); @@ -137,7 +150,9 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() /// /// 对一个迭代器中的元素进行排序 + /// [Obsolete("请直接使用 source.OrderBy(keySelector) (LINQ)")] /// + [Obsolete("请直接使用 source.OrderBy(keySelector) (LINQ)", false)] public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector) { return source.OrderBy(keySelector, Comparer.Default); @@ -161,7 +176,9 @@ public static IOrderedEnumerable OrderBy(this IEnumerabl /// /// 对一个迭代器中的元素进行倒序排序 + /// [Obsolete("请直接使用 source.OrderByDescending(keySelector) (LINQ)")] /// + [Obsolete("请直接使用 source.OrderByDescending(keySelector) (LINQ)", false)] public static IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector) { return source.OrderByDescending(keySelector, Comparer.Default); diff --git a/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs b/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs index d3893db..a27bd80 100644 --- a/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs +++ b/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs @@ -11,10 +11,12 @@ public class LinkedListUtil { /// /// 将指定元素添加到双向链表的结尾处。 + /// [Obsolete("请直接使用 list.AddLast(item)")] /// /// 双向链表元素类型 /// 双向链表 /// 要添加的元素 + [Obsolete("请直接使用 list.AddLast(item)", false)] public static void AddLast(LinkedList list, T item) { list.AddLast(item); @@ -22,10 +24,12 @@ public static void AddLast(LinkedList list, T item) /// /// 将指定元素添加到双向链表的开头处。 + /// [Obsolete("请直接使用 list.AddFirst(item)")] /// /// 双向链表元素类型 /// 双向链表 /// 要添加的元素 + [Obsolete("请直接使用 list.AddFirst(item)", false)] public static void AddFirst(LinkedList list, T item) { list.AddFirst(item); @@ -33,12 +37,14 @@ public static void AddFirst(LinkedList list, T item) /// /// 将指定元素插入到双向链表中的指定位置之前。 + /// [Obsolete("请直接使用 list.AddBefore(node, item)")] /// /// 双向链表元素类型 /// 双向链表 /// 要在其前面插入新元素的节点 /// 要添加的元素 /// 新节点 + [Obsolete("请直接使用 list.AddBefore(node, item)", false)] public static LinkedListNode AddBefore(LinkedList list, LinkedListNode node, T item) { return list.AddBefore(node, item); @@ -46,12 +52,14 @@ public static LinkedListNode AddBefore(LinkedList list, LinkedListNode< /// /// 将指定元素插入到双向链表中的指定位置之后。 + /// [Obsolete("请直接使用 list.AddAfter(node, item)")] /// /// 双向链表元素类型 /// 双向链表 /// 要在其后面插入新元素的节点 /// 要添加的元素 /// 新节点 + [Obsolete("请直接使用 list.AddAfter(node, item)", false)] public static LinkedListNode AddAfter(LinkedList list, LinkedListNode node, T item) { return list.AddAfter(node, item); @@ -84,10 +92,12 @@ public static void MoveFirst(LinkedList list, LinkedListNode node) /// /// 从双向链表中移除指定节点。 + /// [Obsolete("请直接使用 list.Remove(node)")] /// /// 双向链表元素类型 /// 双向链表 /// 要移除的节点 + [Obsolete("请直接使用 list.Remove(node)", false)] public static void Remove(LinkedList list, LinkedListNode node) { list.Remove(node); @@ -95,11 +105,13 @@ public static void Remove(LinkedList list, LinkedListNode node) /// /// 从双向链表中移除指定值的第一个匹配项。 + /// [Obsolete("请直接使用 list.Remove(item)")] /// /// 双向链表元素类型 /// 双向链表 /// 要移除的元素 /// 如果成功移除了元素,则为 true;否则为 false。 + [Obsolete("请直接使用 list.Remove(item)", false)] public static bool Remove(LinkedList list, T item) { return list.Remove(item); @@ -107,11 +119,13 @@ public static bool Remove(LinkedList list, T item) /// /// 确定双向链表中是否包含特定值。 + /// [Obsolete("请直接使用 list.Contains(item)")] /// /// 双向链表元素类型 /// 双向链表 /// 要在双向链表中查找的元素 /// 如果在双向链表中找到了 item,则为 true;否则为 false。 + [Obsolete("请直接使用 list.Contains(item)", false)] public static bool Contains(LinkedList list, T item) { return list.Contains(item); @@ -119,9 +133,11 @@ public static bool Contains(LinkedList list, T item) /// /// 从双向链表中移除所有节点。 + /// [Obsolete("请直接使用 list.Clear()")] /// /// 双向链表元素类型 /// 双向链表 + [Obsolete("请直接使用 list.Clear()", false)] public static void Clear(LinkedList list) { list.Clear(); diff --git a/EasyTool.Core/CollectionsCategory/ListUtil.cs b/EasyTool.Core/CollectionsCategory/ListUtil.cs index 916f896..cb49685 100644 --- a/EasyTool.Core/CollectionsCategory/ListUtil.cs +++ b/EasyTool.Core/CollectionsCategory/ListUtil.cs @@ -9,11 +9,13 @@ public class ListUtil { /// /// 在列表中查找元素,并返回其索引。如果未找到,则返回 -1。 + /// [Obsolete("请直接使用 list.IndexOf(item)")] /// /// 列表元素类型 /// 要查找的列表 /// 要查找的元素 /// 元素在列表中的索引,如果未找到则返回 -1 + [Obsolete("请直接使用 list.IndexOf(item)", false)] public static int IndexOf(List list, T item) { return list.IndexOf(item); @@ -21,10 +23,12 @@ public static int IndexOf(List list, T item) /// /// 向列表中添加多个元素。 + /// [Obsolete("请直接使用 list.AddRange(items)")] /// /// 列表元素类型 /// 要添加元素的列表 /// 要添加到列表中的元素 + [Obsolete("请直接使用 list.AddRange(items)", false)] public static void AddRange(List list, IEnumerable items) { list.AddRange(items); @@ -32,10 +36,12 @@ public static void AddRange(List list, IEnumerable items) /// /// 在列表中删除指定索引处的元素。 + /// [Obsolete("请直接使用 list.RemoveAt(index)")] /// /// 列表元素类型 /// 要删除元素的列表 /// 要删除元素的索引 + [Obsolete("请直接使用 list.RemoveAt(index)", false)] public static void RemoveAt(List list, int index) { list.RemoveAt(index); @@ -43,11 +49,13 @@ public static void RemoveAt(List list, int index) /// /// 从列表中删除指定元素的第一个匹配项。 + /// [Obsolete("请直接使用 list.Remove(item)")] /// /// 列表元素类型 /// 要删除元素的列表 /// 要删除的元素 /// 如果找到并成功删除元素,则返回 true;否则返回 false + [Obsolete("请直接使用 list.Remove(item)", false)] public static bool Remove(List list, T item) { return list.Remove(item); @@ -77,10 +85,12 @@ public static List Concat(params List[] lists) /// /// 返回一个新的列表,其中包含指定列表中的元素,但不包括重复元素。 + /// [Obsolete("请直接使用 list.Distinct().ToList() (LINQ)")] /// /// 列表元素类型 /// 要去重的列表 /// 去重后的新列表 + [Obsolete("请直接使用 list.Distinct().ToList() (LINQ)", false)] public static List Distinct(List list) { return list.Distinct().ToList(); @@ -88,11 +98,13 @@ public static List Distinct(List list) /// /// 根据指定的条件筛选出列表中符合条件的元素。 + /// [Obsolete("请直接使用 list.Where(predicate).ToList() (LINQ)")] /// /// 列表元素类型 /// 要筛选的列表 /// 筛选条件 /// 符合条件的元素列表 + [Obsolete("请直接使用 list.Where(predicate).ToList() (LINQ)", false)] public static List Where(List list, Func predicate) { return list.Where(predicate).ToList(); @@ -100,12 +112,14 @@ public static List Where(List list, Func predicate) /// /// 将列表中的每个元素应用到指定的转换函数,并返回转换后的新列表。 + /// [Obsolete("请直接使用 list.Select(selector).ToList() (LINQ)")] /// /// 列表元素类型 /// 转换后的元素类型 /// 要转换的列表 /// 转换函数 /// 转换后的新列表 + [Obsolete("请直接使用 list.Select(selector).ToList() (LINQ)", false)] public static List Select(List list, Func selector) { return list.Select(selector).ToList(); @@ -113,10 +127,12 @@ public static List Select(List list, Func /// 对列表中的每个元素应用指定的操作。 + /// [Obsolete("请直接使用 list.ForEach(action)")] /// /// 列表元素类型 /// 要应用操作的列表 /// 要应用的操作 + [Obsolete("请直接使用 list.ForEach(action)", false)] public static void ForEach(List list, Action action) { list.ForEach(action); @@ -124,9 +140,11 @@ public static void ForEach(List list, Action action) /// /// 将列表中的元素排序。 + /// [Obsolete("请直接使用 list.Sort()")] /// /// 列表元素类型 /// 要排序的列表 + [Obsolete("请直接使用 list.Sort()", false)] public static void Sort(List list) { list.Sort(); @@ -134,10 +152,12 @@ public static void Sort(List list) /// /// 将列表中的元素按指定的比较器排序。 + /// [Obsolete("请直接使用 list.Sort(comparer)")] /// /// 列表元素类型 /// 要排序的列表 /// 比较器 + [Obsolete("请直接使用 list.Sort(comparer)", false)] public static void Sort(List list, IComparer comparer) { list.Sort(comparer); @@ -160,10 +180,12 @@ public static List Page(List list, int pageSize, int pageIndex) /// /// 向列表中批量添加元素。 + /// [Obsolete("请直接使用 list.AddRange(items)")] /// /// 列表元素类型 /// 要添加元素的列表 /// 要添加到列表中的元素 + [Obsolete("请直接使用 list.AddRange(items)", false)] public static void AddRange(List list, params T[] items) { list.AddRange(items); @@ -194,7 +216,7 @@ public static bool Equals(List list1, List list2) { for (int i = 0; i < list1.Count; i++) { - if (!list1[i].Equals(list2[i])) + if (!EqualityComparer.Default.Equals(list1[i], list2[i])) { return false; } @@ -206,11 +228,13 @@ public static bool Equals(List list1, List list2) /// /// 返回两个列表的交集。 + /// [Obsolete("请直接使用 list1.Intersect(list2).ToList() (LINQ)")] /// /// 列表元素类型 /// 要比较的第一个列表 /// 要比较的第二个列表 /// 交集列表 + [Obsolete("请直接使用 list1.Intersect(list2).ToList() (LINQ)", false)] public static List Intersect(List list1, List list2) { return list1.Intersect(list2).ToList(); @@ -218,11 +242,13 @@ public static List Intersect(List list1, List list2) /// /// 返回两个列表的并集。 + /// [Obsolete("请直接使用 list1.Union(list2).ToList() (LINQ)")] /// /// 列表元素类型 /// 要比较的第一个列表 /// 要比较的第二个列表 /// 并集列表 + [Obsolete("请直接使用 list1.Union(list2).ToList() (LINQ)", false)] public static List Union(List list1, List list2) { return list1.Union(list2).ToList(); @@ -230,11 +256,13 @@ public static List Union(List list1, List list2) /// /// 返回两个列表的差集。 + /// [Obsolete("请直接使用 list1.Except(list2).ToList() (LINQ)")] /// /// 列表元素类型 /// 要比较的第一个列表 /// 要比较的第二个列表 /// 差集列表 + [Obsolete("请直接使用 list1.Except(list2).ToList() (LINQ)", false)] public static List Except(List list1, List list2) { return list1.Except(list2).ToList(); diff --git a/EasyTool.Core/CollectionsCategory/QueueUtil.cs b/EasyTool.Core/CollectionsCategory/QueueUtil.cs index 8c87309..d1f45e9 100644 --- a/EasyTool.Core/CollectionsCategory/QueueUtil.cs +++ b/EasyTool.Core/CollectionsCategory/QueueUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,10 +12,12 @@ public class QueueUtil { /// /// 将指定元素添加到队列的末尾。 + /// [Obsolete("请直接使用 queue.Enqueue(item)")] /// /// 队列元素类型 /// 队列 /// 要添加的元素 + [Obsolete("请直接使用 queue.Enqueue(item)", false)] public static void Enqueue(Queue queue, T item) { queue.Enqueue(item); @@ -37,11 +39,13 @@ public static void EnqueueRange(Queue queue, IEnumerable collection) /// /// 移除并返回位于队列开头的元素。 + /// [Obsolete("请直接使用 queue.Dequeue()")] /// /// 队列元素类型 /// 队列 /// 队列开头的元素 /// 队列为空时引发异常 + [Obsolete("请直接使用 queue.Dequeue()", false)] public static T Dequeue(Queue queue) { return queue.Dequeue(); @@ -49,11 +53,13 @@ public static T Dequeue(Queue queue) /// /// 返回位于队列开头的元素而不将其移除。 + /// [Obsolete("请直接使用 queue.Peek()")] /// /// 队列元素类型 /// 队列 /// 队列开头的元素 /// 队列为空时引发异常 + [Obsolete("请直接使用 queue.Peek()", false)] public static T Peek(Queue queue) { return queue.Peek(); @@ -61,11 +67,13 @@ public static T Peek(Queue queue) /// /// 确定队列中是否包含指定元素。 + /// [Obsolete("请直接使用 queue.Contains(item)")] /// /// 队列元素类型 /// 队列 /// 要查找的元素 /// 如果队列包含指定元素,则为 true;否则为 false。 + [Obsolete("请直接使用 queue.Contains(item)", false)] public static bool Contains(Queue queue, T item) { return queue.Contains(item); @@ -82,7 +90,12 @@ public static bool Remove(Queue queue, T item) { if (queue.Contains(item)) { - queue = new Queue(queue.Where(x => !x.Equals(item))); + var newQueue = new Queue(queue.Where(x => !Equals(x, item))); + queue.Clear(); + foreach (var element in newQueue) + { + queue.Enqueue(element); + } return true; } return false; @@ -90,10 +103,12 @@ public static bool Remove(Queue queue, T item) /// /// 将队列中的所有元素复制到新数组中。 + /// [Obsolete("请直接使用 queue.ToArray()")] /// /// 队列元素类型 /// 队列 /// 包含队列中所有元素的新数组 + [Obsolete("请直接使用 queue.ToArray()", false)] public static T[] ToArray(Queue queue) { return queue.ToArray(); @@ -101,11 +116,13 @@ public static T[] ToArray(Queue queue) /// /// 将队列中的所有元素复制到新数组中,从指定的索引开始。 + /// [Obsolete("请直接使用 queue.CopyTo(array, arrayIndex)")] /// /// 队列元素类型 /// 队列 /// 要复制到的目标数组 /// 目标数组的起始索引 + [Obsolete("请直接使用 queue.CopyTo(array, arrayIndex)", false)] public static void CopyTo(Queue queue, T[] array, int arrayIndex) { queue.CopyTo(array, arrayIndex); @@ -113,9 +130,11 @@ public static void CopyTo(Queue queue, T[] array, int arrayIndex) /// /// 从队列中移除所有元素。 + /// [Obsolete("请直接使用 queue.Clear()")] /// /// 队列元素类型 /// 队列 + [Obsolete("请直接使用 queue.Clear()", false)] public static void Clear(Queue queue) { queue.Clear(); diff --git a/EasyTool.Core/CollectionsCategory/StackUtil.cs b/EasyTool.Core/CollectionsCategory/StackUtil.cs index 232e5c8..5a0cd9c 100644 --- a/EasyTool.Core/CollectionsCategory/StackUtil.cs +++ b/EasyTool.Core/CollectionsCategory/StackUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,10 +12,12 @@ public class StackUtil { /// /// 将指定元素推入堆栈的顶部。 + /// [Obsolete("请直接使用 stack.Push(item)")] /// /// 堆栈元素类型 /// 堆栈 /// 要添加的元素 + [Obsolete("请直接使用 stack.Push(item)", false)] public static void Push(Stack stack, T item) { stack.Push(item); @@ -23,11 +25,13 @@ public static void Push(Stack stack, T item) /// /// 从堆栈的顶部移除并返回对象。 + /// [Obsolete("请直接使用 stack.Pop()")] /// /// 堆栈元素类型 /// 堆栈 /// 堆栈顶部的元素 /// 堆栈为空时引发异常 + [Obsolete("请直接使用 stack.Pop()", false)] public static T Pop(Stack stack) { return stack.Pop(); @@ -35,11 +39,13 @@ public static T Pop(Stack stack) /// /// 返回位于堆栈顶部的对象但不将其移除。 + /// [Obsolete("请直接使用 stack.Peek()")] /// /// 堆栈元素类型 /// 堆栈 /// 堆栈顶部的元素 /// 堆栈为空时引发异常 + [Obsolete("请直接使用 stack.Peek()", false)] public static T Peek(Stack stack) { return stack.Peek(); @@ -47,11 +53,13 @@ public static T Peek(Stack stack) /// /// 确定堆栈是否包含指定元素。 + /// [Obsolete("请直接使用 stack.Contains(item)")] /// /// 堆栈元素类型 /// 堆栈 /// 要查找的元素 /// 如果堆栈包含指定元素,则为 true;否则为 false。 + [Obsolete("请直接使用 stack.Contains(item)", false)] public static bool Contains(Stack stack, T item) { return stack.Contains(item); @@ -68,7 +76,12 @@ public static bool Remove(Stack stack, T item) { if (stack.Contains(item)) { - stack = new Stack(stack.Where(x => !x.Equals(item)).Reverse()); + var newStack = new Stack(stack.Where(x => !Equals(x, item)).Reverse()); + stack.Clear(); + foreach (var element in newStack) + { + stack.Push(element); + } return true; } return false; @@ -76,10 +89,12 @@ public static bool Remove(Stack stack, T item) /// /// 将堆栈中的所有元素复制到新数组中。 + /// [Obsolete("请直接使用 stack.ToArray()")] /// /// 堆栈元素类型 /// 堆栈 /// 包含堆栈中所有元素的新数组 + [Obsolete("请直接使用 stack.ToArray()", false)] public static T[] ToArray(Stack stack) { return stack.ToArray(); @@ -87,11 +102,13 @@ public static T[] ToArray(Stack stack) /// /// 将堆栈中的所有元素复制到新数组中,从指定的索引开始。 + /// [Obsolete("请直接使用 stack.CopyTo(array, arrayIndex)")] /// /// 堆栈元素类型 /// 堆栈 /// 要复制到的目标数组 /// 目标数组的起始索引 + [Obsolete("请直接使用 stack.CopyTo(array, arrayIndex)", false)] public static void CopyTo(Stack stack, T[] array, int arrayIndex) { stack.CopyTo(array, arrayIndex); @@ -99,9 +116,11 @@ public static void CopyTo(Stack stack, T[] array, int arrayIndex) /// /// 从堆栈中移除所有元素。 + /// [Obsolete("请直接使用 stack.Clear()")] /// /// 堆栈元素类型 /// 堆栈 + [Obsolete("请直接使用 stack.Clear()", false)] public static void Clear(Stack stack) { stack.Clear(); diff --git a/EasyTool.Core/ConvertCategory/ByteExtension.cs b/EasyTool.Core/ConvertCategory/ByteExtension.cs new file mode 100644 index 0000000..7208906 --- /dev/null +++ b/EasyTool.Core/ConvertCategory/ByteExtension.cs @@ -0,0 +1,584 @@ +using System; +using System.IO; +using System.Text; +using System.Linq; + +namespace EasyTool.Extension +{ + /// + /// Byte 字节扩展方法 + /// + public static class ByteExtension + { + #region 单字节转换 + + /// + /// 将字节转换为16进制字符串 + /// + public static string ToHex(this byte value) + { + return value.ToString("X2"); + } + + /// + /// 将字节转换为16进制字符串(小写) + /// + public static string ToHexLower(this byte value) + { + return value.ToString("x2"); + } + + /// + /// 将字节转换为二进制字符串 + /// + public static string ToBinaryString(this byte value) + { + return Convert.ToString(value, 2).PadLeft(8, '0'); + } + + /// + /// 获取字节的指定位 + /// + public static bool GetBit(this byte value, int index) + { + if (index < 0 || index > 7) + throw new ArgumentOutOfRangeException(nameof(index), "Index must be between 0 and 7"); + + return (value & (1 << index)) != 0; + } + + /// + /// 设置字节的指定位 + /// + public static byte SetBit(this byte value, int index, bool bitValue) + { + if (index < 0 || index > 7) + throw new ArgumentOutOfRangeException(nameof(index), "Index must be between 0 and 7"); + + if (bitValue) + return (byte)(value | (1 << index)); + else + return (byte)(value & ~(1 << index)); + } + + #endregion + + #region 字节数组转换 + + /// + /// 将字节数组转换为16进制字符串 + /// + public static string ToHex(this byte[] bytes) + { + return bytes.ToHex(true); + } + + /// + /// 将字节数组转换为16进制字符串 + /// + public static string ToHex(this byte[] bytes, bool uppercase) + { + if (bytes == null || bytes.Length == 0) + return string.Empty; + + var format = uppercase ? "X2" : "x2"; + var sb = new StringBuilder(bytes.Length * 2); + + foreach (var b in bytes) + { + sb.Append(b.ToString(format)); + } + + return sb.ToString(); + } + + /// + /// 将字节数组转换为16进制字符串(带分隔符) + /// + public static string ToHex(this byte[] bytes, string separator, bool uppercase = true) + { + if (bytes == null || bytes.Length == 0) + return string.Empty; + + var format = uppercase ? "X2" : "x2"; + return string.Join(separator, bytes.Select(b => b.ToString(format))); + } + + /// + /// 从16进制字符串转换为字节数组 + /// + public static byte[] FromHexToBytes(this string hex) + { + if (string.IsNullOrWhiteSpace(hex)) + return Array.Empty(); + + hex = hex.Replace("-", "").Replace(" ", ""); + + if (hex.Length % 2 != 0) + throw new ArgumentException("Hex string must have an even length", nameof(hex)); + + var bytes = new byte[hex.Length / 2]; + + for (int i = 0; i < bytes.Length; i++) + { + bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + } + + return bytes; + } + + /// + /// 将字节数组转换为Base64字符串 + /// [Obsolete("请直接使用 Convert.ToBase64String(bytes)")] + /// + [Obsolete("请直接使用 Convert.ToBase64String(bytes)", false)] + public static string ToBase64(this byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + return string.Empty; + + return Convert.ToBase64String(bytes); + } + + /// + /// 从Base64字符串转换为字节数组 + /// [Obsolete("请直接使用 Convert.FromBase64String(base64)")] + /// + [Obsolete("请直接使用 Convert.FromBase64String(base64)", false)] + public static byte[] FromBase64ToBytes(this string base64) + { + if (string.IsNullOrWhiteSpace(base64)) + return Array.Empty(); + + return Convert.FromBase64String(base64); + } + + /// + /// 将字节数组转换为二进制字符串 + /// + public static string ToBinaryString(this byte[] bytes, string separator = " ") + { + if (bytes == null || bytes.Length == 0) + return string.Empty; + + return string.Join(separator, bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0'))); + } + + /// + /// 从二进制字符串转换为字节数组 + /// + public static byte[] FromBinaryStringToBytes(this string binary) + { + if (string.IsNullOrWhiteSpace(binary)) + return Array.Empty(); + + binary = binary.Replace(" ", ""); + + if (binary.Length % 8 != 0) + throw new ArgumentException("Binary string length must be a multiple of 8", nameof(binary)); + + var bytes = new byte[binary.Length / 8]; + + for (int i = 0; i < bytes.Length; i++) + { + bytes[i] = Convert.ToByte(binary.Substring(i * 8, 8), 2); + } + + return bytes; + } + + #endregion + + #region 字节数组操作 + + /// + /// 反转字节数组 + /// + public static byte[]? Reverse(this byte[]? bytes) + { + if (bytes == null) + return null; + + var result = new byte[bytes.Length]; + Array.Copy(bytes, result, bytes.Length); + Array.Reverse(result); + return result; + } + + /// + /// 字节数组异或运算 + /// + public static byte[] Xor(this byte[] bytes, byte[] key) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + if (key == null) + throw new ArgumentNullException(nameof(key)); + + var result = new byte[bytes.Length]; + + for (int i = 0; i < bytes.Length; i++) + { + result[i] = (byte)(bytes[i] ^ key[i % key.Length]); + } + + return result; + } + + /// + /// 字节数组异或运算(单字节密钥) + /// + public static byte[] Xor(this byte[] bytes, byte key) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + var result = new byte[bytes.Length]; + + for (int i = 0; i < bytes.Length; i++) + { + result[i] = (byte)(bytes[i] ^ key); + } + + return result; + } + + /// + /// 合并多个字节数组 + /// + public static byte[] Combine(params byte[][]? arrays) + { + if (arrays == null || arrays.Length == 0) + return Array.Empty(); + + int totalLength = 0; + foreach (var arr in arrays) + { + if (arr != null) + totalLength += arr.Length; + } + + var result = new byte[totalLength]; + int offset = 0; + + foreach (var arr in arrays) + { + if (arr != null && arr.Length > 0) + { + Array.Copy(arr, 0, result, offset, arr.Length); + offset += arr.Length; + } + } + + return result; + } + + /// + /// 截取字节数组 + /// + public static byte[] SubArray(this byte[] bytes, int startIndex, int length) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + if (startIndex < 0 || startIndex >= bytes.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex)); + + if (length < 0 || startIndex + length > bytes.Length) + throw new ArgumentOutOfRangeException(nameof(length)); + + var result = new byte[length]; + Array.Copy(bytes, startIndex, result, 0, length); + return result; + } + + /// + /// 截取字节数组(从指定位置到末尾) + /// + public static byte[] SubArray(this byte[] bytes, int startIndex) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + if (startIndex < 0) + startIndex = 0; + + if (startIndex >= bytes.Length) + return Array.Empty(); + + return SubArray(bytes, startIndex, bytes.Length - startIndex); + } + + #endregion + + #region 字节数组比较 + + /// + /// 比较两个字节数组是否相等 + /// + public static bool EqualsTo(this byte[]? bytes, byte[]? other) + { + if (ReferenceEquals(bytes, other)) + return true; + + if (bytes == null || other == null) + return false; + + if (bytes.Length != other.Length) + return false; + + for (int i = 0; i < bytes.Length; i++) + { + if (bytes[i] != other[i]) + return false; + } + + return true; + } + + /// + /// 字节数组比较(返回差异索引) + /// + public static int[] Diff(this byte[]? bytes, byte[]? other) + { + if (bytes == null || other == null) + return Array.Empty(); + + var minLength = Math.Min(bytes.Length, other.Length); + var diffs = new System.Collections.Generic.List(); + + for (int i = 0; i < minLength; i++) + { + if (bytes[i] != other[i]) + diffs.Add(i); + } + + return diffs.ToArray(); + } + + #endregion + + #region 字节数组与基本类型转换 + + /// + /// 将字节数组转换为整数(小端序) + /// + public static int ToInt32(this byte[] bytes, int startIndex = 0) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + if (startIndex < 0 || startIndex + 4 > bytes.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex)); + + return bytes[startIndex] | (bytes[startIndex + 1] << 8) | (bytes[startIndex + 2] << 16) | (bytes[startIndex + 3] << 24); + } + + /// + /// 将整数转换为字节数组(小端序) + /// + public static byte[] ToBytes(this int value) + { + return new[] { (byte)(value & 0xFF), (byte)((value >> 8) & 0xFF), (byte)((value >> 16) & 0xFF), (byte)((value >> 24) & 0xFF) }; + } + + /// + /// 将字节数组转换为长整数(小端序) + /// + public static long ToInt64(this byte[] bytes, int startIndex = 0) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + if (startIndex < 0 || startIndex + 8 > bytes.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex)); + + return BitConverter.ToInt64(bytes, startIndex); + } + + /// + /// 将长整数转换为字节数组(小端序) + /// + public static byte[] ToBytes(this long value) + { + return BitConverter.GetBytes(value); + } + + /// + /// 将字节数组转换为短整数(小端序) + /// + public static short ToInt16(this byte[] bytes, int startIndex = 0) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + if (startIndex < 0 || startIndex + 2 > bytes.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex)); + + return (short)(bytes[startIndex] | (bytes[startIndex + 1] << 8)); + } + + /// + /// 将短整数转换为字节数组(小端序) + /// + public static byte[] ToBytes(this short value) + { + return new[] { (byte)(value & 0xFF), (byte)((value >> 8) & 0xFF) }; + } + + #endregion + + #region 字节数组编码解码 + + /// + /// 将字节数组按UTF-8编码转换为字符串 + /// [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)")] + /// + [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)", false)] + public static string ToUtf8String(this byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + return string.Empty; + + return Encoding.UTF8.GetString(bytes); + } + + /// + /// 将字节数组按指定编码转换为字符串 + /// [Obsolete("请直接使用 encoding.GetString(bytes)")] + /// + [Obsolete("请直接使用 encoding.GetString(bytes)", false)] + public static string ToString(this byte[] bytes, Encoding encoding) + { + if (bytes == null || bytes.Length == 0) + return string.Empty; + + encoding ??= Encoding.UTF8; + return encoding.GetString(bytes); + } + + /// + /// 将字符串按UTF-8编码转换为字节数组 + /// [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)")] + /// + [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)", false)] + public static byte[] ToUtf8Bytes(this string str) + { + if (string.IsNullOrEmpty(str)) + return Array.Empty(); + + return Encoding.UTF8.GetBytes(str); + } + + /// + /// 将字符串按指定编码转换为字节数组 + /// [Obsolete("请直接使用 encoding.GetBytes(str)")] + /// + [Obsolete("请直接使用 encoding.GetBytes(str)", false)] + public static byte[] ToBytes(this string str, Encoding encoding) + { + if (string.IsNullOrEmpty(str)) + return Array.Empty(); + + encoding ??= Encoding.UTF8; + return encoding.GetBytes(str); + } + + #endregion + + #region 字节数组压缩解压 + + /// + /// 压缩字节数组(使用 GZip) + /// + public static byte[]? Compress(this byte[]? bytes) + { + if (bytes == null || bytes.Length == 0) + return bytes; + + using var output = new MemoryStream(); + using (var gzip = new System.IO.Compression.GZipStream(output, System.IO.Compression.CompressionMode.Compress)) + { + gzip.Write(bytes, 0, bytes.Length); + } + return output.ToArray(); + } + + /// + /// 解压字节数组(使用 GZip) + /// + public static byte[]? Decompress(this byte[]? bytes) + { + if (bytes == null || bytes.Length == 0) + return bytes; + + using var input = new MemoryStream(bytes); + using var gzip = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress); + using var output = new MemoryStream(); + gzip.CopyTo(output); + return output.ToArray(); + } + + #endregion + + #region 字节数组哈希 + + /// + /// 计算字节数组的 MD5 哈希值 + /// + public static byte[] ToMd5(this byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + return Array.Empty(); + + using var md5 = System.Security.Cryptography.MD5.Create(); + return md5.ComputeHash(bytes); + } + + /// + /// 计算字节数组的 SHA1 哈希值 + /// + public static byte[] ToSha1(this byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + return Array.Empty(); + + using var sha1 = System.Security.Cryptography.SHA1.Create(); + return sha1.ComputeHash(bytes); + } + + /// + /// 计算字节数组的 SHA256 哈希值 + /// + public static byte[] ToSha256(this byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + return Array.Empty(); + + using var sha256 = System.Security.Cryptography.SHA256.Create(); + return sha256.ComputeHash(bytes); + } + + /// + /// 计算字节数组的 CRC32 校验值 + /// + public static uint ToCrc32(this byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + return 0; + + uint crc = 0xFFFFFFFF; + foreach (var b in bytes) + { + crc ^= b; + for (int i = 0; i < 8; i++) + { + crc = (crc >> 1) ^ ((crc & 1) == 1 ? 0xEDB88320 : 0); + } + } + return ~crc; + } + + #endregion + } +} diff --git a/EasyTool.Core/ConvertCategory/Extension.Convert.cs b/EasyTool.Core/ConvertCategory/ConvertExtension.cs similarity index 95% rename from EasyTool.Core/ConvertCategory/Extension.Convert.cs rename to EasyTool.Core/ConvertCategory/ConvertExtension.cs index c2e380c..907c9bf 100644 --- a/EasyTool.Core/ConvertCategory/Extension.Convert.cs +++ b/EasyTool.Core/ConvertCategory/ConvertExtension.cs @@ -5,9 +5,9 @@ namespace EasyTool.ConvertCategory { /// - /// 数据类型转化 + /// 数据类型转化扩展 /// - public static partial class Extension + public static class ConvertExtension { #region ==数据转换扩展== @@ -205,9 +205,9 @@ public static string ToIntString(this bool b) /// /// 布尔值转换为整数1或者0 + /// [Obsolete("请直接使用 Convert.ToInt32(b)")] /// - /// - /// + [Obsolete("请直接使用 Convert.ToInt32(b)", false)] public static int ToInt(this bool b) { return b ? 1 : 0; @@ -270,9 +270,11 @@ public static string ToHex(this byte[] bytes, bool lowerCase = true) /// /// 转换为Base64 + /// [Obsolete("请直接使用 Convert.ToBase64String(bytes)")] /// /// /// + [Obsolete("请直接使用 Convert.ToBase64String(bytes)", false)] public static string ToBase64(this byte[] bytes) { if (bytes == null) @@ -313,10 +315,11 @@ public static DateTime TimestampToDateTime(this string timeStamp) /// /// 字符串转Guid + /// [Obsolete("请直接使用 Guid.TryParse(guid, out var result) 或 new Guid(guid)")] /// /// /// - + [Obsolete("请直接使用 Guid.TryParse(guid, out var result)", false)] public static Guid? ToGuid(this string guid) { try diff --git a/EasyTool.Core/ConvertCategory/ConvertUtil.cs b/EasyTool.Core/ConvertCategory/ConvertUtil.cs index 752a52f..015812a 100644 --- a/EasyTool.Core/ConvertCategory/ConvertUtil.cs +++ b/EasyTool.Core/ConvertCategory/ConvertUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace EasyTool { @@ -10,22 +10,22 @@ public static class ConvertUtil /// /// 将对象转换为指定类型,转换失败返回指定类型的默认值 /// - public static T To(object value) + public static T? To(object? value) { try { - return (T)Convert.ChangeType(value, typeof(T)); + return (T)Convert.ChangeType(value, typeof(T))!; } catch { - return default(T); + return default; } } /// /// 将字符串转换为整型,转换失败返回0 /// - public static int ToInt32(string value) + public static int ToInt32(string? value) { int result; if (int.TryParse(value, out result)) @@ -38,7 +38,7 @@ public static int ToInt32(string value) /// /// 将字符串转换为长整型,转换失败返回0 /// - public static long ToInt64(string value) + public static long ToInt64(string? value) { long result; if (long.TryParse(value, out result)) @@ -51,7 +51,7 @@ public static long ToInt64(string value) /// /// 将字符串转换为布尔型,转换失败返回默认值,默认值false /// - public static bool ToBoolean(string data, bool defValue = false) + public static bool ToBoolean(string? data, bool defValue = false) { //如果为空则返回默认值 if (string.IsNullOrEmpty(data)) @@ -73,7 +73,7 @@ public static bool ToBoolean(string data, bool defValue = false) /// /// 将对象转换为布尔型,转换失败返回默认值,默认值false /// - public static bool ToBoolean(object data, bool defValue = false) + public static bool ToBoolean(object? data, bool defValue = false) { //如果为空则返回默认值 if (data == null || Convert.IsDBNull(data)) @@ -94,7 +94,7 @@ public static bool ToBoolean(object data, bool defValue = false) /// /// 将字符串转换为单精度浮点型,转换失败返回0 /// - public static float ToSingle(string value) + public static float ToSingle(string? value) { float result; if (float.TryParse(value, out result)) @@ -107,7 +107,7 @@ public static float ToSingle(string value) /// /// 将字符串转换为双精度浮点型,转换失败返回0 /// - public static double ToDouble(string value) + public static double ToDouble(string? value) { double result; if (double.TryParse(value, out result)) @@ -120,7 +120,7 @@ public static double ToDouble(string value) /// /// 将字符串转换为十进制数,转换失败返回0 /// - public static decimal ToDecimal(string value) + public static decimal ToDecimal(string? value) { decimal result; if (decimal.TryParse(value, out result)) @@ -133,7 +133,7 @@ public static decimal ToDecimal(string value) /// /// 将字符串转换为日期时间,转换失败返回DateTime.MinValue /// - public static DateTime ToDateTime(string value) + public static DateTime ToDateTime(string? value) { DateTime result; if (DateTime.TryParse(value, out result)) @@ -146,14 +146,14 @@ public static DateTime ToDateTime(string value) /// /// 将字符串转换为枚举类型,转换失败返回默认值 /// - public static T ToEnum(string value, T defaultValue = default(T)) where T : struct + public static T ToEnum(string? value, T? defaultValue = default) where T : struct { T result; if (Enum.TryParse(value, out result)) { return result; } - return defaultValue; + return defaultValue ?? default; } diff --git a/EasyTool.Core/ConvertCategory/NumberExtension.cs b/EasyTool.Core/ConvertCategory/NumberExtension.cs new file mode 100644 index 0000000..128b7b8 --- /dev/null +++ b/EasyTool.Core/ConvertCategory/NumberExtension.cs @@ -0,0 +1,546 @@ +using System; + +namespace EasyTool.Extension +{ + /// + /// 数字类型扩展方法 + /// + public static class NumberExtension + { + #region 整数扩展 + + /// + /// 判断整数是否为偶数 + /// + public static bool IsEven(this int value) + { + return value % 2 == 0; + } + + /// + /// 判断整数是否为奇数 + /// + public static bool IsOdd(this int value) + { + return value % 2 != 0; + } + + /// + /// 判断长整数是否为偶数 + /// + public static bool IsEven(this long value) + { + return value % 2 == 0; + } + + /// + /// 判断长整数是否为奇数 + /// + public static bool IsOdd(this long value) + { + return value % 2 != 0; + } + + /// + /// 判断短整数是否为偶数 + /// + public static bool IsEven(this short value) + { + return value % 2 == 0; + } + + /// + /// 判断短整数是否为奇数 + /// + public static bool IsOdd(this short value) + { + return value % 2 != 0; + } + + #endregion + + #region 浮点数扩展 + + /// + /// 判断浮点数是否在指定范围内(包含边界) + /// + public static bool IsBetween(this float value, float min, float max) + { + return value >= min && value <= max; + } + + /// + /// 判断双精度浮点数是否在指定范围内(包含边界) + /// + public static bool IsBetween(this double value, double min, double max) + { + return value >= min && value <= max; + } + + /// + /// 判断小数是否在指定范围内(包含边界) + /// + public static bool IsBetween(this decimal value, decimal min, decimal max) + { + return value >= min && value <= max; + } + + /// + /// 限制浮点数在指定范围内 + /// + public static float Clamp(this float value, float min, float max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + /// + /// 限制双精度浮点数在指定范围内 + /// + public static double Clamp(this double value, double min, double max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + /// + /// 限制小数在指定范围内 + /// + public static decimal Clamp(this decimal value, decimal min, decimal max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + /// + /// 限制整数在指定范围内 + /// + public static int Clamp(this int value, int min, int max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + /// + /// 限制长整数在指定范围内 + /// + public static long Clamp(this long value, long min, long max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + #endregion + + #region 百分比转换 + + /// + /// 将小数转换为百分比字符串 + /// + /// 数值 + /// 小数位数,默认2位 + public static string ToPercentage(this double value, int decimals = 2) + { + return (value * 100).ToString($"F{decimals}") + "%"; + } + + /// + /// 将小数转换为百分比字符串 + /// + /// 数值 + /// 小数位数,默认2位 + public static string ToPercentage(this float value, int decimals = 2) + { + return (value * 100).ToString($"F{decimals}") + "%"; + } + + /// + /// 将小数转换为百分比字符串 + /// + /// 数值 + /// 小数位数,默认2位 + public static string ToPercentage(this decimal value, int decimals = 2) + { + return (value * 100).ToString($"F{decimals}") + "%"; + } + + #endregion + + #region 文件大小转换 + + /// + /// 将字节数转换为人类可读的文件大小格式 + /// + /// 字节数 + public static string ToFileSize(this long bytes) + { + string[] sizes = { "B", "KB", "MB", "GB", "TB" }; + double len = bytes; + int order = 0; + + while (len >= 1024 && order < sizes.Length - 1) + { + order++; + len /= 1024; + } + + return $"{len:0.##} {sizes[order]}"; + } + + /// + /// 将字节数转换为人类可读的文件大小格式 + /// + /// 字节数 + public static string ToFileSize(this int bytes) + { + return ((long)bytes).ToFileSize(); + } + + #endregion + + #region 时间转换 + + /// + /// 将秒数转换为时间跨度 + /// + public static TimeSpan ToTimeSpan(this double seconds) + { + return TimeSpan.FromSeconds(seconds); + } + + /// + /// 将秒数转换为时间跨度 + /// + public static TimeSpan ToTimeSpan(this int seconds) + { + return TimeSpan.FromSeconds(seconds); + } + + /// + /// 将毫秒数转换为时间跨度 + /// + public static TimeSpan ToTimeSpanFromMilliseconds(this long milliseconds) + { + return TimeSpan.FromMilliseconds(milliseconds); + } + + /// + /// 将毫秒数转换为时间跨度 + /// + public static TimeSpan ToTimeSpanFromMilliseconds(this int milliseconds) + { + return TimeSpan.FromMilliseconds(milliseconds); + } + + #endregion + + #region 数学运算 + + /// + /// 计算数值的平方 + /// + public static int Square(this int value) + { + return value * value; + } + + /// + /// 计算数值的平方 + /// + public static long Square(this long value) + { + return value * value; + } + + /// + /// 计算数值的平方 + /// + public static double Square(this double value) + { + return value * value; + } + + /// + /// 计算数值的立方 + /// + public static int Cube(this int value) + { + return value * value * value; + } + + /// + /// 计算数值的立方 + /// + public static long Cube(this long value) + { + return value * value * value; + } + + /// + /// 计算数值的立方 + /// + public static double Cube(this double value) + { + return value * value * value; + } + + /// + /// 计算数值的绝对值 + /// [Obsolete("请直接使用 Math.Abs(value)")] + /// + [Obsolete("请直接使用 Math.Abs(value)", false)] + public static int Abs(this int value) + { + return Math.Abs(value); + } + + /// + /// 计算数值的绝对值 + /// [Obsolete("请直接使用 Math.Abs(value)")] + /// + [Obsolete("请直接使用 Math.Abs(value)", false)] + public static long Abs(this long value) + { + return Math.Abs(value); + } + + /// + /// 计算数值的绝对值 + /// [Obsolete("请直接使用 Math.Abs(value)")] + /// + [Obsolete("请直接使用 Math.Abs(value)", false)] + public static float Abs(this float value) + { + return Math.Abs(value); + } + + /// + /// 计算数值的绝对值 + /// [Obsolete("请直接使用 Math.Abs(value)")] + /// + [Obsolete("请直接使用 Math.Abs(value)", false)] + public static double Abs(this double value) + { + return Math.Abs(value); + } + + /// + /// 计算数值的绝对值 + /// [Obsolete("请直接使用 Math.Abs(value)")] + /// + [Obsolete("请直接使用 Math.Abs(value)", false)] + public static decimal Abs(this decimal value) + { + return Math.Abs(value); + } + + #endregion + + #region 数值判断 + + /// + /// 判断浮点数是否为 NaN + /// [Obsolete("请直接使用 float.IsNaN(value)")] + /// + [Obsolete("请直接使用 float.IsNaN(value)", false)] + public static bool IsNaN(this float value) + { + return float.IsNaN(value); + } + + /// + /// 判断双精度浮点数是否为 NaN + /// [Obsolete("请直接使用 double.IsNaN(value)")] + /// + [Obsolete("请直接使用 double.IsNaN(value)", false)] + public static bool IsNaN(this double value) + { + return double.IsNaN(value); + } + + /// + /// 判断浮点数是否为无穷大 + /// [Obsolete("请直接使用 float.IsInfinity(value)")] + /// + [Obsolete("请直接使用 float.IsInfinity(value)", false)] + public static bool IsInfinity(this float value) + { + return float.IsInfinity(value); + } + + /// + /// 判断双精度浮点数是否为无穷大 + /// [Obsolete("请直接使用 double.IsInfinity(value)")] + /// + [Obsolete("请直接使用 double.IsInfinity(value)", false)] + public static bool IsInfinity(this double value) + { + return double.IsInfinity(value); + } + + /// + /// 判断浮点数是否为正无穷大 + /// [Obsolete("请直接使用 float.IsPositiveInfinity(value)")] + /// + [Obsolete("请直接使用 float.IsPositiveInfinity(value)", false)] + public static bool IsPositiveInfinity(this float value) + { + return float.IsPositiveInfinity(value); + } + + /// + /// 判断双精度浮点数是否为正无穷大 + /// [Obsolete("请直接使用 double.IsPositiveInfinity(value)")] + /// + [Obsolete("请直接使用 double.IsPositiveInfinity(value)", false)] + public static bool IsPositiveInfinity(this double value) + { + return double.IsPositiveInfinity(value); + } + + /// + /// 判断浮点数是否为负无穷大 + /// [Obsolete("请直接使用 float.IsNegativeInfinity(value)")] + /// + [Obsolete("请直接使用 float.IsNegativeInfinity(value)", false)] + public static bool IsNegativeInfinity(this float value) + { + return float.IsNegativeInfinity(value); + } + + /// + /// 判断双精度浮点数是否为负无穷大 + /// [Obsolete("请直接使用 double.IsNegativeInfinity(value)")] + /// + [Obsolete("请直接使用 double.IsNegativeInfinity(value)", false)] + public static bool IsNegativeInfinity(this double value) + { + return double.IsNegativeInfinity(value); + } + + #endregion + + #region 数值格式化 + + /// + /// 将数字格式化为带千分位的字符串 + /// + public static string ToThousandsSeparator(this int value) + { + return value.ToString("#,##0"); + } + + /// + /// 将数字格式化为带千分位的字符串 + /// + public static string ToThousandsSeparator(this long value) + { + return value.ToString("#,##0"); + } + + /// + /// 将数字格式化为带千分位的字符串 + /// + /// 小数位数 + public static string ToThousandsSeparator(this double value, int decimals = 2) + { + return value.ToString($"#,##0.{new string('0', decimals)}"); + } + + /// + /// 将数字格式化为带千分位的字符串 + /// + /// 小数位数 + public static string ToThousandsSeparator(this float value, int decimals = 2) + { + return value.ToString($"#,##0.{new string('0', decimals)}"); + } + + /// + /// 将数字格式化为带千分位的字符串 + /// + /// 小数位数 + public static string ToThousandsSeparator(this decimal value, int decimals = 2) + { + return value.ToString($"#,##0.{new string('0', decimals)}"); + } + + /// + /// 将数字转换为中文大写金额 + /// + public static string ToChineseMoney(this decimal value) + { + if (value == 0) + return "零元整"; + + string[] digits = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + string[] units = { "", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟" }; + + string result = string.Empty; + bool hasYuan = false; + bool hasJiao = false; + bool hasFen = false; + + // 处理整数部分 + long integerPart = (long)value; + if (integerPart > 0) + { + string integerStr = integerPart.ToString(); + int length = integerStr.Length; + + for (int i = 0; i < length; i++) + { + int digit = integerStr[i] - '0'; + int pos = length - i - 1; + + if (digit != 0) + { + result += digits[digit] + units[pos]; + hasYuan = true; + } + else if (result.Length > 0 && result[result.Length - 1] != '零') + { + result += '零'; + } + } + + if (hasYuan) + result += '元'; + } + + // 处理小数部分 + decimal decimalPart = value - integerPart; + int jiao = (int)(decimalPart * 10); + int fen = (int)(decimalPart * 100) % 10; + + if (jiao > 0) + { + result += digits[jiao] + "角"; + hasJiao = true; + } + + if (fen > 0) + { + result += digits[fen] + "分"; + hasFen = true; + } + + if (!hasJiao && !hasFen && hasYuan) + result += "整"; + + // 清理多余的零 + result = result.Replace("零零", "零"); + if (result.EndsWith("零元")) + result = result.Substring(0, result.Length - 2) + "元"; + + return result; + } + + #endregion + } +} diff --git a/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs b/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs index 2b7f32f..bbf5b8d 100644 --- a/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs +++ b/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs @@ -103,5 +103,355 @@ public static class DateTimeExtension /// 指定日期。 /// 指定日期所在年份的所有日期。 public static List GetYearDays(this DateTime date) => DateTimeUtil.GetYearDays(date); + + + + #region 新增扩展方法 + + /// + /// 判断日期是否是今天 + /// + public static bool IsToday(this DateTime date) + { + return date.Date == DateTime.Today; + } + + /// + /// 判断日期是否是昨天 + /// + public static bool IsYesterday(this DateTime date) + { + return date.Date == DateTime.Today.AddDays(-1); + } + + /// + /// 判断日期是否是明天 + /// + public static bool IsTomorrow(this DateTime date) + { + return date.Date == DateTime.Today.AddDays(1); + } + + /// + /// 判断日期是否在本周 + /// + public static bool IsThisWeek(this DateTime date) + { + var today = DateTime.Today; + var firstDayOfWeek = today.GetFirstDayOfWeek(); + var lastDayOfWeek = firstDayOfWeek.AddDays(6); + return date.Date >= firstDayOfWeek && date.Date <= lastDayOfWeek; + } + + /// + /// 判断日期是否在本月 + /// + public static bool IsThisMonth(this DateTime date) + { + var today = DateTime.Today; + return date.Year == today.Year && date.Month == today.Month; + } + + /// + /// 判断日期是否在本年 + /// + public static bool IsThisYear(this DateTime date) + { + return date.Year == DateTime.Today.Year; + } + + /// + /// 判断日期是否在指定范围内(包含边界) + /// + public static bool IsBetween(this DateTime date, DateTime startDate, DateTime endDate) + { + return date >= startDate && date <= endDate; + } + + /// + /// 计算年龄 + /// + public static int ToAge(this DateTime birthDate) + { + var today = DateTime.Today; + int age = today.Year - birthDate.Year; + + if (birthDate > today.AddYears(-age)) + age--; + + return age; + } + + /// + /// 将日期转换为 Unix 时间戳(秒) + /// [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeSeconds()")] + /// + [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeSeconds()", false)] + public static long ToUnixTimestamp(this DateTime date) + { + return new DateTimeOffset(date).ToUnixTimeSeconds(); + } + + /// + /// 将日期转换为 Unix 时间戳(毫秒) + /// [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeMilliseconds()")] + /// + [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeMilliseconds()", false)] + public static long ToUnixTimestampMilliseconds(this DateTime date) + { + return new DateTimeOffset(date).ToUnixTimeMilliseconds(); + } + + /// + /// 从 Unix 时间戳(秒)转换为日期 + /// [Obsolete("请使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).LocalDateTime")] + /// + [Obsolete("请使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).LocalDateTime", false)] + public static DateTime FromUnixTimestamp(this long timestamp) + { + return DateTimeOffset.FromUnixTimeSeconds(timestamp).LocalDateTime; + } + + /// + /// 从 Unix 时间戳(毫秒)转换为日期 + /// [Obsolete("请使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime")] + /// + [Obsolete("请使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime", false)] + public static DateTime FromUnixTimestampMilliseconds(this long timestamp) + { + return DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime; + } + + /// + /// 判断是否是周末(周六或周日) + /// + public static bool IsWeekend(this DateTime date) + { + return date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday; + } + + /// + /// 获取日期所在月的最后一天 + /// + public static DateTime GetLastDayOfMonth(this DateTime date) + { + return new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month)); + } + + /// + /// 获取日期所在月的最后一天 + /// + public static DateTime GetLastDayOfWeek(this DateTime date) + { + var firstDay = date.GetFirstDayOfWeek(); + return firstDay.AddDays(6); + } + + /// + /// 获取日期所在季度的最后一天 + /// + public static DateTime GetLastDayOfQuarter(this DateTime date) + { + int currentQuarter = (date.Month - 1) / 3 + 1; + int lastMonthOfQuarter = currentQuarter * 3; + return new DateTime(date.Year, lastMonthOfQuarter, DateTime.DaysInMonth(date.Year, lastMonthOfQuarter)); + } + + /// + /// 获取日期所在年的最后一天 + /// + public static DateTime GetLastDayOfYear(this DateTime date) + { + return new DateTime(date.Year, 12, 31); + } + + /// + /// 获取日期的中文星期表示 + /// + public static string ToChineseWeekDay(this DateTime date) + { + return date.DayOfWeek switch + { + DayOfWeek.Monday => "星期一", + DayOfWeek.Tuesday => "星期二", + DayOfWeek.Wednesday => "星期三", + DayOfWeek.Thursday => "星期四", + DayOfWeek.Friday => "星期五", + DayOfWeek.Saturday => "星期六", + DayOfWeek.Sunday => "星期日", + _ => string.Empty + }; + } + + /// + /// 获取日期的中文星期简称 + /// + public static string ToChineseWeekDayShort(this DateTime date) + { + return date.DayOfWeek switch + { + DayOfWeek.Monday => "周一", + DayOfWeek.Tuesday => "周二", + DayOfWeek.Wednesday => "周三", + DayOfWeek.Thursday => "周四", + DayOfWeek.Friday => "周五", + DayOfWeek.Saturday => "周六", + DayOfWeek.Sunday => "周日", + _ => string.Empty + }; + } + + /// + /// 判断是否是闰年 + /// [Obsolete("请直接使用 DateTime.IsLeapYear(date.Year)")] + /// + [Obsolete("请直接使用 DateTime.IsLeapYear(date.Year)", false)] + public static bool IsLeapYear(this DateTime date) + { + return DateTime.IsLeapYear(date.Year); + } + + /// + /// 获取日期所在季度的数字(1-4) + /// + public static int GetQuarter(this DateTime date) + { + return (date.Month - 1) / 3 + 1; + } + + /// + /// 获取日期所在周在本年的周数 + /// + public static int GetWeekOfYear(this DateTime date) + { + var culture = CultureInfo.CurrentCulture; + var calendar = culture.Calendar; + var weekRule = culture.DateTimeFormat.CalendarWeekRule; + var firstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek; + return calendar.GetWeekOfYear(date, weekRule, firstDayOfWeek); + } + + /// + /// 添加工作日 + /// + /// 起始日期 + /// 要添加的工作日数 + public static DateTime AddWorkDays(this DateTime date, int workDays) + { + var result = date; + int daysToAdd = Math.Abs(workDays); + int direction = workDays >= 0 ? 1 : -1; + + while (daysToAdd > 0) + { + result = result.AddDays(direction); + if (result.IsWorkDay()) + daysToAdd--; + } + + return result; + } + + /// + /// 获取日期的友好字符串表示 + /// + public static string ToFriendlyString(this DateTime date) + { + var today = DateTime.Today; + var span = today - date.Date; + + return span.TotalDays switch + { + 0 => "今天", + 1 => "昨天", + -1 => "明天", + _ when span.TotalDays > 0 && span.TotalDays <= 7 => $"上周{date.ToChineseWeekDayShort()}", + _ when span.TotalDays < 0 && span.TotalDays >= -7 => $"下周{date.ToChineseWeekDayShort()}", + _ when date.Year == today.Year => date.ToString("MM月dd日"), + _ => date.ToString("yyyy年MM月dd日") + }; + } + + /// + /// 获取两个日期之间相差的月数 + /// + public static int GetMonthsBetween(this DateTime startDate, DateTime endDate) + { + int months = (endDate.Year - startDate.Year) * 12 + endDate.Month - startDate.Month; + + // 如果结束日期的日小于开始日期的日,需要减去一个月 + if (endDate.Day < startDate.Day) + { + months--; + } + + return Math.Abs(months); + } + + /// + /// 获取两个日期之间相差的年数 + /// + public static int GetYearsBetween(this DateTime startDate, DateTime endDate) + { + int years = endDate.Year - startDate.Year; + + // 如果结束日期的月和日小于开始日期的月和日,需要减去一年 + if (endDate.Month < startDate.Month || (endDate.Month == startDate.Month && endDate.Day < startDate.Day)) + { + years--; + } + + return Math.Abs(years); + } + + /// + /// 获取一天的开始时间(00:00:00) + /// + public static DateTime StartOfDay(this DateTime date) + { + return date.Date; + } + + /// + /// 获取一天的结束时间(23:59:59) + /// + public static DateTime EndOfDay(this DateTime date) + { + return date.Date.AddDays(1).AddTicks(-1); + } + + /// + /// 获取月的开始时间 + /// + public static DateTime StartOfMonth(this DateTime date) + { + return new DateTime(date.Year, date.Month, 1); + } + + /// + /// 获取月的结束时间 + /// + public static DateTime EndOfMonth(this DateTime date) + { + return date.GetLastDayOfMonth().EndOfDay(); + } + + /// + /// 获取年的开始时间 + /// + public static DateTime StartOfYear(this DateTime date) + { + return new DateTime(date.Year, 1, 1); + } + + /// + /// 获取年的结束时间 + /// + public static DateTime EndOfYear(this DateTime date) + { + return new DateTime(date.Year, 12, 31).EndOfDay(); + } + + #endregion } } diff --git a/EasyTool.Core/DateTimeCategory/TimerUtil.cs b/EasyTool.Core/DateTimeCategory/TimerUtil.cs index 68f56e4..e87f551 100644 --- a/EasyTool.Core/DateTimeCategory/TimerUtil.cs +++ b/EasyTool.Core/DateTimeCategory/TimerUtil.cs @@ -42,8 +42,10 @@ public static TimeSpan GetElapsedTime() /// /// 创建一个新的 Stopwatch 并启动计时。 + /// [Obsolete("请直接使用 Stopwatch.StartNew()")] /// /// 一个新的 Stopwatch。 + [Obsolete("请直接使用 Stopwatch.StartNew()", false)] public static Stopwatch StartNew() { Stopwatch stopwatch = new Stopwatch(); @@ -100,8 +102,10 @@ public static void MeasureAndLog(Action action, string fileName) /// /// 等待指定的时间 + /// [Obsolete("请直接使用 Thread.Sleep(milliseconds)")] /// /// 要等待的毫秒数。 + [Obsolete("请直接使用 Thread.Sleep(milliseconds)", false)] public static void Wait(int milliseconds) { System.Threading.Thread.Sleep(milliseconds); diff --git a/EasyTool.Core/DateTimeCategory/TimestampUtil.cs b/EasyTool.Core/DateTimeCategory/TimestampUtil.cs index 8e712c2..6c7912d 100644 --- a/EasyTool.Core/DateTimeCategory/TimestampUtil.cs +++ b/EasyTool.Core/DateTimeCategory/TimestampUtil.cs @@ -9,8 +9,10 @@ public static class TimestampUtil { /// /// 获取当前时间戳(毫秒级) + /// [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()")] /// /// 当前时间戳(毫秒级) + [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()", false)] public static long GetCurrentTimestamp() { DateTime dt = DateTime.UtcNow; @@ -20,9 +22,11 @@ public static long GetCurrentTimestamp() /// /// 将时间戳(毫秒级)转换为 DateTime 类型 + /// [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime")] /// /// 时间戳(毫秒级) /// 转换后的 DateTime 类型 + [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime", false)] public static DateTime ConvertToDateTime(long timestamp) { DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); @@ -31,9 +35,11 @@ public static DateTime ConvertToDateTime(long timestamp) /// /// 将 DateTime 类型转换为时间戳(毫秒级) + /// [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeMilliseconds()")] /// /// DateTime 类型 /// 转换后的时间戳(毫秒级) + [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeMilliseconds()", false)] public static long ConvertToTimestamp(DateTime dateTime) { TimeSpan ts = dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); @@ -42,8 +48,10 @@ public static long ConvertToTimestamp(DateTime dateTime) /// /// 获取当前时间戳(秒级) + /// [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeSeconds()")] /// /// 当前时间戳(秒级) + [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeSeconds()", false)] public static long GetCurrentTimestampSeconds() { return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; @@ -51,9 +59,11 @@ public static long GetCurrentTimestampSeconds() /// /// 将时间戳(秒级)转换为 DateTime 类型 + /// [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime")] /// /// 时间戳(秒级) /// 转换后的 DateTime 类型 + [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime", false)] public static DateTime ConvertToDateTimeSeconds(long timestamp) { return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp); @@ -61,9 +71,11 @@ public static DateTime ConvertToDateTimeSeconds(long timestamp) /// /// 将 DateTime 类型转换为时间戳(秒级) + /// [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeSeconds()")] /// /// DateTime 类型 /// 转换后的时间戳(秒级) + [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeSeconds()", false)] public static long ConvertToTimestampSeconds(DateTime dateTime) { return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; diff --git a/EasyTool.Core/EasyTool.Core.csproj b/EasyTool.Core/EasyTool.Core.csproj index 405fa8d..e769fec 100644 --- a/EasyTool.Core/EasyTool.Core.csproj +++ b/EasyTool.Core/EasyTool.Core.csproj @@ -3,11 +3,15 @@ netstandard2.1;net10.0 latest - enable true $(NoWarn); $(MSBuildProjectName.Replace(" ", "_").Replace(".Core", "")) + + annotations + + enable + Joce.EasyTool.Core 一个大西瓜,TimChen 2026.0108.1 diff --git a/EasyTool.Core/IEnumerableCategory/IEnumerableExtensions.cs b/EasyTool.Core/IEnumerableCategory/IEnumerableExtensions.cs index 84cafe5..d458173 100644 --- a/EasyTool.Core/IEnumerableCategory/IEnumerableExtensions.cs +++ b/EasyTool.Core/IEnumerableCategory/IEnumerableExtensions.cs @@ -1,31 +1,79 @@ using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Text; namespace EasyTool.IEnumerableCategory { /// - /// 通用拓展 + /// IEnumerable 通用扩展方法 /// public static class IEnumerableExtensions { + #region 空值处理 - - - #region IEnumerable拓展 /// - /// 对List 等集合Foreach的时候不用在上层判空,直接加上这个就好 + /// 对 List 等集合 Foreach 的时候不用在上层判空,直接加上这个就好 /// - /// - /// - /// public static IEnumerable CheckNull(this IEnumerable values) { return values is null ? new List(0) : values; } + /// + /// 判断集合是否为空或 null + /// + public static bool IsNullOrEmpty(this IEnumerable source) + { + return source == null || !source.Any(); + } + + /// + /// 判断集合是否非空 + /// + public static bool IsNotEmpty(this IEnumerable source) + { + return source != null && source.Any(); + } + + #endregion + + #region 遍历操作 + + /// + /// 遍历集合并对每个元素执行指定操作 + /// + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null || action == null) + return; + + foreach (var item in source) + { + action(item); + } + } + + /// + /// 遍历集合并对每个元素及其索引执行指定操作 + /// + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null || action == null) + return; + + int index = 0; + foreach (var item in source) + { + action(item, index++); + } + } + + #endregion + #region 集合运算 + /// /// 求集合的笛卡尔积 /// @@ -40,7 +88,384 @@ select accseq.Concat(new[] { item })); } + /// + /// 按指定键去重 + /// + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) + { + if (source == null) + yield break; + + var seenKeys = new HashSet(); + foreach (var item in source) + { + if (seenKeys.Add(keySelector(item))) + { + yield return item; + } + } + } + + /// + /// 批量处理集合 + /// + /// 每批的大小 + public static IEnumerable> Batch(this IEnumerable source, int batchSize) + { + if (source == null) + yield break; + + if (batchSize <= 0) + throw new ArgumentException("batchSize must be greater than 0", nameof(batchSize)); + + var batch = new List(batchSize); + foreach (var item in source) + { + batch.Add(item); + if (batch.Count == batchSize) + { + yield return batch; + batch = new List(batchSize); + } + } + + if (batch.Count > 0) + { + yield return batch; + } + } + + #endregion + + #region 转换操作 + + /// + /// 将集合转换为 DataTable + /// + public static DataTable ToDataTable(this IEnumerable source) + { + var table = new DataTable(typeof(T).Name); + + var properties = typeof(T).GetProperties(); + foreach (var prop in properties) + { + table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); + } + + foreach (var item in source) + { + var row = table.NewRow(); + foreach (var prop in properties) + { + row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; + } + table.Rows.Add(row); + } + + return table; + } + + /// + /// 将集合转换为 HashSet + /// + public static HashSet ToHashSet(this IEnumerable source) + { + if (source == null) + return new HashSet(); + + return new HashSet(source); + } + + /// + /// 将集合转换为 Queue + /// + public static Queue ToQueue(this IEnumerable source) + { + if (source == null) + return new Queue(); + + return new Queue(source); + } + + /// + /// 将集合转换为 Stack + /// + public static Stack ToStack(this IEnumerable source) + { + if (source == null) + return new Stack(); + + return new Stack(source); + } + + /// + /// 将集合转换为 LinkedList + /// + public static LinkedList ToLinkedList(this IEnumerable source) + { + if (source == null) + return new LinkedList(); + + return new LinkedList(source); + } + + #endregion + + #region 连接操作 + + /// + /// 将集合元素连接成字符串 + /// + /// 分隔符 + public static string JoinAsString(this IEnumerable source, string separator = ",") + { + if (source == null) + return string.Empty; + + return string.Join(separator, source); + } + + /// + /// 将集合元素连接成字符串(使用格式化) + /// + /// 分隔符 + /// 格式化字符串 + public static string JoinAsString(this IEnumerable source, string separator, string format) + { + if (source == null) + return string.Empty; + + return string.Join(separator, source.Select(item => string.Format(format, item))); + } + #endregion + + #region 分页操作 + + /// + /// 分页 + /// + /// 页码(从1开始) + /// 每页大小 + public static IEnumerable Page(this IEnumerable source, int pageIndex, int pageSize) + { + if (source == null || pageIndex < 1 || pageSize < 1) + yield break; + + int skip = (pageIndex - 1) * pageSize; + foreach (var item in source.Skip(skip).Take(pageSize)) + { + yield return item; + } + } + + #endregion + + #region 随机操作 + + /// + /// 随机排序 + /// + public static IEnumerable Shuffle(this IEnumerable source) + { + if (source == null) + yield break; + + var random = new Random(); + var list = source.ToList(); + + for (int i = list.Count - 1; i > 0; i--) + { + int j = random.Next(i + 1); + (list[i], list[j]) = (list[j], list[i]); + } + + foreach (var item in list) + { + yield return item; + } + } + + /// + /// 随机获取一个元素 + /// + public static T Random(this IEnumerable source) + { + if (source == null) + return default; + + var list = source.ToList(); + if (list.Count == 0) + return default; + + var random = new Random(); + return list[random.Next(list.Count)]; + } + + /// + /// 随机获取指定数量的元素 + /// + public static IEnumerable RandomTake(this IEnumerable source, int count) + { + if (source == null || count <= 0) + yield break; + + var list = source.ToList(); + if (list.Count == 0) + yield break; + + count = Math.Min(count, list.Count); + var random = new Random(); + var selected = new HashSet(); + + while (selected.Count < count) + { + selected.Add(random.Next(list.Count)); + } + + foreach (var index in selected) + { + yield return list[index]; + } + } + + #endregion + + #region 条件操作 + + /// + /// 根据条件执行不同的操作 + /// + public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) + { + if (source == null) + yield break; + + if (condition) + { + foreach (var item in source.Where(predicate)) + { + yield return item; + } + } + else + { + foreach (var item in source) + { + yield return item; + } + } + } + + /// + /// 如果集合为空则返回默认集合 + /// + public static IEnumerable IfEmpty(this IEnumerable source, IEnumerable defaultValue) + { + if (source == null || !source.Any()) + return defaultValue ?? Enumerable.Empty(); + + return source; + } + + #endregion + + #region 统计操作 + + /// + /// 统计满足条件的元素数量 + /// + public static int CountEx(this IEnumerable source, Func predicate) + { + if (source == null) + return 0; + + return source.Count(predicate); + } + + #endregion + + #region 索引操作 + + /// + /// 获取指定索引处的元素 + /// + public static T ElementAtOrDefault(this IEnumerable source, int index, T defaultValue) + { + if (source == null || index < 0) + return defaultValue; + + int i = 0; + foreach (var item in source) + { + if (i == index) + return item; + i++; + } + + return defaultValue; + } + + /// + /// 获取第一个元素,如果集合为空则返回默认值 + /// + public static T FirstOrValue(this IEnumerable source, T defaultValue) + { + if (source == null) + return defaultValue; + + foreach (var item in source) + { + return item; + } + + return defaultValue; + } + + /// + /// 获取最后一个元素,如果集合为空则返回默认值 + /// + public static T LastOrValue(this IEnumerable source, T defaultValue) + { + if (source == null) + return defaultValue; + + var last = defaultValue; + var hasElement = false; + + foreach (var item in source) + { + last = item; + hasElement = true; + } + + return hasElement ? last : defaultValue; + } + + #endregion + + #region 集合合并 + + /// + /// 合并多个集合 + /// + public static IEnumerable Merge(params IEnumerable[] sources) + { + if (sources == null || sources.Length == 0) + yield break; + + foreach (var source in sources) + { + if (source != null) + { + foreach (var item in source) + { + yield return item; + } + } + } + } + #endregion } } diff --git a/EasyTool.Core/IOCategory/FileSystemExtension.cs b/EasyTool.Core/IOCategory/FileSystemExtension.cs new file mode 100644 index 0000000..2cd7865 --- /dev/null +++ b/EasyTool.Core/IOCategory/FileSystemExtension.cs @@ -0,0 +1,522 @@ +using System; +using System.IO; +using System.Linq; + +namespace EasyTool.Extension +{ + /// + /// 文件系统扩展方法 + /// + public static class FileSystemExtension + { + #region FileInfo 扩展 + + /// + /// 获取文件大小(格式化字符串) + /// + public static string GetSizeFormatted(this FileInfo file) + { + if (file == null || !file.Exists) + return "0 B"; + + return file.Length.ToFileSize(); + } + + /// + /// 获取相对路径 + /// + /// 源文件 + /// 参考路径 + public static string GetRelativePath(this FileInfo file, string relativeTo) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + + return GetRelativePath(file.FullName, relativeTo); + } + + /// + /// 获取文件的 MIME 类型 + /// + public static string GetMimeType(this FileInfo file) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + + return file.Extension.GetMimeType(); + } + + /// + /// 判断文件是否为图片 + /// + public static bool IsImage(this FileInfo file) + { + if (file == null) + return false; + + string[] imageExtensions = { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".ico", ".svg" }; + return imageExtensions.Contains(file.Extension.ToLowerInvariant()); + } + + /// + /// 判断文件是否为文档 + /// + public static bool IsDocument(this FileInfo file) + { + if (file == null) + return false; + + string[] docExtensions = { ".doc", ".docx", ".pdf", ".txt", ".rtf", ".odt", ".xls", ".xlsx", ".ppt", ".pptx" }; + return docExtensions.Contains(file.Extension.ToLowerInvariant()); + } + + /// + /// 判断文件是否为视频 + /// + public static bool IsVideo(this FileInfo file) + { + if (file == null) + return false; + + string[] videoExtensions = { ".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv", ".webm", ".m4v" }; + return videoExtensions.Contains(file.Extension.ToLowerInvariant()); + } + + /// + /// 判断文件是否为音频 + /// + public static bool IsAudio(this FileInfo file) + { + if (file == null) + return false; + + string[] audioExtensions = { ".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a" }; + return audioExtensions.Contains(file.Extension.ToLowerInvariant()); + } + + /// + /// 判断文件是否被锁定(正在使用) + /// + public static bool IsLocked(this FileInfo file) + { + if (file == null || !file.Exists) + return false; + + try + { + using (var stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)) + { + return false; + } + } + catch (IOException) + { + return true; + } + catch (UnauthorizedAccessException) + { + return true; + } + } + + /// + /// 安全删除文件(如果存在) + /// + public static bool DeleteIfExists(this FileInfo file) + { + if (file == null || !file.Exists) + return false; + + try + { + file.Delete(); + return true; + } + catch + { + return false; + } + } + + /// + /// 移动文件到指定目录 + /// + public static FileInfo MoveToDirectory(this FileInfo file, string targetDirectory) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + + if (!Directory.Exists(targetDirectory)) + Directory.CreateDirectory(targetDirectory); + + string targetPath = Path.Combine(targetDirectory, file.Name); + file.MoveTo(targetPath); + return new FileInfo(targetPath); + } + + /// + /// 复制文件到指定目录 + /// + public static FileInfo CopyToDirectory(this FileInfo file, string targetDirectory, bool overwrite = false) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + + if (!Directory.Exists(targetDirectory)) + Directory.CreateDirectory(targetDirectory); + + string targetPath = Path.Combine(targetDirectory, file.Name); + file.CopyTo(targetPath, overwrite); + return new FileInfo(targetPath); + } + + /// + /// 读取文件的所有文本内容 + /// [Obsolete("请直接使用 File.ReadAllText(file.FullName)")] + /// + [Obsolete("请直接使用 File.ReadAllText(file.FullName)", false)] + public static string ReadAllText(this FileInfo file) + { + if (file == null || !file.Exists) + return string.Empty; + + return File.ReadAllText(file.FullName); + } + + /// + /// 读取文件的所有字节 + /// [Obsolete("请直接使用 File.ReadAllBytes(file.FullName)")] + /// + [Obsolete("请直接使用 File.ReadAllBytes(file.FullName)", false)] + public static byte[] ReadAllBytes(this FileInfo file) + { + if (file == null || !file.Exists) + return Array.Empty(); + + return File.ReadAllBytes(file.FullName); + } + + /// + /// 写入文本内容到文件 + /// [Obsolete("请直接使用 File.WriteAllText(file.FullName, content)")] + /// + [Obsolete("请直接使用 File.WriteAllText(file.FullName, content)", false)] + public static void WriteAllText(this FileInfo file, string content) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + + // 确保目录存在 + if (!file.Directory.Exists) + file.Directory.Create(); + + File.WriteAllText(file.FullName, content); + } + + /// + /// 写入字节到文件 + /// [Obsolete("请直接使用 File.WriteAllBytes(file.FullName, content)")] + /// + [Obsolete("请直接使用 File.WriteAllBytes(file.FullName, content)", false)] + public static void WriteAllBytes(this FileInfo file, byte[] content) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + + // 确保目录存在 + if (!file.Directory.Exists) + file.Directory.Create(); + + File.WriteAllBytes(file.FullName, content); + } + + #endregion + + #region DirectoryInfo 扩展 + + /// + /// 获取目录的总大小(包含所有子目录) + /// + public static long GetTotalSize(this DirectoryInfo directory) + { + if (directory == null || !directory.Exists) + return 0; + + long size = 0; + + try + { + size += directory.GetFiles().Sum(f => f.Length); + size += directory.GetDirectories().Sum(d => GetTotalSize(d)); + } + catch (UnauthorizedAccessException) + { + // 忽略无权限访问的目录 + } + + return size; + } + + /// + /// 获取目录的总大小(格式化字符串) + /// + public static string GetTotalSizeFormatted(this DirectoryInfo directory) + { + return directory.GetTotalSize().ToFileSize(); + } + + /// + /// 获取目录中的所有文件(包含子目录) + /// + public static FileInfo[] GetAllFiles(this DirectoryInfo directory, string searchPattern = "*.*") + { + if (directory == null || !directory.Exists) + return Array.Empty(); + + try + { + var files = directory.GetFiles(searchPattern, SearchOption.AllDirectories); + return files; + } + catch (UnauthorizedAccessException) + { + return Array.Empty(); + } + } + + /// + /// 清空目录(删除所有文件和子目录) + /// + public static void Clear(this DirectoryInfo directory) + { + if (directory == null || !directory.Exists) + return; + + foreach (var file in directory.GetFiles()) + { + file.Delete(); + } + + foreach (var subDir in directory.GetDirectories()) + { + subDir.Delete(true); + } + } + + /// + /// 安全删除目录(如果存在) + /// + public static bool DeleteIfExists(this DirectoryInfo directory, bool recursive = false) + { + if (directory == null || !directory.Exists) + return false; + + try + { + directory.Delete(recursive); + return true; + } + catch + { + return false; + } + } + + /// + /// 确保目录存在,不存在则创建 + /// + public static DirectoryInfo EnsureExists(this DirectoryInfo directory) + { + if (directory == null) + throw new ArgumentNullException(nameof(directory)); + + if (!directory.Exists) + directory.Create(); + + return directory; + } + + /// + /// 复制目录到指定位置 + /// + public static DirectoryInfo CopyTo(this DirectoryInfo sourceDir, string targetPath) + { + if (sourceDir == null) + throw new ArgumentNullException(nameof(sourceDir)); + + var targetDir = Directory.CreateDirectory(targetPath); + + // 复制文件 + foreach (var file in sourceDir.GetFiles()) + { + string targetFilePath = Path.Combine(targetPath, file.Name); + file.CopyTo(targetFilePath, true); + } + + // 递归复制子目录 + foreach (var subDir in sourceDir.GetDirectories()) + { + string targetSubDirPath = Path.Combine(targetPath, subDir.Name); + subDir.CopyTo(targetSubDirPath); + } + + return targetDir; + } + + #endregion + + #region 路径扩展 + + /// + /// 获取相对路径 + /// + /// 绝对路径 + /// 参考路径 + public static string GetRelativePath(string absolutePath, string relativeTo) + { + if (string.IsNullOrEmpty(absolutePath)) + throw new ArgumentNullException(nameof(absolutePath)); + + if (string.IsNullOrEmpty(relativeTo)) + throw new ArgumentNullException(nameof(relativeTo)); + + absolutePath = Path.GetFullPath(absolutePath); + relativeTo = Path.GetFullPath(relativeTo); + + // 从 .NET Core 2.0 / .NET Standard 2.1 开始,可以使用 Path.GetRelativePath + // 这里提供一个兼容的实现 + var absolutePathParts = absolutePath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + var relativeToParts = relativeTo.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + int length = Math.Min(absolutePathParts.Length, relativeToParts.Length); + int lastCommonRoot = -1; + + for (int i = 0; i < length; i++) + { + if (string.Equals(absolutePathParts[i], relativeToParts[i], StringComparison.OrdinalIgnoreCase)) + { + lastCommonRoot = i; + } + else + { + break; + } + } + + if (lastCommonRoot == -1) + return absolutePath; + + var relativePath = new System.Text.StringBuilder(); + + // 添加 .. + for (int i = lastCommonRoot + 1; i < relativeToParts.Length; i++) + { + if (relativePath.Length > 0) + relativePath.Append(Path.DirectorySeparatorChar); + + relativePath.Append(".."); + } + + // 添加目标路径的剩余部分 + for (int i = lastCommonRoot + 1; i < absolutePathParts.Length; i++) + { + if (relativePath.Length > 0) + relativePath.Append(Path.DirectorySeparatorChar); + + relativePath.Append(absolutePathParts[i]); + } + + return relativePath.ToString(); + } + + /// + /// 确保路径以目录分隔符结尾 + /// + public static string EnsureEndsWithSeparator(this string path) + { + if (string.IsNullOrEmpty(path)) + return path; + + char lastChar = path[path.Length - 1]; + if (lastChar != Path.DirectorySeparatorChar && lastChar != Path.AltDirectorySeparatorChar) + { + return path + Path.DirectorySeparatorChar; + } + + return path; + } + + #endregion + + #region 文件扩展名扩展 + + /// + /// 获取文件扩展名对应的 MIME 类型 + /// + public static string GetMimeType(this string extension) + { + if (string.IsNullOrEmpty(extension)) + return "application/octet-stream"; + + // 确保扩展名以 . 开头 + if (!extension.StartsWith(".")) + extension = "." + extension; + + return extension.ToLowerInvariant() switch + { + // 文本 + ".html" => "text/html", + ".htm" => "text/html", + ".css" => "text/css", + ".js" => "application/javascript", + ".json" => "application/json", + ".xml" => "application/xml", + ".txt" => "text/plain", + + // 图片 + ".jpg" => "image/jpeg", + ".jpeg" => "image/jpeg", + ".png" => "image/png", + ".gif" => "image/gif", + ".bmp" => "image/bmp", + ".webp" => "image/webp", + ".ico" => "image/x-icon", + ".svg" => "image/svg+xml", + + // 视频 + ".mp4" => "video/mp4", + ".avi" => "video/x-msvideo", + ".mov" => "video/quicktime", + ".wmv" => "video/x-ms-wmv", + ".flv" => "video/x-flv", + ".mkv" => "video/x-matroska", + ".webm" => "video/webm", + + // 音频 + ".mp3" => "audio/mpeg", + ".wav" => "audio/wav", + ".flac" => "audio/flac", + ".aac" => "audio/aac", + ".ogg" => "audio/ogg", + ".wma" => "audio/x-ms-wma", + ".m4a" => "audio/mp4", + + // 文档 + ".pdf" => "application/pdf", + ".doc" => "application/msword", + ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ".xls" => "application/vnd.ms-excel", + ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ".ppt" => "application/vnd.ms-powerpoint", + ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", + ".zip" => "application/zip", + ".rar" => "application/x-rar-compressed", + ".7z" => "application/x-7z-compressed", + + _ => "application/octet-stream" + }; + } + + #endregion + } +} diff --git a/EasyTool.Core/IOCategory/FileTypeUtil.cs b/EasyTool.Core/IOCategory/FileTypeUtil.cs index 5cb71cb..09b077b 100644 --- a/EasyTool.Core/IOCategory/FileTypeUtil.cs +++ b/EasyTool.Core/IOCategory/FileTypeUtil.cs @@ -38,7 +38,7 @@ public static string GetType(FileInfo file) header += buffer[i].ToString(); } - string type = null; + string? type = null; switch (header) { case "255216": // jpg diff --git a/EasyTool.Core/IOCategory/FileUtil.cs b/EasyTool.Core/IOCategory/FileUtil.cs index 772ca51..857d294 100644 --- a/EasyTool.Core/IOCategory/FileUtil.cs +++ b/EasyTool.Core/IOCategory/FileUtil.cs @@ -304,9 +304,11 @@ public static FileInfo Touch(string path) /// /// 拷贝文件 + /// [Obsolete("请直接使用 File.Copy(src, dest)")] /// /// 源文件路径 /// 目标文件路径 + [Obsolete("请直接使用 File.Copy(src, dest)", false)] public static void Cp(string src, string dest) { try @@ -398,9 +400,11 @@ public static bool Copy(string src, string dest, bool isOverride) /// /// 移动文件或重命名文件 + /// [Obsolete("请直接使用 File.Move(src, dest)")] /// /// 源文件路径 /// 目标文件路径 + [Obsolete("请直接使用 File.Move(src, dest)", false)] public static void Mv(string src, string dest) { try @@ -537,9 +541,11 @@ public static FileInfo Rename(FileInfo file, string newName, bool isRetainExt, b /// /// 获取绝对路径 + /// [Obsolete("请直接使用 Path.GetFullPath(path)")] /// /// 相对路径 /// 绝对路径 + [Obsolete("请直接使用 Path.GetFullPath(path)", false)] public static string GetAbsolutePath(string path) { if (!Path.IsPathRooted(path)) @@ -766,8 +772,10 @@ public static string SubPath(string dirPath, string filePath) /// /// 删除文件 + /// [Obsolete("请直接使用 File.Delete(path)")] /// /// 文件路径 + [Obsolete("请直接使用 File.Delete(path)", false)] public static void Rm(string path) { try @@ -782,8 +790,10 @@ public static void Rm(string path) /// /// 创建目录 + /// [Obsolete("请直接使用 Directory.CreateDirectory(path)")] /// /// 目录路径 + [Obsolete("请直接使用 Directory.CreateDirectory(path)", false)] public static void Mkdir(string path) { try @@ -798,8 +808,10 @@ public static void Mkdir(string path) /// /// 删除目录 + /// [Obsolete("请直接使用 Directory.Delete(path)")] /// /// 目录路径 + [Obsolete("请直接使用 Directory.Delete(path)", false)] public static void Rmdir(string path) { try @@ -815,9 +827,11 @@ public static void Rmdir(string path) /// /// 获取文件名 + /// [Obsolete("请直接使用 file?.Name")] /// /// 文件 /// 文件名 + [Obsolete("请直接使用 file?.Name", false)] public static string GetFileName(FileInfo file) { if (file == null) @@ -1146,10 +1160,12 @@ public static string ReadString(Uri url, Encoding? encoding = null) /// /// 从文件中读取每一行数据 + /// [Obsolete("请直接使用 File.ReadAllLines(path, encoding)")] /// /// 文件路径 /// 编码格式,默认为UTF-8 /// + [Obsolete("请直接使用 File.ReadAllLines(path, encoding)", false)] public static string[] ReadAllLines(string path, Encoding? encoding = null) { // 如果未指定编码格式,则默认为 UTF-8 @@ -1194,8 +1210,10 @@ public static Stream GetOutputStream(string path) /// /// 获取当前系统的换行分隔符 + /// [Obsolete("请直接使用 Environment.NewLine")] /// /// 换行分隔符 + [Obsolete("请直接使用 Environment.NewLine", false)] public static string GetLineSeparator() { return Environment.NewLine; @@ -1231,7 +1249,7 @@ public static FileInfo WriteString(string content, string path, Encoding? encodi /// 文件路径 /// 编码格式,默认为UTF-8 /// 文件信息 - public static FileInfo AppendString(string content, string path, Encoding encoding = null) + public static FileInfo AppendString(string content, string path, Encoding? encoding = null) { if (encoding == null) { @@ -1251,7 +1269,7 @@ public static FileInfo AppendString(string content, string path, Encoding encodi /// 文件路径 /// 编码格式,默认为UTF-8 /// 文件信息 - public static FileInfo WriteLines(List list, string path, Encoding encoding = null) + public static FileInfo WriteLines(List list, string path, Encoding? encoding = null) { if (encoding == null) { diff --git a/EasyTool.Core/IOCategory/IoUtil.cs b/EasyTool.Core/IOCategory/IoUtil.cs index 96cc1b9..d2a647f 100644 --- a/EasyTool.Core/IOCategory/IoUtil.cs +++ b/EasyTool.Core/IOCategory/IoUtil.cs @@ -13,9 +13,11 @@ public static class IoUtil { /// /// 读取文件的所有行到一个字符串数组中 + /// [Obsolete("请直接使用 File.ReadAllLines(path)")] /// /// 文件路径 /// 字符串数组,其中包含文件的所有行。 + [Obsolete("请直接使用 File.ReadAllLines(path)", false)] public static string[] ReadAllLines(string path) { return File.ReadAllLines(path); @@ -23,9 +25,11 @@ public static string[] ReadAllLines(string path) /// /// 将字符串数组写入文件,覆盖原有内容 + /// [Obsolete("请直接使用 File.WriteAllLines(path, lines)")] /// /// 文件路径 /// 待写入的字符串数组 + [Obsolete("请直接使用 File.WriteAllLines(path, lines)", false)] public static void WriteAllLines(string path, string[] lines) { File.WriteAllLines(path, lines); @@ -33,9 +37,11 @@ public static void WriteAllLines(string path, string[] lines) /// /// 读取整个文件到一个字符串中 + /// [Obsolete("请直接使用 File.ReadAllText(path)")] /// /// 文件路径 /// 文件的所有内容 + [Obsolete("请直接使用 File.ReadAllText(path)", false)] public static string ReadAllText(string path) { return File.ReadAllText(path); @@ -43,9 +49,11 @@ public static string ReadAllText(string path) /// /// 将字符串写入文件,覆盖原有内容 + /// [Obsolete("请直接使用 File.WriteAllText(path, text)")] /// /// 文件路径 /// 待写入的字符串 + [Obsolete("请直接使用 File.WriteAllText(path, text)", false)] public static void WriteAllText(string path, string text) { File.WriteAllText(path, text); @@ -53,9 +61,11 @@ public static void WriteAllText(string path, string text) /// /// 读取二进制数据到一个字节数组中 + /// [Obsolete("请直接使用 File.ReadAllBytes(path)")] /// /// 文件路径 /// + [Obsolete("请直接使用 File.ReadAllBytes(path)", false)] public static byte[] ReadAllBytes(string path) { return File.ReadAllBytes(path); @@ -63,9 +73,11 @@ public static byte[] ReadAllBytes(string path) /// /// 将字节数组写入二进制文件,覆盖原有内容 + /// [Obsolete("请直接使用 File.WriteAllBytes(path, bytes)")] /// /// 文件路径 /// 待写入的字节数组 + [Obsolete("请直接使用 File.WriteAllBytes(path, bytes)", false)] public static void WriteAllBytes(string path, byte[] bytes) { File.WriteAllBytes(path, bytes); @@ -141,9 +153,11 @@ public static void WriteMemoryStream(MemoryStream stream, byte[] bytes) /// /// 将一个字符串转换为字节数组 + /// [Obsolete("请直接使用 Encoding.UTF8.GetBytes(text)")] /// /// 待转换的字符串 /// 字节数组,其中包含输入字符串的编码数据 + [Obsolete("请直接使用 Encoding.UTF8.GetBytes(text)", false)] public static byte[] StringToBytes(string text) { return Encoding.UTF8.GetBytes(text); @@ -151,9 +165,11 @@ public static byte[] StringToBytes(string text) /// /// 将一个字节数组转换为字符串 + /// [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)")] /// /// 待转换的字节数组 /// 字符串,其中包含输入字节数组的编码数据 + [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)", false)] public static string BytesToString(byte[] bytes) { return Encoding.UTF8.GetString(bytes); diff --git a/EasyTool.Core/IOCategory/StreamExtension.cs b/EasyTool.Core/IOCategory/StreamExtension.cs new file mode 100644 index 0000000..a8c729b --- /dev/null +++ b/EasyTool.Core/IOCategory/StreamExtension.cs @@ -0,0 +1,365 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace EasyTool.Extension +{ + /// + /// Stream 扩展方法 + /// + public static class StreamExtension + { + #region 读取操作 + + /// + /// 读取流中的所有字节 + /// + public static byte[] ReadAllBytes(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + using var ms = new MemoryStream(); + stream.CopyTo(ms); + return ms.ToArray(); + } + + /// + /// 异步读取流中的所有字节 + /// + public static async Task ReadAllBytesAsync(this Stream stream, CancellationToken cancellationToken = default) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms, cancellationToken); + return ms.ToArray(); + } + + /// + /// 读取流中的所有文本(UTF-8 编码) + /// + public static string ReadAllText(this Stream stream) + { + return stream.ReadAllText(Encoding.UTF8); + } + + /// + /// 读取流中的所有文本 + /// + public static string ReadAllText(this Stream stream, Encoding encoding) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + encoding ??= Encoding.UTF8; + + using var reader = new StreamReader(stream, encoding, true); + return reader.ReadToEnd(); + } + + /// + /// 异步读取流中的所有文本(UTF-8 编码) + /// + public static Task ReadAllTextAsync(this Stream stream, CancellationToken cancellationToken = default) + { + return stream.ReadAllTextAsync(Encoding.UTF8, cancellationToken); + } + + /// + /// 异步读取流中的所有文本 + /// + public static async Task ReadAllTextAsync(this Stream stream, Encoding encoding, CancellationToken cancellationToken = default) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + encoding ??= Encoding.UTF8; + + using var reader = new StreamReader(stream, encoding, true); + return await reader.ReadToEndAsync(); + } + + /// + /// 读取流中的所有行 + /// + public static string[] ReadAllLines(this Stream stream, Encoding? encoding = null) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + encoding ??= Encoding.UTF8; + + using var reader = new StreamReader(stream, encoding, true); + var lines = new System.Collections.Generic.List(); + string? line; + while ((line = reader.ReadLine()) != null) + { + lines.Add(line); + } + return lines.ToArray(); + } + + #endregion + + #region 写入操作 + + /// + /// 将字节写入流 + /// + public static void WriteBytes(this Stream stream, byte[] bytes) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (bytes == null || bytes.Length == 0) + return; + + stream.Write(bytes, 0, bytes.Length); + } + + /// + /// 异步将字节写入流 + /// + public static async Task WriteBytesAsync(this Stream stream, byte[] bytes, CancellationToken cancellationToken = default) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (bytes == null || bytes.Length == 0) + return; + + await stream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); + } + + /// + /// 将文本写入流(UTF-8 编码) + /// + public static void WriteText(this Stream stream, string text) + { + stream.WriteText(text, Encoding.UTF8); + } + + /// + /// 将文本写入流 + /// + public static void WriteText(this Stream stream, string text, Encoding encoding) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (string.IsNullOrEmpty(text)) + return; + + encoding ??= Encoding.UTF8; + + var bytes = encoding.GetBytes(text); + stream.Write(bytes, 0, bytes.Length); + } + + /// + /// 异步将文本写入流(UTF-8 编码) + /// + public static Task WriteTextAsync(this Stream stream, string text, CancellationToken cancellationToken = default) + { + return stream.WriteTextAsync(text, Encoding.UTF8, cancellationToken); + } + + /// + /// 异步将文本写入流 + /// + public static async Task WriteTextAsync(this Stream stream, string text, Encoding encoding, CancellationToken cancellationToken = default) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (string.IsNullOrEmpty(text)) + return; + + encoding ??= Encoding.UTF8; + + var bytes = encoding.GetBytes(text); + await stream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); + } + + #endregion + + #region 复制操作 + + /// + /// 将流复制到另一个流 + /// [Obsolete("请直接使用 source.CopyTo(destination)")] + /// + [Obsolete("请直接使用 source.CopyTo(destination)", false)] + public static void CopyTo(this Stream source, Stream destination) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + + source.CopyTo(destination, 81920); + } + + /// + /// 将流复制到另一个流(指定缓冲区大小) + /// [Obsolete("请直接使用 source.CopyTo(destination)")] + /// + [Obsolete("请直接使用 source.CopyTo(destination)", false)] + public static void CopyTo(this Stream source, Stream destination, int bufferSize) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + + if (!source.CanRead) + throw new InvalidOperationException("Source stream does not support reading."); + if (!destination.CanWrite) + throw new InvalidOperationException("Destination stream does not support writing."); + + var buffer = new byte[bufferSize]; + int bytesRead; + while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) + { + destination.Write(buffer, 0, bytesRead); + } + } + + /// + /// 将流复制到字节数组 + /// + public static byte[] CopyToByteArray(this Stream stream) + { + return stream.ReadAllBytes(); + } + + /// + /// 将流复制到内存流 + /// + public static MemoryStream CopyToMemoryStream(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + var ms = new MemoryStream(); + stream.CopyTo(ms); + return ms; + } + + #endregion + + #region 位置操作 + + /// + /// 将流位置重置到开头 + /// [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.Begin)")] + /// + [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.Begin)", false)] + public static void ResetPosition(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + } + + /// + /// 将流位置重置到末尾 + /// [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.End)")] + /// + [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.End)", false)] + public static void SeekToEnd(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.End); + } + } + + #endregion + + #region 转换操作 + + /// + /// 将流转为 Base64 字符串 + /// + public static string ToBase64(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + var bytes = stream.ReadAllBytes(); + return Convert.ToBase64String(bytes); + } + + /// + /// 将流转为十六进制字符串 + /// + public static string ToHex(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + var bytes = stream.ReadAllBytes(); + return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant(); + } + + #endregion + + #region 检查操作 + + /// + /// 判断流是否为空 + /// + public static bool IsEmpty(this Stream? stream) + { + if (stream == null) + return true; + + if (stream.CanSeek) + { + return stream.Length == 0; + } + + return stream.ReadByte() == -1; + } + + #endregion + + #region 缓冲操作 + + /// + /// 使用缓冲读取器包装流 + /// + public static BufferedStream Buffer(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + return new BufferedStream(stream); + } + + /// + /// 使用缓冲读取器包装流(指定缓冲区大小) + /// + public static BufferedStream Buffer(this Stream stream, int bufferSize) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + return new BufferedStream(stream, bufferSize); + } + + #endregion + } +} diff --git a/EasyTool.Core/IOCategory/Tailer.cs b/EasyTool.Core/IOCategory/Tailer.cs index b8df260..1a6d6f8 100644 --- a/EasyTool.Core/IOCategory/Tailer.cs +++ b/EasyTool.Core/IOCategory/Tailer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -16,7 +16,7 @@ public class Tailer : IDisposable private readonly Timer timer; // 定时器,用于定期检查文件是否有新内容 // 定义事件,用于通知外部监听器 - public event EventHandler NewLine; + public event EventHandler? NewLine; // 构造函数,初始化文件路径、StreamReader 和定时器 public Tailer(string filePath) @@ -48,7 +48,7 @@ private void OnTimerCallback(object state) return; // 逐行读取文件内容 - string line; + string? line; while ((line = reader.ReadLine()) != null) { // 如果有新行,触发 NewLine 事件 diff --git a/EasyTool.Core/IOCategory/WatchMonitor.cs b/EasyTool.Core/IOCategory/WatchMonitor.cs index d9a6634..226e44c 100644 --- a/EasyTool.Core/IOCategory/WatchMonitor.cs +++ b/EasyTool.Core/IOCategory/WatchMonitor.cs @@ -13,11 +13,11 @@ public class WatchMonitor private readonly FileSystemWatcher watcher; // 定义事件,用于通知外部监听器 - public event EventHandler FileChanged; - public event EventHandler FileCreated; - public event EventHandler FileDeleted; - public event EventHandler FileMissing; - public event EventHandler FileError; + public event EventHandler? FileChanged; + public event EventHandler? FileCreated; + public event EventHandler? FileDeleted; + public event EventHandler? FileMissing; + public event EventHandler? FileError; /// /// 构造函数,初始化 FileSystemWatcher 实例 @@ -109,7 +109,7 @@ private void OnFileError(object sender, ErrorEventArgs e) { if (FileError != null) { - FileError(this, new FileEventArgs(e.GetException())); + FileError(this, new FileEventArgs(e.GetException()!)); } } diff --git a/EasyTool.Core/LanguageCategory/TreeUtil.cs b/EasyTool.Core/LanguageCategory/TreeUtil.cs index f437583..7003b9b 100644 --- a/EasyTool.Core/LanguageCategory/TreeUtil.cs +++ b/EasyTool.Core/LanguageCategory/TreeUtil.cs @@ -25,10 +25,10 @@ public TreeUtil(List> nodes) /// 构建树结构 /// /// 根节点 - public TreeNode BuildTree() + public TreeNode? BuildTree() { // 获取根节点 - var root = _nodes.FirstOrDefault(n => n.ParentId.Equals(default(T))); + var root = _nodes.FirstOrDefault(n => n.ParentId == null || n.ParentId.Equals(default(T))); if (root == null) { @@ -43,7 +43,7 @@ public TreeNode BuildTree() private void BuildTree(TreeNode node) { - node.Children = _nodes.Where(n => n.ParentId.Equals(node.Id)).ToList(); + node.Children = _nodes.Where(n => n.ParentId != null && n.ParentId.Equals(node.Id)).ToList(); if (node.Children.Count > 0) { @@ -105,9 +105,9 @@ private void GetDescendants(TreeNode node, List> descendant } } - private TreeNode GetParent(T id) + private TreeNode? GetParent(T id) { - return _nodes.FirstOrDefault(n => n.Id.Equals(id)); + return _nodes.FirstOrDefault(n => n.Id != null && n.Id.Equals(id)); } /// @@ -122,7 +122,7 @@ public List> GetSiblings(TreeNode node) { return new List>(); } - return parent.Children.Where(n => !n.Id.Equals(node.Id)).ToList(); + return parent.Children.Where(n => n.Id == null || !n.Id.Equals(node.Id)).ToList(); } /// @@ -171,7 +171,7 @@ public int GetMinDepth() public TreeNode? GetNextSibling(TreeNode node) { var siblings = GetSiblings(node); - var index = siblings.FindIndex(n => n.Id!.Equals(node.Id)); + var index = siblings.FindIndex(n => n.Id != null && n.Id.Equals(node.Id)); return index + 1 < siblings.Count ? siblings[index + 1] : null; } @@ -183,7 +183,7 @@ public int GetMinDepth() public TreeNode? GetPreviousSibling(TreeNode node) { var siblings = GetSiblings(node); - var index = siblings.FindIndex(n => n.Id!.Equals(node.Id)); + var index = siblings.FindIndex(n => n.Id != null && n.Id.Equals(node.Id)); return index - 1 >= 0 ? siblings[index - 1] : null; } @@ -283,13 +283,13 @@ public int GetMinWeight() public class TreeNode { public T Id { get; set; } - public T ParentId { get; set; } - public string Name { get; set; } + public T? ParentId { get; set; } + public string Name { get; set; } = string.Empty; public int Weight { get; set; } - public D Data { get; set; } - public List> Children { get; set; } + public D Data { get; set; } = default!; + public List> Children { get; set; } = new List>(); - public TreeNode(T id, T parentId, string name, int weight, D data) + public TreeNode(T id, T? parentId, string name, int weight, D data) { this.Id = id; this.ParentId = parentId; diff --git a/EasyTool.Core/MathCategory/MathUtil.cs b/EasyTool.Core/MathCategory/MathUtil.cs index df37079..76010ec 100644 --- a/EasyTool.Core/MathCategory/MathUtil.cs +++ b/EasyTool.Core/MathCategory/MathUtil.cs @@ -124,10 +124,12 @@ public static int CountBits(int n) /// /// 求两个浮点数的平均值 + /// [Obsolete("请直接使用 (a + b) / 2")] /// /// 第一个浮点数 /// 第二个浮点数 /// 两个浮点数的平均值 + [Obsolete("请直接使用 (a + b) / 2", false)] public static double Average(double a, double b) { return (a + b) / 2; diff --git a/EasyTool.Core/MathCategory/NumberUtil.cs b/EasyTool.Core/MathCategory/NumberUtil.cs index a55a70f..ad84f16 100644 --- a/EasyTool.Core/MathCategory/NumberUtil.cs +++ b/EasyTool.Core/MathCategory/NumberUtil.cs @@ -123,10 +123,12 @@ public static decimal Div(double a, double b, int decimalPlaces) /// /// 格式化一个 decimal 数字 + /// [Obsolete("请直接使用 number.ToString(format)")] /// /// 待格式化的数字 /// 格式化字符串 /// 格式化后的字符串 + [Obsolete("请直接使用 number.ToString(format)", false)] public static string DecimalFormat(decimal number, string format) { return number.ToString(format); diff --git a/EasyTool.Core/NetCategory/HttpClientExtension.cs b/EasyTool.Core/NetCategory/HttpClientExtension.cs index 452804b..2c4654e 100644 --- a/EasyTool.Core/NetCategory/HttpClientExtension.cs +++ b/EasyTool.Core/NetCategory/HttpClientExtension.cs @@ -13,7 +13,7 @@ namespace EasyTool.Extension public static class HttpClientExtension { private const string NetErrorMessage = "网络异常"; - + #region 标准的Http请求扩展 /// diff --git a/EasyTool.Core/ToolCategory/ArrayUtil.cs b/EasyTool.Core/ToolCategory/ArrayUtil.cs index ca24653..fdf234c 100644 --- a/EasyTool.Core/ToolCategory/ArrayUtil.cs +++ b/EasyTool.Core/ToolCategory/ArrayUtil.cs @@ -21,9 +21,11 @@ public static bool IsEmpty(Array array) /// /// 获取数组的长度 + /// [Obsolete("请直接使用 array?.Length ?? 0")] /// /// 要获取长度的数组 /// 返回数组的长度 + [Obsolete("请直接使用 array?.Length ?? 0", false)] public static int Length(Array array) { if (array == null) @@ -36,9 +38,11 @@ public static int Length(Array array) /// /// 获取数组中的最大值 + /// [Obsolete("请直接使用 array.Max() (LINQ)")] /// /// 要获取最大值的数组 /// 返回数组中的最大值 + [Obsolete("请直接使用 array.Max() (LINQ)", false)] public static T Max(T[] array) where T : IComparable { if (IsEmpty(array)) @@ -60,9 +64,11 @@ public static T Max(T[] array) where T : IComparable /// /// 获取数组中的最小值 + /// [Obsolete("请直接使用 array.Min() (LINQ)")] /// /// 要获取最小值的数组 /// 返回数组中的最小值 + [Obsolete("请直接使用 array.Min() (LINQ)", false)] public static T Min(T[] array) where T : IComparable { if (IsEmpty(array)) @@ -83,9 +89,11 @@ public static T Min(T[] array) where T : IComparable /// /// 获取数组中的和 + /// [Obsolete("请直接使用 array.Sum() (LINQ)")] /// /// 要获取和的数组 /// 返回数组的和 + [Obsolete("请直接使用 array.Sum() (LINQ)", false)] public static int Sum(int[] array) { if (IsEmpty(array)) @@ -104,9 +112,11 @@ public static int Sum(int[] array) /// /// 获取数组的平均值 + /// [Obsolete("请直接使用 array.Average() (LINQ)")] /// /// 要获取平均值的数组 /// 返回数组的平均值 + [Obsolete("请直接使用 array.Average() (LINQ)", false)] public static double Average(int[] array) { if (IsEmpty(array)) @@ -160,10 +170,12 @@ public static T[] Reverse(T[] array) /// /// 判断数组是否包含某个元素 + /// [Obsolete("请直接使用 array.Contains(item) (LINQ)")] /// /// 要操作的数组 /// 要判断的元素 /// 如果数组中包含该元素,则返回 true;否则返回 false + [Obsolete("请直接使用 array.Contains(item) (LINQ)", false)] public static bool Contains(T[] array, T item) { if (IsEmpty(array)) diff --git a/EasyTool.Core/ToolCategory/ClassUtil.cs b/EasyTool.Core/ToolCategory/ClassUtil.cs index b3ad3d4..5ee1647 100644 --- a/EasyTool.Core/ToolCategory/ClassUtil.cs +++ b/EasyTool.Core/ToolCategory/ClassUtil.cs @@ -12,9 +12,11 @@ public class ClassUtil { /// /// 获取类的完全限定名 + /// [Obsolete("请直接使用 type.FullName")] /// /// 要获取名称的类 /// 类的完全限定名 + [Obsolete("请直接使用 type.FullName", false)] public static string GetClassName(Type type) { return type.FullName; @@ -22,9 +24,11 @@ public static string GetClassName(Type type) /// /// 获取类的命名空间 + /// [Obsolete("请直接使用 type.Namespace")] /// /// 要获取命名空间的类 /// 类的命名空间 + [Obsolete("请直接使用 type.Namespace", false)] public static string GetClassNamespace(Type type) { return type.Namespace; @@ -50,9 +54,11 @@ public static Type[] GetClassHierarchy(Type type) /// /// 获取类的所有方法 + /// [Obsolete("请直接使用 type.GetMethods()")] /// /// 要获取方法的类 /// 类的所有方法 + [Obsolete("请直接使用 type.GetMethods()", false)] public static MethodInfo[] GetClassMethods(Type type) { return type.GetMethods(); @@ -60,9 +66,11 @@ public static MethodInfo[] GetClassMethods(Type type) /// /// 获取类的所有属性 + /// [Obsolete("请直接使用 type.GetProperties()")] /// /// 要获取属性的类 /// 类的所有属性 + [Obsolete("请直接使用 type.GetProperties()", false)] public static PropertyInfo[] GetClassProperties(Type type) { return type.GetProperties(); @@ -70,9 +78,11 @@ public static PropertyInfo[] GetClassProperties(Type type) /// /// 获取类的所有字段 + /// [Obsolete("请直接使用 type.GetFields()")] /// /// 要获取字段的类 /// 类的所有字段 + [Obsolete("请直接使用 type.GetFields()", false)] public static FieldInfo[] GetClassFields(Type type) { return type.GetFields(); @@ -80,9 +90,11 @@ public static FieldInfo[] GetClassFields(Type type) /// /// 获取类的所有事件 + /// [Obsolete("请直接使用 type.GetEvents()")] /// /// 要获取事件的类 /// 类的所有事件 + [Obsolete("请直接使用 type.GetEvents()", false)] public static EventInfo[] GetClassEvents(Type type) { return type.GetEvents(); @@ -90,9 +102,11 @@ public static EventInfo[] GetClassEvents(Type type) /// /// 获取类的所有构造函数 + /// [Obsolete("请直接使用 type.GetConstructors()")] /// /// 要获取构造函数的类 /// 类的所有构造函数 + [Obsolete("请直接使用 type.GetConstructors()", false)] public static ConstructorInfo[] GetClassConstructors(Type type) { return type.GetConstructors(); @@ -100,9 +114,11 @@ public static ConstructorInfo[] GetClassConstructors(Type type) /// /// 获取类的默认构造函数 + /// [Obsolete("请直接使用 type.GetConstructor(Type.EmptyTypes)")] /// /// 要获取默认构造函数的类 /// 类的默认构造函数 + [Obsolete("请直接使用 type.GetConstructor(Type.EmptyTypes)", false)] public static ConstructorInfo GetDefaultClassConstructor(Type type) { return type.GetConstructor(Type.EmptyTypes); diff --git a/EasyTool.Core/ToolCategory/ColorExtension.cs b/EasyTool.Core/ToolCategory/ColorExtension.cs new file mode 100644 index 0000000..0298c0a --- /dev/null +++ b/EasyTool.Core/ToolCategory/ColorExtension.cs @@ -0,0 +1,348 @@ +using System; +using System.Drawing; + +namespace EasyTool.Extension +{ + /// + /// Color 颜色扩展方法 + /// + public static class ColorExtension + { + #region 转换方法 + + /// + /// 将颜色转换为16进制字符串 + /// + public static string ToHex(this Color color) + { + return color.ToHex(false); + } + + /// + /// 将颜色转换为16进制字符串 + /// + public static string ToHex(this Color color, bool includeAlpha) + { + if (includeAlpha) + return $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; + else + return $"#{color.R:X2}{color.G:X2}{color.B:X2}"; + } + + /// + /// 从16进制字符串创建颜色 + /// + public static Color FromHex(string hex) + { + if (string.IsNullOrEmpty(hex)) + return Color.Empty; + + hex = hex.TrimStart('#'); + + if (hex.Length == 6) + { + int r = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + int g = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + int b = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + return Color.FromArgb(r, g, b); + } + else if (hex.Length == 8) + { + int a = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + int r = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + int g = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + int b = int.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); + return Color.FromArgb(a, r, g, b); + } + + throw new ArgumentException("Invalid hex color format", nameof(hex)); + } + + /// + /// 将颜色转换为 RGB 字符串 + /// + public static string ToRgbString(this Color color) + { + return $"rgb({color.R}, {color.G}, {color.B})"; + } + + /// + /// 将颜色转换为 RGBA 字符串 + /// + public static string ToRgbaString(this Color color) + { + return $"rgba({color.R}, {color.G}, {color.B}, {color.A / 255f:F2})"; + } + + /// + /// 将颜色转换为 HSL + /// + public static (double h, double s, double l) ToHsl(this Color color) + { + double r = color.R / 255d; + double g = color.G / 255d; + double b = color.B / 255d; + + double max = Math.Max(r, Math.Max(g, b)); + double min = Math.Min(r, Math.Min(g, b)); + double h = 0, s = 0, l = (max + min) / 2d; + + if (max != min) + { + double d = max - min; + s = l > 0.5d ? d / (2d - max - min) : d / (max + min); + + if (max == r) + h = (g - b) / d + (g < b ? 6d : 0d); + else if (max == g) + h = (b - r) / d + 2d; + else + h = (r - g) / d + 4d; + + h /= 6d; + } + + return (h * 360d, s * 100d, l * 100d); + } + + /// + /// 从 HSL 创建颜色 + /// + public static Color FromHsl(double h, double s, double l) + { + h = h / 360d; + s = s / 100d; + l = l / 100d; + + double r, g, b; + + if (s == 0) + { + r = g = b = l; + } + else + { + double q = l < 0.5d ? l * (1d + s) : l + s - l * s; + double p = 2d * l - q; + + r = Hue2Rgb(p, q, h + 1d / 3d); + g = Hue2Rgb(p, q, h); + b = Hue2Rgb(p, q, h - 1d / 3d); + } + + return Color.FromArgb((int)(r * 255), (int)(g * 255), (int)(b * 255)); + } + + private static double Hue2Rgb(double p, double q, double t) + { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1d / 6d) return p + (q - p) * 6d * t; + if (t < 1d / 2d) return q; + if (t < 2d / 3d) return p + (q - p) * (2d / 3d - t) * 6d; + return p; + } + + #endregion + + #region 颜色调整 + + /// + /// 变亮颜色 + /// + public static Color Lighten(this Color color, double percent) + { + var (h, s, l) = color.ToHsl(); + l = Math.Min(100, l + percent); + return FromHsl(h, s, l); + } + + /// + /// 变暗颜色 + /// + public static Color Darken(this Color color, double percent) + { + var (h, s, l) = color.ToHsl(); + l = Math.Max(0, l - percent); + return FromHsl(h, s, l); + } + + /// + /// 调整颜色透明度 + /// + public static Color WithAlpha(this Color color, int alpha) + { + return Color.FromArgb(alpha, color.R, color.G, color.B); + } + + /// + /// 调整颜色透明度 + /// + public static Color WithAlpha(this Color color, double alphaPercent) + { + int alpha = (int)(alphaPercent * 255); + return Color.FromArgb(alpha, color.R, color.G, color.B); + } + + /// + /// 反转颜色 + /// + public static Color Invert(this Color color) + { + return Color.FromArgb(color.A, 255 - color.R, 255 - color.G, 255 - color.B); + } + + /// + /// 灰度化颜色 + /// + public static Color Grayscale(this Color color) + { + int gray = (int)(color.R * 0.299 + color.G * 0.587 + color.B * 0.114); + return Color.FromArgb(color.A, gray, gray, gray); + } + + /// + /// 混合两种颜色 + /// + public static Color Blend(this Color color1, Color color2, double percent) + { + int r = (int)(color1.R + (color2.R - color1.R) * percent); + int g = (int)(color1.G + (color2.G - color1.G) * percent); + int b = (int)(color1.B + (color2.B - color1.B) * percent); + int a = (int)(color1.A + (color2.A - color1.A) * percent); + return Color.FromArgb(a, r, g, b); + } + + /// + /// 获取互补色 + /// + public static Color Complementary(this Color color) + { + var (h, s, l) = color.ToHsl(); + h = (h + 180) % 360; + return FromHsl(h, s, l); + } + + /// + /// 获取类比色 + /// + public static Color[] Analogous(this Color color, int count = 3) + { + var (h, s, l) = color.ToHsl(); + var colors = new Color[count]; + + for (int i = 0; i < count; i++) + { + double hue = (h + (i * 30)) % 360; + colors[i] = FromHsl(hue, s, l); + } + + return colors; + } + + /// + /// 获取三色组合 + /// + public static Color[] Triadic(this Color color) + { + var (h, s, l) = color.ToHsl(); + return new[] + { + FromHsl(h, s, l), + FromHsl((h + 120) % 360, s, l), + FromHsl((h + 240) % 360, s, l) + }; + } + + #endregion + + #region 颜色判断 + + /// + /// 判断是否是深色 + /// + public static bool IsDark(this Color color) + { + // 使用亮度公式判断 + double brightness = (color.R * 299 + color.G * 587 + color.B * 114) / 1000d; + return brightness < 128; + } + + /// + /// 判断是否是浅色 + /// + public static bool IsLight(this Color color) + { + return !color.IsDark(); + } + + #endregion + + #region 命名颜色 + + /// + /// 从名称创建颜色 + /// [Obsolete("请直接使用 Color.FromName(name)")] + /// + [Obsolete("请直接使用 Color.FromName(name)", false)] + public static Color FromName(string name) + { + if (string.IsNullOrEmpty(name)) + return Color.Empty; + + return Color.FromName(name); + } + + /// + /// 获取颜色名称 + /// + public static string GetColorName(this Color color) + { + if (color.IsNamedColor) + return color.Name; + + return color.ToHex(); + } + + #endregion + + #region 颜色对比 + + /// + /// 计算两种颜色的对比度 + /// + public static double ContrastWith(this Color color1, Color color2) + { + double GetLuminance(Color c) + { + double r = c.R / 255d; + double g = c.G / 255d; + double b = c.B / 255d; + + r = r <= 0.03928 ? r / 12.92 : Math.Pow((r + 0.055) / 1.055, 2.4); + g = g <= 0.03928 ? g / 12.92 : Math.Pow((g + 0.055) / 1.055, 2.4); + b = b <= 0.03928 ? b / 12.92 : Math.Pow((b + 0.055) / 1.055, 2.4); + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; + } + + double l1 = GetLuminance(color1); + double l2 = GetLuminance(color2); + + double lighter = Math.Max(l1, l2); + double darker = Math.Min(l1, l2); + + return (lighter + 0.05) / (darker + 0.05); + } + + /// + /// 根据背景色选择合适的文本颜色(黑色或白色) + /// + public static Color GetReadableTextColor(this Color backgroundColor) + { + return backgroundColor.IsDark() ? Color.White : Color.Black; + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/CreditCodeUtil.cs b/EasyTool.Core/ToolCategory/CreditCodeUtil.cs index fc4704a..25b9e44 100644 --- a/EasyTool.Core/ToolCategory/CreditCodeUtil.cs +++ b/EasyTool.Core/ToolCategory/CreditCodeUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; @@ -89,20 +89,21 @@ public static string GenerateRandomCreditCode() /// /// 社会信用代码 /// 组织机构代码 - public static string GetOrgCodeFromCreditCode(string creditCode) + public static string? GetOrgCodeFromCreditCode(string? creditCode) { if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18) { return null; } - return creditCode.Substring(0, 9); - } + return creditCode.Substring(0, 9); + } + /// /// 从社会信用代码中提取企业类型 /// /// 社会信用代码 /// 企业类型 - public static string GetEntTypeFromCreditCode(string creditCode) + public static string? GetEntTypeFromCreditCode(string? creditCode) { if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18) { @@ -117,7 +118,7 @@ public static string GetEntTypeFromCreditCode(string creditCode) /// /// 社会信用代码 /// 注册号 - public static string GetRegNumFromCreditCode(string creditCode) + public static string? GetRegNumFromCreditCode(string? creditCode) { if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18) { @@ -132,7 +133,7 @@ public static string GetRegNumFromCreditCode(string creditCode) /// /// 社会信用代码 /// 行政区划码 - public static string GetAreaCodeFromCreditCode(string creditCode) + public static string? GetAreaCodeFromCreditCode(string? creditCode) { if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18) { @@ -147,7 +148,7 @@ public static string GetAreaCodeFromCreditCode(string creditCode) /// /// 社会信用代码 /// 机构类型 - public static string GetOrgTypeFromCreditCode(string creditCode) + public static string? GetOrgTypeFromCreditCode(string? creditCode) { if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18) { diff --git a/EasyTool.Core/ToolCategory/DLLUtil.cs b/EasyTool.Core/ToolCategory/DLLUtil.cs index e41e2ab..d14a199 100644 --- a/EasyTool.Core/ToolCategory/DLLUtil.cs +++ b/EasyTool.Core/ToolCategory/DLLUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Reflection; @@ -13,9 +13,11 @@ public class DLLUtil { /// /// 根据文件路径加载 DLL 程序集,并返回一个 Assembly 对象 + /// [Obsolete("请直接使用 Assembly.LoadFile(dllFilePath)")] /// /// DLL 文件路径 /// 返回一个 Assembly 对象 + [Obsolete("请直接使用 Assembly.LoadFile(dllFilePath)", false)] public static Assembly LoadAssembly(string dllFilePath) { return Assembly.LoadFile(dllFilePath); @@ -23,22 +25,26 @@ public static Assembly LoadAssembly(string dllFilePath) /// /// 根据类型名称从程序集中获取 Type 对象 + /// [Obsolete("请直接使用 assembly.GetType(typeName)")] /// /// 程序集 /// 类型名称 /// 返回 Type 对象 - public static Type GetTypeFromAssembly(Assembly assembly, string typeName) + [Obsolete("请直接使用 assembly.GetType(typeName)", false)] + public static Type? GetTypeFromAssembly(Assembly assembly, string typeName) { return assembly.GetType(typeName); } /// /// 创建指定类型的实例,并返回一个 Object 对象 + /// [Obsolete("请直接使用 Activator.CreateInstance(type, parameters)")] /// /// 要创建实例的类型 /// 实例化类型所需要的参数 /// 返回创建的实例对象 - public static object CreateInstance(Type type, params object[] parameters) + [Obsolete("请直接使用 Activator.CreateInstance(type, parameters)", false)] + public static object? CreateInstance(Type type, params object[] parameters) { return Activator.CreateInstance(type, parameters); } @@ -50,9 +56,9 @@ public static object CreateInstance(Type type, params object[] parameters) /// 类型名称 /// 实例化类型所需要的参数 /// 返回创建的实例对象 - public static object CreateInstanceFromAssembly(Assembly assembly, string typeName, params object[] parameters) + public static object? CreateInstanceFromAssembly(Assembly assembly, string typeName, params object[] parameters) { - Type type = GetTypeFromAssembly(assembly, typeName); + Type? type = GetTypeFromAssembly(assembly, typeName); if (type != null) { return CreateInstance(type, parameters); @@ -67,10 +73,10 @@ public static object CreateInstanceFromAssembly(Assembly assembly, string typeNa /// 方法名称 /// 方法所需要的参数 /// 返回调用结果 - public static object InvokeMethod(object instance, string methodName, params object[] parameters) + public static object? InvokeMethod(object instance, string methodName, params object[] parameters) { Type type = instance.GetType(); - MethodInfo methodInfo = type.GetMethod(methodName); + MethodInfo? methodInfo = type.GetMethod(methodName); if (methodInfo != null) { return methodInfo.Invoke(instance, parameters); @@ -83,9 +89,11 @@ public static object InvokeMethod(object instance, string methodName, params obj /// /// 获取程序集中所有的类型信息 + /// [Obsolete("请直接使用 assembly.GetTypes()")] /// /// 程序集 /// 返回 Type[] 数组,数组中每个元素代表程序集中的一个类型 + [Obsolete("请直接使用 assembly.GetTypes()", false)] public static Type[] GetAllTypesFromAssembly(Assembly assembly) { return assembly.GetTypes(); @@ -93,10 +101,12 @@ public static Type[] GetAllTypesFromAssembly(Assembly assembly) /// /// 判断指定类型是否实现了指定的接口 + /// [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)")] /// /// 要判断的类型 /// 要判断的接口类型 /// 返回布尔值,表示指定类型是否实现了指定的接口 + [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)", false)] public static bool IsImplementInterface(Type type, Type interfaceType) { return interfaceType.IsAssignableFrom(type); diff --git a/EasyTool.Core/ToolCategory/DelegateExtension.cs b/EasyTool.Core/ToolCategory/DelegateExtension.cs new file mode 100644 index 0000000..d90708b --- /dev/null +++ b/EasyTool.Core/ToolCategory/DelegateExtension.cs @@ -0,0 +1,428 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace EasyTool.Extension +{ + /// + /// Delegate 委托扩展方法 + /// + public static class DelegateExtension + { + #region 安全调用 + + /// + /// 安全调用 Action(捕获异常) + /// + public static void SafeInvoke(this Action? action, Action? onError = null) + { + if (action == null) + return; + + try + { + action(); + } + catch (Exception ex) + { + onError?.Invoke(ex); + } + } + + /// + /// 安全调用 Func(捕获异常,失败返回默认值) + /// + public static T? SafeInvoke(this Func? func, T? defaultValue = default, Action? onError = null) + { + if (func == null) + return defaultValue; + + try + { + return func(); + } + catch (Exception ex) + { + onError?.Invoke(ex); + return defaultValue; + } + } + + #endregion + + #region 重试 + + /// + /// Action 重试执行 + /// + public static void Retry(this Action action, int retryCount = 3, int delayMs = 0) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + Exception? lastException = null; + + for (int i = 0; i <= retryCount; i++) + { + try + { + action(); + return; + } + catch (Exception ex) + { + lastException = ex; + + if (i < retryCount && delayMs > 0) + { + Thread.Sleep(delayMs); + } + } + } + + throw lastException ?? new Exception("Retry failed"); + } + + /// + /// Func 重试执行 + /// + public static T Retry(this Func func, int retryCount = 3, int delayMs = 0) + { + if (func == null) + throw new ArgumentNullException(nameof(func)); + + Exception? lastException = null; + + for (int i = 0; i <= retryCount; i++) + { + try + { + return func(); + } + catch (Exception ex) + { + lastException = ex; + + if (i < retryCount && delayMs > 0) + { + Thread.Sleep(delayMs); + } + } + } + + throw lastException ?? new Exception("Retry failed"); + } + + /// + /// 异步 Action 重试执行 + /// + public static async Task RetryAsync(this Func action, int retryCount = 3, int delayMs = 0) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + Exception? lastException = null; + + for (int i = 0; i <= retryCount; i++) + { + try + { + await action(); + return; + } + catch (Exception ex) + { + lastException = ex; + + if (i < retryCount && delayMs > 0) + { + await Task.Delay(delayMs); + } + } + } + + throw lastException ?? new Exception("Retry failed"); + } + + /// + /// 异步 Func 重试执行 + /// + public static async Task RetryAsync(this Func> func, int retryCount = 3, int delayMs = 0) + { + if (func == null) + throw new ArgumentNullException(nameof(func)); + + Exception? lastException = null; + + for (int i = 0; i <= retryCount; i++) + { + try + { + return await func(); + } + catch (Exception ex) + { + lastException = ex; + + if (i < retryCount && delayMs > 0) + { + await Task.Delay(delayMs); + } + } + } + + throw lastException ?? new Exception("Retry failed"); + } + + #endregion + + #region 超时 + + /// + /// 设置 Action 超时 + /// + public static void WithTimeout(this Action action, int timeoutMs) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + var task = Task.Run(action); + if (!task.Wait(timeoutMs)) + throw new TimeoutException($"操作超时({timeoutMs}ms)"); + } + + /// + /// 设置 Func 超时 + /// + public static T WithTimeout(this Func func, int timeoutMs) + { + if (func == null) + throw new ArgumentNullException(nameof(func)); + + var task = Task.Run(func); + if (!task.Wait(timeoutMs)) + throw new TimeoutException($"操作超时({timeoutMs}ms)"); + + return task.Result; + } + + #endregion + + #region 防抖与节流 + + /// + /// 防抖(延迟执行,如果在延迟时间内再次调用则重新计时) + /// + public static Action Debounce(this Action action, int delayMs) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + Timer? timer = null; + return () => + { + timer?.Dispose(); + timer = new Timer(state => + { + action(); + timer?.Dispose(); + }, null, delayMs, Timeout.Infinite); + }; + } + + /// + /// 节流(指定时间间隔内只执行一次) + /// + public static Action Throttle(this Action action, int intervalMs) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + DateTime lastRun = DateTime.MinValue; + return () => + { + var now = DateTime.Now; + if ((now - lastRun).TotalMilliseconds >= intervalMs) + { + action(); + lastRun = now; + } + }; + } + + #endregion + + #region 延迟执行 + + /// + /// 延迟执行 Action + /// + public static void Delay(this Action action, int delayMs) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + Task.Delay(delayMs).ContinueWith(_ => action()); + } + + /// + /// 异步延迟执行 Action + /// + public static async Task DelayAsync(this Action action, int delayMs) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + await Task.Delay(delayMs); + action(); + } + + /// + /// 异步延迟执行 Func + /// + public static async Task DelayAsync(this Func func, int delayMs) + { + if (func == null) + throw new ArgumentNullException(nameof(func)); + + await Task.Delay(delayMs); + return func(); + } + + #endregion + + #region 链式调用 + + /// + /// 链式调用 Action + /// + public static Action? Then(this Action? first, Action? second) + { + if (first == null) + return second; + if (second == null) + return first; + + return () => + { + first(); + second(); + }; + } + + /// + /// 链式调用 Func + /// + public static Func? Then(this Func? first, Func? second) + { + if (first == null) + return second; + if (second == null) + return first; + + return () => + { + first(); + return second(); + }; + } + + /// + /// 链式调用 Func(转换) + /// + public static Func? Then(this Func? first, Func? second) + { + if (first == null || second == null) + return null; + + return () => second(first()); + } + + #endregion + + #region 条件执行 + + /// + /// 条件执行 Action + /// + public static Action? ExecuteIf(this Action? action, bool condition) + { + if (action == null) + return null; + + return () => + { + if (condition) + action(); + }; + } + + /// + /// 条件执行 Func + /// + public static Func? ExecuteIf(this Func? func, bool condition) + { + if (func == null) + return null; + + return () => condition ? func() : default; + } + + #endregion + + #region 缓存 + + /// + /// 缓存 Func 结果 + /// + public static Func? Cache(this Func? func) + { + if (func == null) + return null; + + bool cached = false; + T? value = default; + + return () => + { + if (!cached) + { + value = func(); + cached = true; + } + return value; + }; + } + + #endregion + + #region 组合 + + /// + /// 组合多个 Action + /// + public static Action Combine(params Action[] actions) + { + return () => + { + foreach (var action in actions) + { + action?.Invoke(); + } + }; + } + + /// + /// 组合多个 Func(后者的结果作为前者的参数) + /// + public static Func? Compose(this Func? func1, Func? func2) + { + if (func1 == null || func2 == null) + return null; + + return x => func1(func2(x)); + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/EnumExtension.cs b/EasyTool.Core/ToolCategory/EnumExtension.cs new file mode 100644 index 0000000..bd117b5 --- /dev/null +++ b/EasyTool.Core/ToolCategory/EnumExtension.cs @@ -0,0 +1,357 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace EasyTool.Extension +{ + /// + /// Enum 枚举扩展方法 + /// + public static class EnumExtension + { + #region 描述信息 + + /// + /// 获取枚举值的描述(Description 特性) + /// + public static string GetDescription(this Enum value) + { + if (value == null) + return string.Empty; + + var field = value.GetType().GetField(value.ToString()); + if (field == null) + return value.ToString(); + + var attr = field.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute; + return attr?.Description ?? value.ToString(); + } + + /// + /// 获取枚举值的显示名称(Display 特性) + /// + public static string GetDisplayName(this Enum value) + { + if (value == null) + return string.Empty; + + var field = value.GetType().GetField(value.ToString()); + if (field == null) + return value.ToString(); + + var attr = field.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false).FirstOrDefault() as System.ComponentModel.DataAnnotations.DisplayAttribute; + return attr?.GetName() ?? value.ToString(); + } + + #endregion + + #region 枚举转换 + + /// + /// 将枚举值转换为整数 + /// [Obsolete("请直接使用 Convert.ToInt32(value)")] + /// + [Obsolete("请直接使用 Convert.ToInt32(value)", false)] + public static int ToInt(this Enum value) + { + if (value == null) + return 0; + + return Convert.ToInt32(value); + } + + /// + /// 将整数转换为枚举 + /// + public static T ToEnum(this int value) where T : struct, Enum + { + return Enum.Parse(value.ToString()); + } + + /// + /// 安全解析字符串为枚举 + /// + public static T ParseEnum(this string value) where T : struct, Enum + { + return Enum.Parse(value, true); + } + + /// + /// 安全解析字符串为枚举,失败返回默认值 + /// + public static T ParseEnumOrDefault(this string value, T defaultValue = default) where T : struct, Enum + { + if (Enum.TryParse(value, true, out var result)) + return result; + + return defaultValue; + } + + /// + /// 尝试解析字符串为枚举 + /// [Obsolete("请直接使用 Enum.TryParse(value, true, out result)")] + /// + [Obsolete("请直接使用 Enum.TryParse(value, true, out result)", false)] + public static bool TryParseEnum(this string value, out T result) where T : struct, Enum + { + return Enum.TryParse(value, true, out result); + } + + #endregion + + #region 枚举集合 + + /// + /// 获取枚举类型的所有值 + /// + public static T[] GetValues() where T : struct, Enum + { + return (T[])Enum.GetValues(typeof(T)); + } + + /// + /// 获取枚举类型的所有名称 + /// + public static string[] GetNames() where T : struct, Enum + { + return Enum.GetNames(typeof(T)); + } + + /// + /// 获取枚举类型的所有值和描述 + /// + public static Dictionary ToDictionary() where T : struct, Enum + { + var dictionary = new Dictionary(); + foreach (T value in GetValues()) + { + dictionary[value] = value.GetDescription(); + } + return dictionary; + } + + /// + /// 获取枚举类型的所有值和显示名称 + /// + public static Dictionary ToDisplayNameDictionary() where T : struct, Enum + { + var dictionary = new Dictionary(); + foreach (T value in GetValues()) + { + dictionary[value] = value.GetDisplayName(); + } + return dictionary; + } + + #endregion + + #region 枚举判断 + + /// + /// 判断是否是定义的枚举值 + /// [Obsolete("请直接使用 Enum.IsDefined(typeof(T), value)")] + /// + [Obsolete("请直接使用 Enum.IsDefined(typeof(T), value)", false)] + public static bool IsDefined(this T value) where T : struct, Enum + { + return Enum.IsDefined(typeof(T), value); + } + + /// + /// 判断字符串是否是有效的枚举名称或值 + /// + public static bool IsEnumDefined(this string value) where T : struct, Enum + { + return Enum.IsDefined(typeof(T), value); + } + + /// + /// 判断枚举值是否有指定标记 + /// + public static bool HasFlag(this T value, T flag) where T : struct, Enum + { + var valueInt = Convert.ToInt64(value); + var flagInt = Convert.ToInt64(flag); + return (valueInt & flagInt) == flagInt; + } + + /// + /// 设置枚举标记 + /// + public static T SetFlag(this T value, T flag) where T : struct, Enum + { + var valueInt = Convert.ToInt64(value); + var flagInt = Convert.ToInt64(flag); + var result = valueInt | flagInt; + return (T)Enum.ToObject(typeof(T), result); + } + + /// + /// 清除枚举标记 + /// + public static T ClearFlag(this T value, T flag) where T : struct, Enum + { + var valueInt = Convert.ToInt64(value); + var flagInt = Convert.ToInt64(flag); + var result = valueInt & ~flagInt; + return (T)Enum.ToObject(typeof(T), result); + } + + /// + /// 切换枚举标记 + /// + public static T ToggleFlag(this T value, T flag) where T : struct, Enum + { + var valueInt = Convert.ToInt64(value); + var flagInt = Convert.ToInt64(flag); + var result = valueInt ^ flagInt; + return (T)Enum.ToObject(typeof(T), result); + } + + #endregion + + #region 下一个/上一个值 + + /// + /// 获取下一个枚举值 + /// + public static T Next(this T value) where T : struct, Enum + { + var values = GetValues(); + int index = Array.IndexOf(values, value); + if (index < 0 || index >= values.Length - 1) + return value; + + return values[index + 1]; + } + + /// + /// 获取上一个枚举值 + /// + public static T Previous(this T value) where T : struct, Enum + { + var values = GetValues(); + int index = Array.IndexOf(values, value); + if (index <= 0) + return value; + + return values[index - 1]; + } + + /// + /// 获取第一个枚举值 + /// + public static T First() where T : struct, Enum + { + var values = GetValues(); + return values[0]; + } + + /// + /// 获取最后一个枚举值 + /// + public static T Last() where T : struct, Enum + { + var values = GetValues(); + return values[values.Length - 1]; + } + + #endregion + + #region 随机值 + + /// + /// 获取随机枚举值 + /// + public static T Random() where T : struct, Enum + { + var values = GetValues(); + var random = new System.Random(); + int index = random.Next(values.Length); + return values[index]; + } + + #endregion + + #region Flags 操作 + + /// + /// 获取 Flags 枚举的所有设置值 + /// + public static IEnumerable GetFlags(this T value) where T : struct, Enum + { + var values = GetValues(); + var valueInt = Convert.ToInt64(value); + + foreach (T flag in values) + { + var flagInt = Convert.ToInt64(flag); + if (flagInt == 0) + continue; + + if ((valueInt & flagInt) == flagInt) + yield return flag; + } + } + + /// + /// 判断是否是 Flags 枚举 + /// + public static bool IsFlagsEnum() where T : struct, Enum + { + return typeof(T).IsDefined(typeof(FlagsAttribute), false); + } + + #endregion + + #region 下拉框/选择列表 + + /// + /// 获取枚举的所有选项(用于下拉框等) + /// + public static List> GetItems() where T : struct, Enum + { + var items = new List>(); + foreach (T value in GetValues()) + { + items.Add(new EnumItem + { + Value = value, + Name = value.ToString(), + Description = value.GetDescription(), + DisplayName = value.GetDisplayName() + }); + } + return items; + } + + #endregion + } + + /// + /// 枚举项 + /// + public class EnumItem where T : struct, Enum + { + /// + /// 枚举值 + /// + public T Value { get; set; } + + /// + /// 名称 + /// + public string? Name { get; set; } + + /// + /// 描述 + /// + public string? Description { get; set; } + + /// + /// 显示名称 + /// + public string? DisplayName { get; set; } + } +} diff --git a/EasyTool.Core/ToolCategory/EnumUtil.cs b/EasyTool.Core/ToolCategory/EnumUtil.cs index bd3dc3e..f03522b 100644 --- a/EasyTool.Core/ToolCategory/EnumUtil.cs +++ b/EasyTool.Core/ToolCategory/EnumUtil.cs @@ -15,9 +15,11 @@ public class EnumUtil { /// /// 获取指定枚举类型的所有成员名称 + /// [Obsolete("请直接使用 Enum.GetNames(typeof(TEnum))")] /// /// 要获取成员名称的枚举类型 /// 所有成员名称的字符串数组 + [Obsolete("请直接使用 Enum.GetNames(typeof(TEnum))", false)] public static string[] GetNames() { return Enum.GetNames(typeof(TEnum)); @@ -25,9 +27,11 @@ public static string[] GetNames() /// /// 获取指定枚举类型的所有成员的值 + /// [Obsolete("请直接使用 (TEnum[])Enum.GetValues(typeof(TEnum))")] /// /// 要获取成员值的枚举类型 /// 所有成员值的数组 + [Obsolete("请直接使用 (TEnum[])Enum.GetValues(typeof(TEnum))", false)] public static TEnum[] GetValues() { return (TEnum[])Enum.GetValues(typeof(TEnum)); @@ -35,10 +39,12 @@ public static TEnum[] GetValues() /// /// 获取指定枚举值的名称 + /// [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)")] /// /// 枚举类型 /// 枚举值 /// 枚举值的名称 + [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)", false)] public static string GetName(TEnum value) { return Enum.GetName(typeof(TEnum), value); @@ -46,10 +52,12 @@ public static string GetName(TEnum value) /// /// 检查指定的值是否是枚举类型TEnum的成员 + /// [Obsolete("请直接使用 Enum.IsDefined(typeof(TEnum), value)")] /// /// 枚举类型 /// 要检查的值 /// 如果指定的值是TEnum的成员,则为true;否则为false + [Obsolete("请直接使用 Enum.IsDefined(typeof(TEnum), value)", false)] public static bool IsDefined(object value) { return Enum.IsDefined(typeof(TEnum), value); @@ -57,10 +65,12 @@ public static bool IsDefined(object value) /// /// 将字符串转换为枚举类型TEnum的值 + /// [Obsolete("请直接使用 (TEnum)Enum.Parse(typeof(TEnum), value)")] /// /// 枚举类型 /// 要转换的字符串 /// 与字符串对应的枚举值 + [Obsolete("请直接使用 (TEnum)Enum.Parse(typeof(TEnum), value)", false)] public static TEnum Parse(string value) { return (TEnum)Enum.Parse(typeof(TEnum), value); @@ -81,9 +91,11 @@ public static TEnum Parse(string value, TEnum defaultValue) /// /// 获取指定枚举类型的Type对象 + /// [Obsolete("请直接使用 typeof(TEnum)")] /// /// 枚举类型 /// 枚举类型的Type对象 + [Obsolete("请直接使用 typeof(TEnum)", false)] public static Type GetEnumType() { return typeof(TEnum); @@ -193,10 +205,12 @@ public static TEnum GetValueByName(string name) /// /// 获取指定枚举类型的指定值的名称 + /// [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)")] /// /// 枚举类型 /// 枚举值 /// 与值对应的名称,如果值不存在,则返回null + [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)", false)] public static string? GetNameByValue(TEnum value) { return Enum.GetName(typeof(TEnum), value!); diff --git a/EasyTool.Core/ToolCategory/EnvUtil.cs b/EasyTool.Core/ToolCategory/EnvUtil.cs index 54932cf..bb67821 100644 --- a/EasyTool.Core/ToolCategory/EnvUtil.cs +++ b/EasyTool.Core/ToolCategory/EnvUtil.cs @@ -35,9 +35,11 @@ public static string GetSystemInfo() /// /// 获取环境变量值 + /// [Obsolete("请直接使用 Environment.GetEnvironmentVariable(name)")] /// /// 环境变量名称 /// 环境变量值 + [Obsolete("请直接使用 Environment.GetEnvironmentVariable(name)", false)] public static string GetEnvironmentVariable(string name) { return Environment.GetEnvironmentVariable(name); @@ -45,9 +47,11 @@ public static string GetEnvironmentVariable(string name) /// /// 设置环境变量值 + /// [Obsolete("请直接使用 Environment.SetEnvironmentVariable(name, value)")] /// /// 环境变量名称 /// 环境变量值 + [Obsolete("请直接使用 Environment.SetEnvironmentVariable(name, value)", false)] public static void SetEnvironmentVariable(string name, string value) { Environment.SetEnvironmentVariable(name, value); @@ -127,8 +131,10 @@ public static List GetDirectoriesInDirectory(string path) /// /// 创建文件 + /// [Obsolete("请直接使用 File.Create(path)")] /// /// 文件路径 + [Obsolete("请直接使用 File.Create(path)", false)] public static void CreateFile(string path) { File.Create(path); @@ -136,8 +142,10 @@ public static void CreateFile(string path) /// /// 删除文件 + /// [Obsolete("请直接使用 File.Delete(path)")] /// /// 文件路径 + [Obsolete("请直接使用 File.Delete(path)", false)] public static void DeleteFile(string path) { File.Delete(path); @@ -145,8 +153,10 @@ public static void DeleteFile(string path) /// /// 创建目录 + /// [Obsolete("请直接使用 Directory.CreateDirectory(path)")] /// /// 目录路径 + [Obsolete("请直接使用 Directory.CreateDirectory(path)", false)] public static void CreateDirectory(string path) { Directory.CreateDirectory(path); @@ -154,8 +164,10 @@ public static void CreateDirectory(string path) /// /// 删除目录 + /// [Obsolete("请直接使用 Directory.Delete(path, true)")] /// /// 目录路径 + [Obsolete("请直接使用 Directory.Delete(path, true)", false)] public static void DeleteDirectory(string path) { Directory.Delete(path, true); @@ -163,9 +175,11 @@ public static void DeleteDirectory(string path) /// /// 检查目录是否存在 + /// [Obsolete("请直接使用 Directory.Exists(path)")] /// /// 目录路径 /// 目录是否存在 + [Obsolete("请直接使用 Directory.Exists(path)", false)] public static bool DirectoryExists(string path) { return Directory.Exists(path); @@ -173,9 +187,11 @@ public static bool DirectoryExists(string path) /// /// 检查文件是否存在 + /// [Obsolete("请直接使用 File.Exists(path)")] /// /// 文件路径 /// 文件是否存在 + [Obsolete("请直接使用 File.Exists(path)", false)] public static bool FileExists(string path) { return File.Exists(path); @@ -216,10 +232,12 @@ public static DateTime GetFileLastWriteTime(string path) /// /// 复制文件 + /// [Obsolete("请直接使用 File.Copy(sourcePath, destinationPath, overwrite)")] /// /// 源文件路径 /// 目标文件路径 /// 是否覆盖已有文件 + [Obsolete("请直接使用 File.Copy(sourcePath, destinationPath, overwrite)", false)] public static void CopyFile(string sourcePath, string destinationPath, bool overwrite) { File.Copy(sourcePath, destinationPath, overwrite); @@ -227,9 +245,11 @@ public static void CopyFile(string sourcePath, string destinationPath, bool over /// /// 移动文件 + /// [Obsolete("请直接使用 File.Move(sourcePath, destinationPath)")] /// /// 源文件路径 /// 目标文件路径 + [Obsolete("请直接使用 File.Move(sourcePath, destinationPath)", false)] public static void MoveFile(string sourcePath, string destinationPath) { File.Move(sourcePath, destinationPath); diff --git a/EasyTool.Core/ToolCategory/EscapeUtil.cs b/EasyTool.Core/ToolCategory/EscapeUtil.cs index 7d030c5..eea8c2f 100644 --- a/EasyTool.Core/ToolCategory/EscapeUtil.cs +++ b/EasyTool.Core/ToolCategory/EscapeUtil.cs @@ -94,9 +94,11 @@ public static string Unescape(string str) /// /// 将URL中的特殊字符进行转义 + /// [Obsolete("请直接使用 Uri.EscapeDataString(url)")] /// /// 需要转义的URL /// 转义后的URL + [Obsolete("请直接使用 Uri.EscapeDataString(url)", false)] public static string UrlEncode(string url) { if (string.IsNullOrEmpty(url)) @@ -109,9 +111,11 @@ public static string UrlEncode(string url) /// /// 将URL中的转义字符还原成特殊字符 + /// [Obsolete("请直接使用 Uri.UnescapeDataString(url)")] /// /// 需要还原的URL /// 还原后的URL + [Obsolete("请直接使用 Uri.UnescapeDataString(url)", false)] public static string UrlDecode(string url) { if (string.IsNullOrEmpty(url)) @@ -124,9 +128,11 @@ public static string UrlDecode(string url) /// /// 将HTML字符串进行转义,将特殊字符替换成HTML实体 + /// [Obsolete("请直接使用 System.Net.WebUtility.HtmlEncode(html)")] /// /// 需要转义的HTML字符串 /// 转义后的HTML字符串 + [Obsolete("请直接使用 System.Net.WebUtility.HtmlEncode(html)", false)] public static string HtmlEncode(string html) { if (string.IsNullOrEmpty(html)) @@ -139,9 +145,11 @@ public static string HtmlEncode(string html) /// /// 将HTML字符串中的HTML实体还原成特殊字符 + /// [Obsolete("请直接使用 System.Net.WebUtility.HtmlDecode(html)")] /// /// 需要还原的HTML字符串 /// 还原后的HTML字符串 + [Obsolete("请直接使用 System.Net.WebUtility.HtmlDecode(html)", false)] public static string HtmlDecode(string html) { if (string.IsNullOrEmpty(html)) @@ -154,9 +162,11 @@ public static string HtmlDecode(string html) /// /// 将XML字符串进行转义,将特殊字符替换成XML实体 + /// [Obsolete("请直接使用 System.Security.SecurityElement.Escape(xml)")] /// /// 需要转义的XML字符串 /// 转义后的XML字符串 + [Obsolete("请直接使用 System.Security.SecurityElement.Escape(xml)", false)] public static string XmlEncode(string xml) { if (string.IsNullOrEmpty(xml)) diff --git a/EasyTool.Core/ToolCategory/ExceptionExtension.cs b/EasyTool.Core/ToolCategory/ExceptionExtension.cs new file mode 100644 index 0000000..6ebc43e --- /dev/null +++ b/EasyTool.Core/ToolCategory/ExceptionExtension.cs @@ -0,0 +1,338 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +namespace EasyTool.Extension +{ + /// + /// Exception 异常扩展方法 + /// + public static class ExceptionExtension + { + #region 消息获取 + + /// + /// 获取完整的异常消息(包含所有内层异常) + /// + public static string GetFullMessage(this Exception? exception) + { + if (exception == null) + return string.Empty; + + var sb = new StringBuilder(); + var current = exception; + + int depth = 0; + while (current != null) + { + if (depth > 0) + sb.Append("Inner Exception: "); + + sb.AppendLine(current.Message); + current = current.InnerException; + depth++; + + // 防止无限循环 + if (depth > 100) + break; + } + + return sb.ToString().Trim(); + } + + /// + /// 获取所有异常(包含内层异常) + /// + public static Exception[] GetAllExceptions(this Exception? exception) + { + if (exception == null) + return Array.Empty(); + + var exceptions = new List(); + var current = exception; + + int depth = 0; + while (current != null) + { + exceptions.Add(current); + current = current.InnerException; + depth++; + + // 防止无限循环 + if (depth > 100) + break; + } + + return exceptions.ToArray(); + } + + /// + /// 获取所有内层异常 + /// + public static Exception[] GetInnerExceptions(this Exception exception) + { + var all = exception.GetAllExceptions(); + return all.Skip(1).ToArray(); + } + + #endregion + + #region 详细信息 + + /// + /// 获取异常的详细字符串表示 + /// + public static string ToDetailedString(this Exception? exception) + { + if (exception == null) + return string.Empty; + + var sb = new StringBuilder(); + + sb.AppendLine($"Exception Type: {exception.GetType().FullName}"); + sb.AppendLine($"Message: {exception.Message}"); + sb.AppendLine($"Source: {exception.Source ?? "(unknown)"}"); + + if (exception.TargetSite != null) + { + sb.AppendLine($"Target Site: {exception.TargetSite}"); + } + + if (!string.IsNullOrEmpty(exception.HelpLink)) + { + sb.AppendLine($"Help Link: {exception.HelpLink}"); + } + + if (exception.Data != null && exception.Data.Count > 0) + { + sb.AppendLine("Data:"); + foreach (var key in exception.Data.Keys) + { + sb.AppendLine($" {key}: {exception.Data[key]}"); + } + } + + if (!string.IsNullOrEmpty(exception.StackTrace)) + { + sb.AppendLine("Stack Trace:"); + sb.AppendLine(exception.StackTrace); + } + + // 处理内层异常 + if (exception.InnerException != null) + { + sb.AppendLine(); + sb.AppendLine("--- Inner Exception ---"); + sb.Append(exception.InnerException.ToDetailedString()); + } + + return sb.ToString(); + } + + /// + /// 获取异常的简略字符串表示 + /// + public static string ToSimpleString(this Exception? exception) + { + if (exception == null) + return string.Empty; + + return $"{exception.GetType().Name}: {exception.Message}"; + } + + #endregion + + #region 异常类型判断 + + /// + /// 判断异常是否是指定类型 + /// + public static bool IsType(this Exception? exception) where T : Exception + { + return exception is T; + } + + /// + /// 判断异常或其内层异常是否是指定类型 + /// + public static bool IsOrContainsType(this Exception? exception) where T : Exception + { + var current = exception; + + while (current != null) + { + if (current is T) + return true; + + current = current.InnerException; + } + + return false; + } + + /// + /// 查找第一个指定类型的异常 + /// + public static T? FindType(this Exception? exception) where T : Exception + { + var current = exception; + + while (current != null) + { + if (current is T typedException) + return typedException; + + current = current.InnerException; + } + + return null; + } + + #endregion + + #region 特定异常处理 + + /// + /// 判断是否是超时异常 + /// + public static bool IsTimeout(this Exception? exception) + { + return exception.IsOrContainsType(); + } + + /// + /// 判断是否是取消操作异常 + /// + public static bool IsOperationCanceled(this Exception? exception) + { + return exception.IsOrContainsType(); + } + + /// + /// 判断是否是参数异常 + /// + public static bool IsArgumentException(this Exception? exception) + { + return exception.IsOrContainsType(); + } + + /// + /// 判断是否是空引用异常 + /// + public static bool IsNullReference(this Exception? exception) + { + return exception is NullReferenceException; + } + + /// + /// 判断是否是 IO 异常 + /// + public static bool IsIOException(this Exception? exception) + { + return exception.IsOrContainsType(); + } + + #endregion + + #region 异常包装 + + /// + /// 使用指定消息包装异常 + /// + public static Exception WrapWith(this Exception? exception, string message) + { + return new Exception(message, exception); + } + + /// + /// 使用指定类型包装异常 + /// + public static TException WrapWith(this Exception? exception, string message) where TException : Exception + { + return (TException)Activator.CreateInstance(typeof(TException), message, exception)!; + } + + #endregion + + #region 日志格式化 + + /// + /// 获取适合日志记录的异常信息 + /// + public static string ToLogString(this Exception? exception, bool includeStackTrace = true) + { + if (exception == null) + return string.Empty; + + var sb = new StringBuilder(); + + sb.Append($"[{exception.GetType().Name}] "); + sb.AppendLine(exception.Message); + + if (includeStackTrace && !string.IsNullOrEmpty(exception.StackTrace)) + { + sb.AppendLine(exception.StackTrace.Trim()); + } + + return sb.ToString(); + } + + /// + /// 获取单行格式的异常信息 + /// + public static string ToOneLineString(this Exception? exception) + { + if (exception == null) + return string.Empty; + + var sb = new StringBuilder(); + var current = exception; + + while (current != null) + { + if (sb.Length > 0) + sb.Append(" -> "); + + sb.Append($"[{current.GetType().Name}] {current.Message}"); + current = current.InnerException; + + // 防止无限循环 + if (sb.Length > 1000) + break; + } + + return sb.ToString(); + } + + #endregion + + #region 聚合异常处理 + + /// + /// 获取聚合异常中的所有异常 + /// + public static Exception[] GetInnerExceptions(this AggregateException? exception) + { + if (exception == null) + return Array.Empty(); + + return exception.InnerExceptions.ToArray(); + } + + /// + /// 展平聚合异常(递归获取所有内层异常) + /// [Obsolete("请直接使用 exception.Flatten().InnerExceptions.ToArray()")] + /// + [Obsolete("请直接使用 exception.Flatten().InnerExceptions.ToArray()", false)] + public static Exception[] Flatten(this AggregateException? exception) + { + if (exception == null) + return Array.Empty(); + + return exception.Flatten().InnerExceptions.ToArray(); + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/GuidExtension.cs b/EasyTool.Core/ToolCategory/GuidExtension.cs new file mode 100644 index 0000000..4f04619 --- /dev/null +++ b/EasyTool.Core/ToolCategory/GuidExtension.cs @@ -0,0 +1,288 @@ +using System; +using System.Text; + +namespace EasyTool.Extension +{ + /// + /// Guid 扩展方法 + /// + public static class GuidExtension + { + #region 空值判断 + + /// + /// 判断 Guid 是否为空 + /// [Obsolete("请直接使用 guid == Guid.Empty")] + /// + [Obsolete("请直接使用 guid == Guid.Empty", false)] + public static bool IsEmpty(this Guid guid) + { + return guid == Guid.Empty; + } + + /// + /// 判断 Guid 是否为空或默认值 + /// + public static bool IsNullOrEmpty(this Guid? guid) + { + return guid == null || guid.Value == Guid.Empty; + } + + /// + /// 判断 Guid 是否有值(非空) + /// + public static bool HasValue(this Guid guid) + { + return guid != Guid.Empty; + } + + /// + /// 判断可空 Guid 是否有值 + /// + public static bool HasValue(this Guid? guid) + { + return guid.HasValue && guid.Value != Guid.Empty; + } + + #endregion + + #region 格式化转换 + + /// + /// 获取短格式 Guid(不带连字符) + /// + public static string ToShortString(this Guid guid) + { + return guid.ToString("N"); + } + + /// + /// 获取短格式 Guid(带连字符) + /// + public static string ToShortStringWithDashes(this Guid guid) + { + return guid.ToString("D"); + } + + /// + /// 获取带括号的 Guid 格式 + /// + public static string ToFormattedString(this Guid guid) + { + return guid.ToString("B"); + } + + /// + /// 获取带大括号的 Guid 格式 + /// + public static string ToBracedString(this Guid guid) + { + return guid.ToString("B"); + } + + /// + /// 获取 Guid 的字节数组表示 + /// [Obsolete("请直接使用 guid.ToByteArray()")] + /// + [Obsolete("请直接使用 guid.ToByteArray()", false)] + public static byte[] ToByteArray(this Guid guid) + { + return guid.ToByteArray(); + } + + /// + /// 将 Guid 转换为 Base64 字符串 + /// + public static string ToBase64String(this Guid guid) + { + return Convert.ToBase64String(guid.ToByteArray()); + } + + /// + /// 从 Base64 字符串创建 Guid + /// + public static Guid FromBase64String(this string base64) + { + var bytes = Convert.FromBase64String(base64); + return new Guid(bytes); + } + + #endregion + + #region 字符串解析 + + /// + /// 尝试解析字符串为 Guid,失败返回空 Guid + /// + public static Guid ToGuidOrDefault(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return Guid.Empty; + + return Guid.TryParse(value, out var guid) ? guid : Guid.Empty; + } + + /// + /// 尝试解析字符串为 Guid,失败返回默认值 + /// + public static Guid ToGuidOrDefault(this string value, Guid defaultValue) + { + if (string.IsNullOrWhiteSpace(value)) + return defaultValue; + + return Guid.TryParse(value, out var guid) ? guid : defaultValue; + } + + /// + /// 判断字符串是否是有效的 Guid 格式 + /// + public static bool IsValidGuid(this string value) + { + return !string.IsNullOrWhiteSpace(value) && Guid.TryParse(value, out _); + } + + #endregion + + #region 加密相关 + + /// + /// 获取 Guid 的 MD5 哈希值(作为新的 Guid) + /// + public static Guid ToMd5Guid(this Guid guid) + { + using var md5 = System.Security.Cryptography.MD5.Create(); + var bytes = guid.ToByteArray(); + var hash = md5.ComputeHash(bytes); + return new Guid(hash); + } + + /// + /// 获取 Guid 的 SHA1 哈希值(取前16字节作为 Guid) + /// + public static Guid ToSha1Guid(this Guid guid) + { + using var sha1 = System.Security.Cryptography.SHA1.Create(); + var bytes = guid.ToByteArray(); + var hash = sha1.ComputeHash(bytes); + var guidBytes = new byte[16]; + Array.Copy(hash, 0, guidBytes, 0, 16); + return new Guid(guidBytes); + } + + #endregion + + #region Guid 生成 + + /// + /// 基于 Guid 生成连续的 Guid(适用于 COMB 类型) + /// + public static Guid NewCombGuid() + { + var guidArray = Guid.NewGuid().ToByteArray(); + var baseDate = new DateTime(1900, 1, 1); + var now = DateTime.Now; + var days = new TimeSpan(now.Ticks - baseDate.Ticks); + var msecs = now.TimeOfDay; + + var daysArray = BitConverter.GetBytes(days.Days); + var msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds / 3.333333)); + + Array.Reverse(daysArray); + Array.Reverse(msecsArray); + + Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2); + Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4); + + return new Guid(guidArray); + } + + /// + /// 基于指定前缀生成可预测的 Guid + /// + public static Guid NewDeterministicGuid(string prefix) + { + using var md5 = System.Security.Cryptography.MD5.Create(); + var prefixBytes = Encoding.UTF8.GetBytes(prefix); + var hash = md5.ComputeHash(prefixBytes); + return new Guid(hash); + } + + /// + /// 基于多个参数生成可预测的 Guid + /// + public static Guid NewDeterministicGuid(params object[] values) + { + using var md5 = System.Security.Cryptography.MD5.Create(); + var combined = string.Join("|", values); + var bytes = Encoding.UTF8.GetBytes(combined); + var hash = md5.ComputeHash(bytes); + return new Guid(hash); + } + + #endregion + + #region Guid 比较 + + /// + /// 比较两个 Guid 是否相等 + /// + public static bool EqualsTo(this Guid guid, Guid other) + { + return guid.Equals(other); + } + + /// + /// 比较两个可空 Guid 是否相等 + /// + public static bool EqualsTo(this Guid? guid, Guid? other) + { + if (guid.HasValue && other.HasValue) + return guid.Value.Equals(other.Value); + return guid.HasValue == other.HasValue; + } + + #endregion + + #region Guid 操作 + + /// + /// 获取 Guid 的指定部分的值 + /// + /// Guid + /// 部分:0-3(Data1-Data4) + public static int GetPart(this Guid guid, int part) + { + var bytes = guid.ToByteArray(); + return part switch + { + 0 => BitConverter.ToInt32(bytes, 0), + 1 => BitConverter.ToInt16(bytes, 4), + 2 => BitConverter.ToInt16(bytes, 6), + 3 => bytes[8] << 24 | bytes[9] << 16 | bytes[10] << 8 | bytes[11], + _ => throw new ArgumentOutOfRangeException(nameof(part), "Part must be between 0 and 3") + }; + } + + /// + /// 将 Guid 转换为整数(用于某些场景的简化处理) + /// + public static int ToInt32(this Guid guid) + { + var bytes = guid.ToByteArray(); + return BitConverter.ToInt32(bytes, 0); + } + + /// + /// 将 Guid 转换为长整数 + /// + public static long ToInt64(this Guid guid) + { + var bytes = guid.ToByteArray(); + var high = BitConverter.ToInt64(bytes, 0); + var low = BitConverter.ToInt64(bytes, 8); + return high ^ low; + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/HexUtil.cs b/EasyTool.Core/ToolCategory/HexUtil.cs index 1bc1079..52b892f 100644 --- a/EasyTool.Core/ToolCategory/HexUtil.cs +++ b/EasyTool.Core/ToolCategory/HexUtil.cs @@ -87,9 +87,11 @@ public static string IntToHex(int number) /// /// 将16进制字符串中的所有字符转换为大写 + /// [Obsolete("请直接使用 hex.ToUpper()")] /// /// 16进制字符串 /// 大写16进制字符串 + [Obsolete("请直接使用 hex.ToUpper()", false)] public static string HexToUpper(string hex) { return hex.ToUpper(); @@ -97,9 +99,11 @@ public static string HexToUpper(string hex) /// /// 将16进制字符串中的所有字符转换为小写 + /// [Obsolete("请直接使用 hex.ToLower()")] /// /// 16进制字符串 /// 小写16进制字符串 + [Obsolete("请直接使用 hex.ToLower()", false)] public static string HexToLower(string hex) { return hex.ToLower(); diff --git a/EasyTool.Core/ToolCategory/IdcardUtil.cs b/EasyTool.Core/ToolCategory/IdcardUtil.cs index eee852e..0f6d9bd 100644 --- a/EasyTool.Core/ToolCategory/IdcardUtil.cs +++ b/EasyTool.Core/ToolCategory/IdcardUtil.cs @@ -241,7 +241,7 @@ public static bool IsValidChecksum(string idcard) /// /// 身份证号码 /// 省份 - public static string GetProvince(string idcard) + public static string? GetProvince(string? idcard) { if (string.IsNullOrEmpty(idcard)) { @@ -345,7 +345,7 @@ public static string GetProvince(string idcard) /// 身份证号码 /// 新的生日日期 /// 新的身份证号码 - public static string ReplaceBirthday(string idcard, DateTime birthday) + public static string? ReplaceBirthday(string? idcard, DateTime birthday) { if (string.IsNullOrEmpty(idcard)) { diff --git a/EasyTool.Core/ToolCategory/ObjectExtension.cs b/EasyTool.Core/ToolCategory/ObjectExtension.cs new file mode 100644 index 0000000..b14b515 --- /dev/null +++ b/EasyTool.Core/ToolCategory/ObjectExtension.cs @@ -0,0 +1,479 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.Json; +using System.Xml.Serialization; + +namespace EasyTool.Extension +{ + /// + /// Object 对象扩展方法 + /// + public static class ObjectExtension + { + #region 空值判断 + + /// + /// 判断对象是否为 null + /// + public static bool IsNull(this object obj) + { + return obj == null; + } + + /// + /// 判断对象是否不为 null + /// + public static bool IsNotNull(this object obj) + { + return obj != null; + } + + /// + /// 判断对象是否为空(null 或空字符串或空集合) + /// + public static bool IsNullOrEmpty(this object obj) + { + if (obj == null) + return true; + + if (obj is string str) + return string.IsNullOrEmpty(str); + + if (obj is System.Collections.ICollection collection) + return collection.Count == 0; + + return false; + } + + #endregion + + #region 类型转换 + + /// + /// 将对象转换为指定类型 + /// + public static T? As(this object obj) + { + if (obj == null) + return default; + + return (T)obj; + } + + /// + /// 尝试将对象转换为指定类型 + /// + public static T To(this object obj) where T : struct + { + return (T)Convert.ChangeType(obj, typeof(T)); + } + + /// + /// 安全转换,失败返回默认值 + /// + public static T? ToOrDefault(this object obj) + { + return ToOrDefault(obj, default(T)); + } + + /// + /// 安全转换,失败返回指定默认值 + /// + public static T ToOrDefault(this object obj, T defaultValue) + { + if (obj == null) + return defaultValue; + + try + { + if (obj is T direct) + return direct; + + return (T)Convert.ChangeType(obj, typeof(T)); + } + catch + { + return defaultValue; + } + } + + #endregion + + #region JSON 序列化 + + /// + /// 将对象序列化为 JSON 字符串 + /// + public static string? ToJson(this object obj, bool indented = false) + { + if (obj == null) + return null; + + var options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = indented + }; + + return JsonSerializer.Serialize(obj, options); + } + + /// + /// 从 JSON 字符串反序列化为对象 + /// + public static T? FromJson(this string json) + { + if (string.IsNullOrEmpty(json)) + return default; + + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + + return JsonSerializer.Deserialize(json, options); + } + + #endregion + + #region XML 序列化 + + /// + /// 将对象序列化为 XML 字符串 + /// + public static string? ToXml(this object obj) + { + if (obj == null) + return null; + + var serializer = new XmlSerializer(obj.GetType()); + using var writer = new StringWriter(); + serializer.Serialize(writer, obj); + return writer.ToString(); + } + + /// + /// 从 XML 字符串反序列化为对象 + /// + public static T? FromXml(this string xml) + { + if (string.IsNullOrEmpty(xml)) + return default; + + var serializer = new XmlSerializer(typeof(T)); + using var reader = new StringReader(xml); + return (T?)serializer.Deserialize(reader); + } + + #endregion + + #region 深拷贝 + + /// + /// 深拷贝对象(使用 JSON 序列化) + /// + public static T? DeepClone(this T obj) + { + if (obj == null) + return default; + + var json = obj.ToJson(); + return json.FromJson(); + } + + /// + /// 浅拷贝对象(使用 MemberwiseClone) + /// + public static T? ShallowClone(this T obj) where T : class + { + if (obj == null) + return null; + + var method = obj.GetType().GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic); + if (method != null) + return (T?)method.Invoke(obj, null); + + throw new InvalidOperationException("Object does not support MemberwiseClone"); + } + + #endregion + + #region 字典转换 + + /// + /// 将对象转换为字典 + /// + public static Dictionary? ToDictionary(this object obj) + { + if (obj == null) + return null; + + var dict = new Dictionary(); + + foreach (var prop in obj.GetType().GetProperties()) + { + if (prop.CanRead) + { + dict[prop.Name] = prop.GetValue(obj) ?? string.Empty; + } + } + + return dict; + } + + /// + /// 从字典创建对象 + /// + public static T? FromDictionary(this Dictionary? dict) where T : new() + { + if (dict == null) + return default; + + var obj = new T(); + + foreach (var prop in typeof(T).GetProperties()) + { + if (prop.CanWrite && dict.TryGetValue(prop.Name, out var value)) + { + if (value != null) + { + var convertedValue = Convert.ChangeType(value, prop.PropertyType); + prop.SetValue(obj, convertedValue); + } + } + } + + return obj; + } + + #endregion + + #region 属性访问 + + /// + /// 获取属性值 + /// + public static object? GetPropertyValue(this object obj, string propertyName) + { + if (obj == null || string.IsNullOrEmpty(propertyName)) + return null; + + var prop = obj.GetType().GetProperty(propertyName); + return prop?.GetValue(obj); + } + + /// + /// 设置属性值 + /// + public static void SetPropertyValue(this object obj, string propertyName, object? value) + { + if (obj == null || string.IsNullOrEmpty(propertyName)) + return; + + var prop = obj.GetType().GetProperty(propertyName); + prop?.SetValue(obj, value); + } + + #endregion + + #region 条件执行 + + /// + /// 条件执行(当条件满足时执行操作) + /// + public static T If(this T obj, bool condition, Action? action) + { + if (condition) + { + action?.Invoke(obj); + } + return obj; + } + + /// + /// 条件执行(当条件满足时返回函数结果) + /// + public static TResult? If(this T obj, bool condition, Func? func) + { + return condition ? func!(obj) : default; + } + + /// + /// 条件执行(当对象不为 null 时执行操作) + /// + public static T IfNotNull(this T obj, Action? action) where T : class + { + if (obj != null) + { + action?.Invoke(obj); + } + return obj; + } + + /// + /// 条件执行(当对象不为 null 时返回函数结果) + /// + public static TResult? IfNotNull(this T obj, Func func) where T : class + { + return obj != null ? func(obj) : default; + } + + #endregion + + #region 管道操作 + + /// + /// 管道操作(执行函数并返回结果) + /// + public static TResult Pipe(this T obj, Func func) + { + return func(obj); + } + + /// + /// 管道操作(执行操作) + /// + public static T Pipe(this T obj, Action action) + { + action(obj); + return obj; + } + + #endregion + + #region 对象检查 + + /// + /// 判断对象是否是指定类型 + /// [Obsolete("请直接使用 obj is T")] + /// + [Obsolete("请直接使用 obj is T", false)] + public static bool Is(this object obj) + { + return obj is T; + } + + /// + /// 判断对象是否实现了指定接口 + /// + public static bool Implements(this object obj) + { + if (obj == null) + return false; + + return typeof(TInterface).IsAssignableFrom(obj.GetType()); + } + + #endregion + + #region 对象相等比较 + + /// + /// 比较两个对象的属性值是否相等 + /// + public static bool PropertiesEqual(this T obj, T? other) where T : class + { + if (obj == null && other == null) + return true; + + if (obj == null || other == null) + return false; + + var type = typeof(T); + + foreach (var prop in type.GetProperties()) + { + if (!prop.CanRead) + continue; + + var value1 = prop.GetValue(obj); + var value2 = prop.GetValue(other); + + if (!Equals(value1, value2)) + return false; + } + + return true; + } + + #endregion + + #region 对象信息 + + /// + /// 获取对象的类型名称 + /// [Obsolete("请直接使用 obj?.GetType().Name")] + /// + [Obsolete("请直接使用 obj?.GetType().Name", false)] + public static string GetTypeName(this object obj) + { + return obj?.GetType().Name ?? "null"; + } + + /// + /// 获取对象的完整类型名称 + /// [Obsolete("请直接使用 obj?.GetType().FullName")] + /// + [Obsolete("请直接使用 obj?.GetType().FullName", false)] + public static string GetFullTypeName(this object obj) + { + return obj?.GetType().FullName ?? "null"; + } + + #endregion + + #region 对象转字符串 + + /// + /// 将对象转换为字符串(处理 null) + /// [Obsolete("请直接使用 obj?.ToString() ?? string.Empty")] + /// + [Obsolete("请直接使用 obj?.ToString() ?? string.Empty", false)] + public static string ToStringSafe(this object obj) + { + return obj?.ToString() ?? string.Empty; + } + + /// + /// 将对象转换为字符串(null 时返回默认值) + /// [Obsolete("请直接使用 obj?.ToString() ?? defaultValue")] + /// + [Obsolete("请直接使用 obj?.ToString() ?? defaultValue", false)] + public static string ToStringOrDefault(this object obj, string defaultValue = "") + { + return obj?.ToString() ?? defaultValue; + } + + #endregion + + #region 对象抛出异常 + + /// + /// 对象为 null 时抛出异常 + /// + public static T ThrowIfNull(this T? obj, string? paramName = null) where T : class + { + if (obj == null) + throw new ArgumentNullException(paramName ?? typeof(T).Name); + + return obj; + } + + /// + /// 条件不满足时抛出异常 + /// + public static T ThrowIf(this T obj, bool condition, string message) where T : class + { + if (condition) + throw new Exception(message); + + return obj; + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/ObjectUtil.cs b/EasyTool.Core/ToolCategory/ObjectUtil.cs index a2f3bcd..81c5921 100644 --- a/EasyTool.Core/ToolCategory/ObjectUtil.cs +++ b/EasyTool.Core/ToolCategory/ObjectUtil.cs @@ -21,7 +21,9 @@ public class ObjectUtil /// /// 检查对象是否为 null + /// [Obsolete("请直接使用 obj == null")] /// + [Obsolete("请直接使用 obj == null", false)] public static bool IsNull(object? obj) { return obj == null; @@ -29,7 +31,9 @@ public static bool IsNull(object? obj) /// /// 检查对象是否不为 null + /// [Obsolete("请直接使用 obj != null")] /// + [Obsolete("请直接使用 obj != null", false)] public static bool IsNotNull(object? obj) { return obj != null; @@ -86,7 +90,9 @@ public static bool IsNotNullOrEmpty(object? obj) /// /// 获取对象的类型名称 + /// [Obsolete("请直接使用 obj.GetType().Name")] /// + [Obsolete("请直接使用 obj.GetType().Name", false)] public static string GetTypeName(object obj) { return obj.GetType().Name; @@ -107,27 +113,89 @@ public static T Convert(object obj) { if (IsNull(obj)) { - return null; + // 处理可空值类型的默认值 + return GetDefault(targetType); } - if (targetType.IsAssignableFrom(obj.GetType())) + Type sourceType = obj.GetType(); + + // 如果目标类型可以从源类型赋值,直接返回 + if (targetType.IsAssignableFrom(sourceType)) { return obj; } + // 使用 TypeConverter 进行转换 + var converter = System.ComponentModel.TypeDescriptor.GetConverter(targetType); + if (converter != null && converter.CanConvertFrom(sourceType)) + { + return converter.ConvertFrom(obj); + } + + // 尝试从源类型的 TypeConverter 转换 + var sourceConverter = System.ComponentModel.TypeDescriptor.GetConverter(sourceType); + if (sourceConverter != null && sourceConverter.CanConvertTo(targetType)) + { + return sourceConverter.ConvertTo(obj, targetType); + } + + // 使用 IConvertible 接口转换 if (obj is IConvertible) { - return System.Convert.ChangeType(obj, targetType); + try + { + return System.Convert.ChangeType(obj, targetType); + } + catch (InvalidCastException) + { + // 继续尝试其他转换方式 + } } - // TODO: 支持自定义类型转换 + // 尝试使用隐式或显式转换操作符 + try + { + // 查找源类型的隐式转换操作符 + var implicitOp = sourceType.GetMethod("op_Implicit", new[] { sourceType }); + if (implicitOp != null && implicitOp.ReturnType == targetType) + { + return implicitOp.Invoke(null, new[] { obj }); + } + + // 查找源类型的显式转换操作符 + var explicitOp = sourceType.GetMethod("op_Explicit", new[] { sourceType }); + if (explicitOp != null && explicitOp.ReturnType == targetType) + { + return explicitOp.Invoke(null, new[] { obj }); + } + + // 查找目标类型的隐式转换操作符 + var targetImplicitOp = targetType.GetMethod("op_Implicit", new[] { sourceType }); + if (targetImplicitOp != null && targetImplicitOp.ReturnType == targetType) + { + return targetImplicitOp.Invoke(null, new[] { obj }); + } + + // 查找目标类型的显式转换操作符 + var targetExplicitOp = targetType.GetMethod("op_Explicit", new[] { sourceType }); + if (targetExplicitOp != null && targetExplicitOp.ReturnType == targetType) + { + return targetExplicitOp.Invoke(null, new[] { obj }); + } + } + catch (InvalidCastException) + { + // 转换操作符失败,继续抛出异常 + } - throw new InvalidCastException($"无法将类型为 {obj.GetType().Name} 的对象转换为类型为 {targetType.Name} 的对象"); + throw new InvalidCastException($"无法将类型为 {sourceType.Name} 的对象转换为类型为 {targetType.Name} 的对象"); } /// /// 获取对象的属性列表 + /// [Obsolete("请直接使用 obj.GetType().GetProperties()")] /// + [Obsolete("请直接使用 obj.GetType().GetProperties()", false)] public static IEnumerable GetProperties(object obj) { return obj.GetType().GetProperties(); @@ -151,7 +219,9 @@ public static void SetPropertyValue(object obj, string propertyName, object? val /// /// 获取对象的字段列表 + /// [Obsolete("请直接使用 obj.GetType().GetFields()")] /// + [Obsolete("请直接使用 obj.GetType().GetFields()", false)] public static IEnumerable GetFields(object obj) { return obj.GetType().GetFields(); @@ -175,7 +245,9 @@ public static void SetFieldValue(object obj, string fieldName, object? value) /// /// 获取对象的方法列表 + /// [Obsolete("请直接使用 obj.GetType().GetMethods()")] /// + [Obsolete("请直接使用 obj.GetType().GetMethods()", false)] public static IEnumerable GetMethods(object obj) { return obj.GetType().GetMethods(); @@ -183,7 +255,9 @@ public static IEnumerable GetMethods(object obj) /// /// 判断对象是否实现了指定接口 + /// [Obsolete("请直接使用 interfaceType.IsAssignableFrom(obj.GetType())")] /// + [Obsolete("请直接使用 interfaceType.IsAssignableFrom(obj.GetType())", false)] public static bool ImplementsInterface(object obj, Type interfaceType) { return interfaceType.IsAssignableFrom(obj.GetType()); @@ -191,7 +265,9 @@ public static bool ImplementsInterface(object obj, Type interfaceType) /// /// 判断对象是否为指定类型的实例 + /// [Obsolete("请直接使用 targetType.IsInstanceOfType(obj)")] /// + [Obsolete("请直接使用 targetType.IsInstanceOfType(obj)", false)] public static bool IsInstanceOfType(object obj, Type targetType) { return targetType.IsInstanceOfType(obj); @@ -474,7 +550,9 @@ public static IEnumerable Compare(object obj1, object obj2) /// /// 获取对象的哈希码 + /// [Obsolete("请直接使用 obj?.GetHashCode() ?? 0")] /// + [Obsolete("请直接使用 obj?.GetHashCode() ?? 0", false)] public static int GetHashCode(object obj) { if (IsNull(obj)) @@ -507,7 +585,9 @@ public static int GetHashCode(object obj) /// /// 判断对象是否为值类型 + /// [Obsolete("请直接使用 obj?.GetType().IsValueType ?? false")] /// + [Obsolete("请直接使用 obj?.GetType().IsValueType ?? false", false)] public static bool IsValueType(object obj) { if (IsNull(obj)) @@ -668,7 +748,9 @@ public static void CopyProperties(object source, object target) /// /// 获取指定类型的 Type 对象 + /// [Obsolete("请直接使用 Type.GetType(typeName)")] /// + [Obsolete("请直接使用 Type.GetType(typeName)", false)] public static Type GetType(string typeName) { return Type.GetType(typeName); @@ -676,7 +758,9 @@ public static Type GetType(string typeName) /// /// 获取对象的 Type 对象 + /// [Obsolete("请直接使用 obj.GetType()")] /// + [Obsolete("请直接使用 obj.GetType()", false)] public static Type GetType(object obj) { return obj.GetType(); @@ -684,7 +768,9 @@ public static Type GetType(object obj) /// /// 获取类型的所有成员信息,包括字段、属性、方法和事件等 + /// [Obsolete("请直接使用 type.GetMembers()")] /// + [Obsolete("请直接使用 type.GetMembers()", false)] public static MemberInfo[] GetMembers(Type type) { return type.GetMembers(); @@ -692,7 +778,9 @@ public static MemberInfo[] GetMembers(Type type) /// /// 获取类型的所有属性信息 + /// [Obsolete("请直接使用 type.GetProperties()")] /// + [Obsolete("请直接使用 type.GetProperties()", false)] public static PropertyInfo[] GetProperties(Type type) { return type.GetProperties(); @@ -700,7 +788,9 @@ public static PropertyInfo[] GetProperties(Type type) /// /// 获取类型的所有字段信息 + /// [Obsolete("请直接使用 type.GetFields()")] /// + [Obsolete("请直接使用 type.GetFields()", false)] public static FieldInfo[] GetFields(Type type) { return type.GetFields(); @@ -708,7 +798,9 @@ public static FieldInfo[] GetFields(Type type) /// /// 获取指定名称的属性信息 + /// [Obsolete("请直接使用 type.GetProperty(propertyName)")] /// + [Obsolete("请直接使用 type.GetProperty(propertyName)", false)] public static PropertyInfo GetProperty(Type type, string propertyName) { return type.GetProperty(propertyName); @@ -716,7 +808,9 @@ public static PropertyInfo GetProperty(Type type, string propertyName) /// /// 获取指定名称的属性信息 + /// [Obsolete("请直接使用 obj.GetType().GetProperty(propertyName)")] /// + [Obsolete("请直接使用 obj.GetType().GetProperty(propertyName)", false)] public static PropertyInfo GetProperty(object obj, string propertyName) { return obj.GetType().GetProperty(propertyName); @@ -724,7 +818,9 @@ public static PropertyInfo GetProperty(object obj, string propertyName) /// /// 获取指定名称的字段信息 + /// [Obsolete("请直接使用 type.GetField(fieldName)")] /// + [Obsolete("请直接使用 type.GetField(fieldName)", false)] public static FieldInfo GetField(Type type, string fieldName) { return type.GetField(fieldName); @@ -732,7 +828,9 @@ public static FieldInfo GetField(Type type, string fieldName) /// /// 获取指定名称的字段信息 + /// [Obsolete("请直接使用 obj.GetType().GetField(fieldName)")] /// + [Obsolete("请直接使用 obj.GetType().GetField(fieldName)", false)] public static FieldInfo GetField(object obj, string fieldName) { return obj.GetType().GetField(fieldName); @@ -740,7 +838,9 @@ public static FieldInfo GetField(object obj, string fieldName) /// /// 获取指定名称的方法信息 + /// [Obsolete("请直接使用 type.GetMethod(methodName)")] /// + [Obsolete("请直接使用 type.GetMethod(methodName)", false)] public static MethodInfo GetMethod(Type type, string methodName) { return type.GetMethod(methodName); @@ -748,7 +848,9 @@ public static MethodInfo GetMethod(Type type, string methodName) /// /// 获取指定名称的方法信息 + /// [Obsolete("请直接使用 obj.GetType().GetMethod(methodName)")] /// + [Obsolete("请直接使用 obj.GetType().GetMethod(methodName)", false)] public static MethodInfo GetMethod(object obj, string methodName) { return obj.GetType().GetMethod(methodName); @@ -756,7 +858,9 @@ public static MethodInfo GetMethod(object obj, string methodName) /// /// 获取指定名称和参数类型的方法信息 + /// [Obsolete("请直接使用 type.GetMethod(methodName, parameterTypes)")] /// + [Obsolete("请直接使用 type.GetMethod(methodName, parameterTypes)", false)] public static MethodInfo GetMethod(Type type, string methodName, Type[] parameterTypes) { return type.GetMethod(methodName, parameterTypes); @@ -764,7 +868,9 @@ public static MethodInfo GetMethod(Type type, string methodName, Type[] paramete /// /// 获取指定名称和参数类型的方法信息 + /// [Obsolete("请直接使用 obj.GetType().GetMethod(methodName, parameterTypes)")] /// + [Obsolete("请直接使用 obj.GetType().GetMethod(methodName, parameterTypes)", false)] public static MethodInfo GetMethod(object obj, string methodName, Type[] parameterTypes) { return obj.GetType().GetMethod(methodName, parameterTypes); @@ -792,7 +898,9 @@ public static object InvokeMethod(object obj, string methodName, Type[] paramete /// /// 创建指定类型的实例 + /// [Obsolete("请直接使用 Activator.CreateInstance(type, constructorParameters)")] /// + [Obsolete("请直接使用 Activator.CreateInstance(type, constructorParameters)", false)] public static object CreateInstance(Type type, object[] constructorParameters) { return Activator.CreateInstance(type, constructorParameters); @@ -801,7 +909,9 @@ public static object CreateInstance(Type type, object[] constructorParameters) /// /// 判断指定类型是否派生自指定的基类或接口 + /// [Obsolete("请直接使用 type.IsSubclassOf(baseType)")] /// + [Obsolete("请直接使用 type.IsSubclassOf(baseType)", false)] public static bool IsSubclassOf(Type type, Type baseType) { return type.IsSubclassOf(baseType); @@ -820,7 +930,9 @@ public static Type[] GetSubclassesOf(Type baseType) /// /// 获取指定类型实现的所有接口类型 + /// [Obsolete("请直接使用 type.GetInterfaces()")] /// + [Obsolete("请直接使用 type.GetInterfaces()", false)] public static Type[] GetInterfaces(Type type) { return type.GetInterfaces(); @@ -828,7 +940,9 @@ public static Type[] GetInterfaces(Type type) /// /// 获取指定类型的程序集限定名 + /// [Obsolete("请直接使用 type.AssemblyQualifiedName")] /// + [Obsolete("请直接使用 type.AssemblyQualifiedName", false)] public static string GetAssemblyQualifiedName(Type type) { return type.AssemblyQualifiedName; @@ -852,7 +966,9 @@ public static bool IsDefaultValue(object obj) /// /// 判断指定类型是否是可空类型 + /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) != null")] /// + [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) != null", false)] public static bool IsNullable(Type type) { return Nullable.GetUnderlyingType(type) != null; @@ -860,7 +976,9 @@ public static bool IsNullable(Type type) /// /// 获取可空类型的基础类型 + /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type")] /// + [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type", false)] public static Type GetNullableType(Type type) { return Nullable.GetUnderlyingType(type) ?? type; @@ -944,7 +1062,9 @@ public static bool IsDateTimeType(Type type) /// /// 判断指定类型是否是枚举类型 + /// [Obsolete("请直接使用 type.IsEnum")] /// + [Obsolete("请直接使用 type.IsEnum", false)] public static bool IsEnumType(Type type) { return type.IsEnum; diff --git a/EasyTool.Core/ToolCategory/ProcessUtil.cs b/EasyTool.Core/ToolCategory/ProcessUtil.cs index 210f34d..63a9d8b 100644 --- a/EasyTool.Core/ToolCategory/ProcessUtil.cs +++ b/EasyTool.Core/ToolCategory/ProcessUtil.cs @@ -29,9 +29,11 @@ public static Process GetProcessByName(string processName) /// /// 获取进程的所有线程 + /// [Obsolete("请直接使用 process.Threads")] /// /// 进程 /// 线程集合 + [Obsolete("请直接使用 process.Threads", false)] public static ProcessThreadCollection GetProcessThreads(Process process) { return process.Threads; @@ -39,9 +41,11 @@ public static ProcessThreadCollection GetProcessThreads(Process process) /// /// 获取进程的主窗口句柄 + /// [Obsolete("请直接使用 process.MainWindowHandle")] /// /// 进程 /// 窗口句柄 + [Obsolete("请直接使用 process.MainWindowHandle", false)] public static IntPtr GetMainWindowHandle(Process process) { return process.MainWindowHandle; @@ -49,9 +53,11 @@ public static IntPtr GetMainWindowHandle(Process process) /// /// 获取进程的主窗口标题 + /// [Obsolete("请直接使用 process.MainWindowTitle")] /// /// 进程 /// 窗口标题 + [Obsolete("请直接使用 process.MainWindowTitle", false)] public static string GetMainWindowTitle(Process process) { return process.MainWindowTitle; @@ -59,9 +65,11 @@ public static string GetMainWindowTitle(Process process) /// /// 获取进程的所有模块 + /// [Obsolete("请直接使用 process.Modules")] /// /// 进程 /// 模块集合 + [Obsolete("请直接使用 process.Modules", false)] public static ProcessModuleCollection GetProcessModules(Process process) { return process.Modules; @@ -69,8 +77,10 @@ public static ProcessModuleCollection GetProcessModules(Process process) /// /// 关闭进程 + /// [Obsolete("请直接使用 process.Kill()")] /// /// 进程 + [Obsolete("请直接使用 process.Kill()", false)] public static void KillProcess(Process process) { process.Kill(); @@ -88,9 +98,11 @@ public static void KillProcessAndWait(Process process) /// /// 启动新进程 + /// [Obsolete("请直接使用 Process.Start(fileName)")] /// /// 文件名 /// 新进程 + [Obsolete("请直接使用 Process.Start(fileName)", false)] public static Process StartProcess(string fileName) { return Process.Start(fileName); @@ -118,9 +130,11 @@ public static bool IsProcessExists(string processName) /// /// 获取进程使用的内存大小 + /// [Obsolete("请直接使用 process.WorkingSet64")] /// /// 进程 /// 内存大小(字节) + [Obsolete("请直接使用 process.WorkingSet64", false)] public static long GetProcessMemorySize(Process process) { return process.WorkingSet64; diff --git a/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs b/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs new file mode 100644 index 0000000..5bf41fb --- /dev/null +++ b/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs @@ -0,0 +1,390 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; + +namespace EasyTool.Extension +{ + /// + /// PropertyInfo 扩展方法 + /// + public static class PropertyInfoExtension + { + #region 值获取 + + /// + /// 安全获取属性值,失败返回默认值 + /// + public static object? GetValueOrDefault(this PropertyInfo? property, object? obj) + { + if (property == null || obj == null) + return null; + + try + { + return property.GetValue(obj); + } + catch + { + return null; + } + } + + /// + /// 安全获取属性值,失败返回指定默认值 + /// + public static T? GetValueOrDefault(this PropertyInfo? property, object? obj, T? defaultValue = default) + { + if (property == null || obj == null) + return defaultValue; + + try + { + var value = property.GetValue(obj); + if (value == null) + return defaultValue; + + return (T)value; + } + catch + { + return defaultValue; + } + } + + #endregion + + #region 值设置 + + /// + /// 安全设置属性值 + /// + public static bool SetValueSafe(this PropertyInfo? property, object? obj, object? value) + { + if (property == null || obj == null) + return false; + + try + { + if (!property.CanWrite) + return false; + + property.SetValue(obj, value); + return true; + } + catch + { + return false; + } + } + + /// + /// 设置属性值(支持类型转换) + /// + public static bool SetValueWithConvert(this PropertyInfo? property, object? obj, object? value) + { + if (property == null || obj == null || !property.CanWrite) + return false; + + try + { + object? convertedValue = value; + + // 如果类型不匹配,尝试转换 + if (value != null && value.GetType() != property.PropertyType) + { + // 处理可空类型 + var targetType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; + + if (targetType.IsEnum && value is string str) + { + convertedValue = Enum.Parse(targetType, str); + } + else + { + convertedValue = Convert.ChangeType(value, targetType); + } + } + + property.SetValue(obj, convertedValue); + return true; + } + catch + { + return false; + } + } + + #endregion + + #region 特性检查 + + /// + /// 判断属性是否有指定特性 + /// + public static bool HasAttribute(this PropertyInfo? property) where T : Attribute + { + if (property == null) + return false; + + return property.GetCustomAttribute() != null; + } + + /// + /// 判断属性是否有指定特性 + /// + public static bool HasAttribute(this PropertyInfo? property, Type? attributeType) + { + if (property == null || attributeType == null) + return false; + + return property.GetCustomAttributes(attributeType, false).Any(); + } + + /// + /// 获取属性特性 + /// + public static T? GetAttribute(this PropertyInfo? property) where T : Attribute + { + if (property == null) + return null; + + return property.GetCustomAttribute(); + } + + /// + /// 获取属性的所有特性 + /// + public static T[] GetAttributes(this PropertyInfo? property) where T : Attribute + { + if (property == null) + return Array.Empty(); + + return property.GetCustomAttributes().ToArray(); + } + + #endregion + + #region DataAnnotations 特性快捷访问 + + /// + /// 判断是否是必填项 + /// + public static bool IsRequired(this PropertyInfo? property) + { + return property.HasAttribute(); + } + + /// + /// 获取显示名称 + /// + public static string GetDisplayName(this PropertyInfo? property) + { + var displayAttr = property.GetAttribute(); + if (displayAttr != null && !string.IsNullOrEmpty(displayAttr.GetName())) + return displayAttr.GetName()!; + + var displayNameAttr = property.GetAttribute(); + if (displayNameAttr != null && !string.IsNullOrEmpty(displayNameAttr.DisplayName)) + return displayNameAttr.DisplayName; + + return property?.Name ?? string.Empty; + } + + /// + /// 获取描述 + /// + public static string GetDescription(this PropertyInfo? property) + { + var descriptionAttr = property.GetAttribute(); + if (descriptionAttr != null && !string.IsNullOrEmpty(descriptionAttr.Description)) + return descriptionAttr.Description; + + var displayAttr = property.GetAttribute(); + if (displayAttr != null && !string.IsNullOrEmpty(displayAttr.GetDescription())) + return displayAttr.GetDescription()!; + + return string.Empty; + } + + /// + /// 获取字符串长度限制 + /// + public static int GetStringLength(this PropertyInfo? property) + { + var attr = property.GetAttribute(); + return attr?.MaximumLength ?? 0; + } + + /// + /// 获取数据类型 + /// + public static string GetDataType(this PropertyInfo? property) + { + var attr = property.GetAttribute(); + return attr?.DataType.ToString() ?? string.Empty; + } + + #endregion + + #region 类型判断 + + /// + /// 判断是否是字符串类型 + /// + public static bool IsString(this PropertyInfo? property) + { + return property?.PropertyType == typeof(string); + } + + /// + /// 判断是否是数值类型 + /// + public static bool IsNumeric(this PropertyInfo? property) + { + var type = Nullable.GetUnderlyingType(property?.PropertyType) ?? property?.PropertyType; + if (type == null) + return false; + + return type == typeof(byte) || + type == typeof(sbyte) || + type == typeof(short) || + type == typeof(ushort) || + type == typeof(int) || + type == typeof(uint) || + type == typeof(long) || + type == typeof(ulong) || + type == typeof(float) || + type == typeof(double) || + type == typeof(decimal); + } + + /// + /// 判断是否是日期类型 + /// + public static bool IsDateTime(this PropertyInfo? property) + { + var type = Nullable.GetUnderlyingType(property?.PropertyType) ?? property?.PropertyType; + if (type == null) + return false; + + return type == typeof(DateTime) || type == typeof(DateTimeOffset); + } + + /// + /// 判断是否是布尔类型 + /// + public static bool IsBoolean(this PropertyInfo? property) + { + var type = Nullable.GetUnderlyingType(property?.PropertyType) ?? property?.PropertyType; + if (type == null) + return false; + + return type == typeof(bool); + } + + /// + /// 判断是否是枚举类型 + /// + public static bool IsEnum(this PropertyInfo? property) + { + var type = Nullable.GetUnderlyingType(property?.PropertyType) ?? property?.PropertyType; + return type?.IsEnum == true; + } + + /// + /// 判断是否是集合类型 + /// + public static bool IsCollection(this PropertyInfo? property) + { + if (property == null) + return false; + + return typeof(System.Collections.IEnumerable).IsAssignableFrom(property.PropertyType) && + property.PropertyType != typeof(string); + } + + /// + /// 判断是否是可空类型 + /// + public static bool IsNullable(this PropertyInfo? property) + { + if (property == null) + return false; + + return Nullable.GetUnderlyingType(property.PropertyType) != null; + } + + #endregion + + #region 访问判断 + + /// + /// 判断是否可读 + /// + public static bool CanRead(this PropertyInfo? property) + { + return property?.CanRead == true; + } + + /// + /// 判断是否可写 + /// + public static bool CanWrite(this PropertyInfo? property) + { + return property?.CanWrite == true; + } + + /// + /// 判断是否有公共的 getter + /// + public static bool HasPublicGetter(this PropertyInfo? property) + { + return property?.CanRead == true && property.GetGetMethod(false) != null; + } + + /// + /// 判断是否有公共的 setter + /// + public static bool HasPublicSetter(this PropertyInfo? property) + { + return property?.CanWrite == true && property.GetSetMethod(false) != null; + } + + #endregion + + #region 获取元素类型 + + /// + /// 获取集合的元素类型 + /// + public static Type? GetElementType(this PropertyInfo? property) + { + if (property == null) + return null; + + var type = property.PropertyType; + + // 处理数组 + if (type.IsArray) + return type.GetElementType(); + + // 处理泛型集合 + if (type.IsGenericType) + { + var genericType = type.GetGenericTypeDefinition(); + if (genericType == typeof(System.Collections.Generic.IEnumerable<>) || + genericType == typeof(System.Collections.Generic.List<>) || + genericType == typeof(System.Collections.Generic.IList<>) || + genericType == typeof(System.Collections.Generic.ICollection<>)) + { + return type.GetGenericArguments()[0]; + } + } + + return null; + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/ReflectUtil.cs b/EasyTool.Core/ToolCategory/ReflectUtil.cs index a2a6374..2226924 100644 --- a/EasyTool.Core/ToolCategory/ReflectUtil.cs +++ b/EasyTool.Core/ToolCategory/ReflectUtil.cs @@ -11,9 +11,11 @@ public class ReflectUtil { /// /// 根据类型名称获取Type对象 + /// [Obsolete("请直接使用 Type.GetType(typeName)")] /// /// 类型名称 /// Type对象 + [Obsolete("请直接使用 Type.GetType(typeName)", false)] public static Type GetType(string typeName) { return Type.GetType(typeName); @@ -21,9 +23,11 @@ public static Type GetType(string typeName) /// /// 获取指定程序集中的所有类型 + /// [Obsolete("请直接使用 assembly.GetTypes()")] /// /// 程序集 /// 类型数组 + [Obsolete("请直接使用 assembly.GetTypes()", false)] public static Type[] GetTypes(Assembly assembly) { return assembly.GetTypes(); @@ -31,9 +35,11 @@ public static Type[] GetTypes(Assembly assembly) /// /// 获取指定类型所在的程序集 + /// [Obsolete("请直接使用 type.Assembly")] /// /// 类型 /// 程序集 + [Obsolete("请直接使用 type.Assembly", false)] public static Assembly GetAssembly(Type type) { return type.Assembly; @@ -41,10 +47,12 @@ public static Assembly GetAssembly(Type type) /// /// 获取指定类型的指定类型的特性 + /// [Obsolete("请直接使用 type.GetCustomAttribute()")] /// /// 特性类型 /// 类型 /// 特性对象 + [Obsolete("请直接使用 type.GetCustomAttribute()", false)] public static T GetAttribute(Type type) where T : Attribute { return type.GetCustomAttribute(); @@ -63,9 +71,11 @@ public static T[] GetAttributes(Type type) where T : Attribute /// /// 获取指定类型的默认值 + /// [Obsolete("请直接使用 type.IsValueType ? Activator.CreateInstance(type) : null")] /// /// 类型 /// 默认值 + [Obsolete("请直接使用 type.IsValueType ? Activator.CreateInstance(type) : null", false)] public static object GetDefaultValue(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; @@ -73,9 +83,11 @@ public static object GetDefaultValue(Type type) /// /// 获取类型的基类 + /// [Obsolete("请直接使用 type.BaseType")] /// /// 类型 /// 基类 + [Obsolete("请直接使用 type.BaseType", false)] public static Type GetBaseType(Type type) { return type.BaseType; @@ -83,10 +95,12 @@ public static Type GetBaseType(Type type) /// /// 判断类型是否实现了某个接口 + /// [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)")] /// /// 类型 /// 接口类型 /// 是否实现 + [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)", false)] public static bool HasInterface(Type type, Type interfaceType) { return interfaceType.IsAssignableFrom(type); @@ -94,9 +108,11 @@ public static bool HasInterface(Type type, Type interfaceType) /// /// 获取方法的参数信息 + /// [Obsolete("请直接使用 method.GetParameters()")] /// /// 方法 /// 参数信息数组 + [Obsolete("请直接使用 method.GetParameters()", false)] public static ParameterInfo[] GetParameters(MethodInfo method) { return method.GetParameters(); @@ -154,9 +170,11 @@ public static EventInfo[] GetEvents(Type type) /// /// 获取类型的所有接口 + /// [Obsolete("请直接使用 type.GetInterfaces()")] /// /// 类型 /// 接口数组 + [Obsolete("请直接使用 type.GetInterfaces()", false)] public static Type[] GetInterfaces(Type type) { return type.GetInterfaces(); diff --git a/EasyTool.Core/ToolCategory/RegexUtil.cs b/EasyTool.Core/ToolCategory/RegexUtil.cs index 4694e9a..5f5a1d5 100644 --- a/EasyTool.Core/ToolCategory/RegexUtil.cs +++ b/EasyTool.Core/ToolCategory/RegexUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -13,10 +13,12 @@ public class RegexUtil { /// /// 验证字符串是否与指定的正则表达式匹配 + /// [Obsolete("请直接使用 Regex.IsMatch(input, pattern)")] /// /// 要验证的字符串 /// 正则表达式 /// 如果字符串与正则表达式匹配,则为true;否则为false + [Obsolete("请直接使用 Regex.IsMatch(input, pattern)", false)] public static bool IsMatch(string input, string pattern) { return Regex.IsMatch(input, pattern); @@ -28,7 +30,7 @@ public static bool IsMatch(string input, string pattern) /// 要验证的字符串 /// 正则表达式 /// 如果字符串与正则表达式匹配,则为匹配结果;否则为null - public static string Match(string input, string pattern) + public static string? Match(string input, string pattern) { Match match = Regex.Match(input, pattern); return match.Success ? match.Value : null; @@ -47,11 +49,13 @@ public static string[] Matches(string input, string pattern) /// /// 使用指定的替换字符串替换输入字符串中与指定正则表达式匹配的所有子字符串 + /// [Obsolete("请直接使用 Regex.Replace(input, pattern, replacement)")] /// /// 要替换的字符串 /// 正则表达式 /// 替换字符串 /// 替换后的字符串 + [Obsolete("请直接使用 Regex.Replace(input, pattern, replacement)", false)] public static string Replace(string input, string pattern, string replacement) { return Regex.Replace(input, pattern, replacement); @@ -65,7 +69,7 @@ public static string Replace(string input, string pattern, string replacement) /// 替换字符串 /// 替换次数 /// 包含替换后的字符串和替换次数的元组 - public static (string, int) Replace(string input, string pattern, string replacement, int count) + public static (string?, int) Replace(string input, string pattern, string replacement, int count) { string result = Regex.Replace(input, pattern, replacement, RegexOptions.None, TimeSpan.FromSeconds(1)); return (result, Regex.Matches(input, pattern).Count); @@ -77,7 +81,7 @@ public static (string, int) Replace(string input, string pattern, string replace /// 要验证的字符串 /// 正则表达式 /// 所有分组匹配结果的字符串数组 - public static string[] MatchGroups(string input, string pattern) + public static string[]? MatchGroups(string input, string pattern) { Match match = Regex.Match(input, pattern); if (match.Success) @@ -96,7 +100,7 @@ public static string[] MatchGroups(string input, string pattern) /// 要验证的字符串 /// 正则表达式 /// 所有分组匹配结果及分组名称的字典 - public static Dictionary MatchGroupsWithNames(string input, string pattern) + public static Dictionary? MatchGroupsWithNames(string input, string pattern) { Match match = Regex.Match(input, pattern); if (match.Success) @@ -115,7 +119,7 @@ public static Dictionary MatchGroupsWithNames(string input, stri /// 要验证的字符串 /// 正则表达式 /// 匹配结果和捕获组名称的字典 - public static Dictionary MatchWithGroupNames(string input, string pattern) + public static Dictionary? MatchWithGroupNames(string input, string pattern) { Match match = Regex.Match(input, pattern); if (match.Success) @@ -134,7 +138,7 @@ public static Dictionary MatchWithGroupNames(string input, strin /// 要验证的字符串 /// 正则表达式 /// 所有分组匹配结果及分组名称的元组 - public static (string, Dictionary) MatchGroupsWithNamesTuple(string input, string pattern) + public static (string?, Dictionary?) MatchGroupsWithNamesTuple(string input, string pattern) { Match match = Regex.Match(input, pattern); if (match.Success) diff --git a/EasyTool.Core/ToolCategory/RuntimeUtil.cs b/EasyTool.Core/ToolCategory/RuntimeUtil.cs index eb37da6..eea6036 100644 --- a/EasyTool.Core/ToolCategory/RuntimeUtil.cs +++ b/EasyTool.Core/ToolCategory/RuntimeUtil.cs @@ -16,8 +16,10 @@ public class RuntimeUtil { /// /// 获取当前运行的 .NET 版本 + /// [Obsolete("请直接使用 Environment.Version.ToString()")] /// /// .NET 版本 + [Obsolete("请直接使用 Environment.Version.ToString()", false)] public static string GetDotNetVersion() { return Environment.Version.ToString(); @@ -25,8 +27,10 @@ public static string GetDotNetVersion() /// /// 获取当前操作系统版本 + /// [Obsolete("请直接使用 Environment.OSVersion.ToString()")] /// /// 操作系统版本 + [Obsolete("请直接使用 Environment.OSVersion.ToString()", false)] public static string GetOSVersion() { return Environment.OSVersion.ToString(); diff --git a/EasyTool.Core/ToolCategory/StrExtension.cs b/EasyTool.Core/ToolCategory/StrExtension.cs index 651a010..20933be 100644 --- a/EasyTool.Core/ToolCategory/StrExtension.cs +++ b/EasyTool.Core/ToolCategory/StrExtension.cs @@ -1,21 +1,337 @@ -using System.Text.RegularExpressions; +using System; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; namespace EasyTool.Extension { + /// + /// 字符串扩展方法 + /// public static class StrExtension { #region 文本可为空判断 + /// + /// 判断字符串是否为 null 或空 + /// [Obsolete("请直接使用 string.IsNullOrEmpty(value)")] + /// + [Obsolete("请直接使用 string.IsNullOrEmpty(value)", false)] public static bool IsNullOrEmpty(this string value) { return string.IsNullOrEmpty(value); } + /// + /// 判断字符串是否为 null 或空白字符 + /// [Obsolete("请直接使用 string.IsNullOrWhiteSpace(value)")] + /// + [Obsolete("请直接使用 string.IsNullOrWhiteSpace(value)", false)] public static bool IsNullOrWhiteSpace(this string value) { return string.IsNullOrWhiteSpace(value); } #endregion + + #region 字符串验证 + + /// + /// 判断字符串是否是有效的电子邮件地址 + /// + public static bool IsEmail(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + const string pattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"; + return Regex.IsMatch(value, pattern); + } + + /// + /// 判断字符串是否是有效的手机号(中国大陆) + /// + public static bool IsPhoneNumber(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + // 中国大陆手机号:1开头,11位数字 + const string pattern = @"^1[3-9]\d{9}$"; + return Regex.IsMatch(value, pattern); + } + + /// + /// 判断字符串是否是有效的 URL + /// + public static bool IsUrl(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + const string pattern = @"^(https?|ftp)://[^\s/$.?#].[^\s]*$"; + return Regex.IsMatch(value, pattern, RegexOptions.IgnoreCase); + } + + /// + /// 判断字符串是否是有效的 IPv4 地址 + /// + public static bool IsIPv4(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + const string pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + return Regex.IsMatch(value, pattern); + } + + /// + /// 判断字符串是否是有效的身份证号(中国大陆) + /// + public static bool IsIdCard(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + // 18位身份证号码 + const string pattern = @"^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$"; + return Regex.IsMatch(value, pattern); + } + + #endregion + + #region 字符串转换 + + /// + /// 将字符串转换为 Base64 编码 + /// + public static string ToBase64(this string value) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + var bytes = Encoding.UTF8.GetBytes(value); + return Convert.ToBase64String(bytes); + } + + /// + /// 将 Base64 编码的字符串解码 + /// + public static string FromBase64(this string value) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + var bytes = Convert.FromBase64String(value); + return Encoding.UTF8.GetString(bytes); + } + + /// + /// 计算字符串的 MD5 哈希值 + /// + public static string ToMd5(this string value) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + using var md5 = System.Security.Cryptography.MD5.Create(); + var bytes = Encoding.UTF8.GetBytes(value); + var hash = md5.ComputeHash(bytes); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + + /// + /// 计算字符串的 SHA256 哈希值 + /// + public static string ToSha256(this string value) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + using var sha256 = System.Security.Cryptography.SHA256.Create(); + var bytes = Encoding.UTF8.GetBytes(value); + var hash = sha256.ComputeHash(bytes); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + + /// + /// 将字符串转换为16进制表示 + /// + public static string ToHex(this string value) + { + if (string.IsNullOrEmpty(value)) + return string.Empty; + + var bytes = Encoding.UTF8.GetBytes(value); + return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant(); + } + + #endregion + + #region 字符串处理 + + /// + /// 截断字符串到指定长度,超出部分用省略号代替 + /// + /// 原始字符串 + /// 最大长度 + /// 后缀,默认为"..." + public static string Truncate(this string value, int maxLength, string suffix = "...") + { + if (string.IsNullOrEmpty(value) || value.Length <= maxLength) + return value; + + return value.Substring(0, maxLength) + suffix; + } + + /// + /// 移除字符串中的音调符号(如 é -> e) + /// + public static string RemoveDiacritics(this string value) + { + if (string.IsNullOrEmpty(value)) + return value; + + var normalizedString = value.Normalize(NormalizationForm.FormD); + var sb = new StringBuilder(); + + foreach (var c in normalizedString) + { + var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); + if (unicodeCategory != UnicodeCategory.NonSpacingMark) + { + sb.Append(c); + } + } + + return sb.ToString().Normalize(NormalizationForm.FormC); + } + + /// + /// 生成 URL 友好的 slug(例如:"Hello World" -> "hello-world") + /// + public static string GenerateSlug(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return string.Empty; + + // 移除音调符号 + var slug = value.RemoveDiacritics(); + + // 转换为小写 + slug = slug.ToLowerInvariant(); + + // 替换空格和特殊字符为连字符 + slug = Regex.Replace(slug, @"[^a-z0-9\s-]", ""); + slug = Regex.Replace(slug, @"\s+", "-"); + slug = Regex.Replace(slug, @"-+", "-"); + slug = slug.Trim('-'); + + return slug; + } + + /// + /// 反转字符串 + /// [Obsolete("请直接使用 new string(value.Reverse().ToArray()) 或通过 LINQ")] + /// + [Obsolete("请直接使用 new string(value.Reverse().ToArray())", false)] + public static string Reverse(this string value) + { + if (string.IsNullOrEmpty(value)) + return value; + + var charArray = value.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + + /// + /// 获取字符串的字节数(UTF-8编码) + /// [Obsolete("请直接使用 Encoding.UTF8.GetByteCount(value)")] + /// + [Obsolete("请直接使用 Encoding.UTF8.GetByteCount(value)", false)] + public static int GetByteCount(this string value) + { + if (string.IsNullOrEmpty(value)) + return 0; + + return Encoding.UTF8.GetByteCount(value); + } + + /// + /// 隐藏字符串的中间部分(例如:手机号、身份证号) + /// + /// 原始字符串 + /// 开头保留字符数 + /// 结尾保留字符数 + /// 掩码字符,默认为'*' + public static string Mask(this string value, int visibleStart = 3, int visibleEnd = 4, char maskChar = '*') + { + if (string.IsNullOrEmpty(value)) + return value; + + if (value.Length <= visibleStart + visibleEnd) + return value; + + var start = value.Substring(0, visibleStart); + var end = value.Substring(value.Length - visibleEnd); + var maskLength = value.Length - visibleStart - visibleEnd; + var mask = new string(maskChar, maskLength); + + return start + mask + end; + } + + #endregion + + #region 字符串操作 + + /// + /// 移除字符串中指定的字符 + /// + public static string RemoveChars(this string value, params char[] charsToRemove) + { + if (string.IsNullOrEmpty(value) || charsToRemove == null || charsToRemove.Length == 0) + return value; + + var result = new StringBuilder(value.Length); + foreach (var c in value) + { + if (Array.IndexOf(charsToRemove, c) < 0) + { + result.Append(c); + } + } + return result.ToString(); + } + + /// + /// 确保字符串以指定后缀结尾 + /// + public static string EnsureEndsWith(this string value, string suffix) + { + if (string.IsNullOrEmpty(value)) + return suffix ?? string.Empty; + + if (string.IsNullOrEmpty(suffix)) + return value; + + return value.EndsWith(suffix) ? value : value + suffix; + } + + /// + /// 确保字符串以指定前缀开头 + /// + public static string EnsureStartsWith(this string value, string prefix) + { + if (string.IsNullOrEmpty(value)) + return prefix ?? string.Empty; + + if (string.IsNullOrEmpty(prefix)) + return value; + + return value.StartsWith(prefix) ? value : prefix + value; + } + + #endregion } } diff --git a/EasyTool.Core/ToolCategory/StrUtil.cs b/EasyTool.Core/ToolCategory/StrUtil.cs index 938649c..aaaf9be 100644 --- a/EasyTool.Core/ToolCategory/StrUtil.cs +++ b/EasyTool.Core/ToolCategory/StrUtil.cs @@ -22,11 +22,13 @@ public static string RemoveAllSpaces(string str) /// /// 将字符串中的指定字符替换成新的字符 + /// [Obsolete("请直接使用 str.Replace(oldChar, newChar)")] /// /// 要处理的字符串 /// 要替换的字符 /// 新的字符 /// 处理后的字符串 + [Obsolete("请直接使用 str.Replace(oldChar, newChar)", false)] public static string ReplaceChar(string str, char oldChar, char newChar) { return str.Replace(oldChar, newChar); @@ -67,9 +69,11 @@ public static bool IsDate(string str) /// /// 获取字符串的字节数组 + /// [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)")] /// /// 要处理的字符串 /// 字符串的字节数组 + [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)", false)] public static byte[] GetBytes(string str) { return System.Text.Encoding.UTF8.GetBytes(str); @@ -77,9 +81,11 @@ public static byte[] GetBytes(string str) /// /// 将字节数组转换为字符串 + /// [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)")] /// /// 要处理的字节数组 /// 字节数组转换后的字符串 + [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)", false)] public static string GetString(byte[] bytes) { return System.Text.Encoding.UTF8.GetString(bytes); @@ -87,9 +93,11 @@ public static string GetString(byte[] bytes) /// /// 将字符串转换为大写 + /// [Obsolete("请直接使用 str.ToUpper()")] /// /// 要处理的字符串 /// 处理后的字符串 + [Obsolete("请直接使用 str.ToUpper()", false)] public static string ToUpperCase(string str) { return str.ToUpper(); @@ -97,9 +105,11 @@ public static string ToUpperCase(string str) /// /// 将字符串转换为小写 + /// [Obsolete("请直接使用 str.ToLower()")] /// /// 要处理的字符串 /// 处理后的字符串 + [Obsolete("请直接使用 str.ToLower()", false)] public static string ToLowerCase(string str) { return str.ToLower(); @@ -107,9 +117,11 @@ public static string ToLowerCase(string str) /// /// 检查字符串是否为空或null + /// [Obsolete("请直接使用 string.IsNullOrEmpty(str)")] /// /// 要检查的字符串 /// 如果是空或null,则返回true,否则返回false + [Obsolete("请直接使用 string.IsNullOrEmpty(str)", false)] public static bool IsNullOrEmpty(string str) { return string.IsNullOrEmpty(str); @@ -117,9 +129,11 @@ public static bool IsNullOrEmpty(string str) /// /// 检查字符串是否为空或仅由空格组成 + /// [Obsolete("请直接使用 string.IsNullOrWhiteSpace(str)")] /// /// 要检查的字符串 /// 如果是空或仅由空格组成,则返回true,否则返回false + [Obsolete("请直接使用 string.IsNullOrWhiteSpace(str)", false)] public static bool IsNullOrWhiteSpace(string str) { return string.IsNullOrWhiteSpace(str); @@ -127,11 +141,13 @@ public static bool IsNullOrWhiteSpace(string str) /// /// 截取字符串的指定部分 + /// [Obsolete("请直接使用 str.Substring(startIndex, length)")] /// /// 要处理的字符串 /// 起始位置(从0开始) /// 要截取的长度 /// 截取后的字符串 + [Obsolete("请直接使用 str.Substring(startIndex, length)", false)] public static string Substring(string str, int startIndex, int length) { return str.Substring(startIndex, length); @@ -245,11 +261,13 @@ public static bool EqualsIgnoreCaseAndWhiteSpace(string str1, string str2) /// /// 在字符串的左侧填充指定字符,使字符串达到指定长度 + /// [Obsolete("请直接使用 str.PadLeft(length, paddingChar)")] /// /// 要处理的字符串 /// 指定长度 /// 填充字符 /// 处理后的字符串 + [Obsolete("请直接使用 str.PadLeft(length, paddingChar)", false)] public static string PadLeft(string str, int length, char paddingChar) { return str.PadLeft(length, paddingChar); @@ -257,11 +275,13 @@ public static string PadLeft(string str, int length, char paddingChar) /// /// 在字符串的右侧填充指定字符,使字符串达到指定长度 + /// [Obsolete("请直接使用 str.PadRight(length, paddingChar)")] /// /// 要处理的字符串 /// 指定长度 /// 填充字符 /// 处理后的字符串 + [Obsolete("请直接使用 str.PadRight(length, paddingChar)", false)] public static string PadRight(string str, int length, char paddingChar) { return str.PadRight(length, paddingChar); diff --git a/EasyTool.Core/ToolCategory/StringBuilderExtension.cs b/EasyTool.Core/ToolCategory/StringBuilderExtension.cs new file mode 100644 index 0000000..c5c3294 --- /dev/null +++ b/EasyTool.Core/ToolCategory/StringBuilderExtension.cs @@ -0,0 +1,417 @@ +using System; +using System.IO; +using System.Text; + +namespace EasyTool.Extension +{ + /// + /// StringBuilder 扩展方法 + /// + public static class StringBuilderExtension + { + #region 追加操作 + + /// + /// 追加带格式的字符串(忽略 null 值) + /// + public static StringBuilder AppendFormatIfNotNull(this StringBuilder sb, string format, object? arg) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (arg != null) + { + sb.AppendFormat(format, arg); + } + return sb; + } + + /// + /// 追加带格式的字符串(忽略空字符串) + /// + public static StringBuilder AppendFormatIfNotEmpty(this StringBuilder sb, string format, string? arg) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (!string.IsNullOrEmpty(arg)) + { + sb.AppendFormat(format, arg); + } + return sb; + } + + /// + /// 追加带格式的字符串(忽略空白字符串) + /// + public static StringBuilder AppendFormatIfNotBlank(this StringBuilder sb, string format, string? arg) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (!string.IsNullOrWhiteSpace(arg)) + { + sb.AppendFormat(format, arg); + } + return sb; + } + + /// + /// 条件追加字符串 + /// + public static StringBuilder AppendIf(this StringBuilder sb, string? value, bool condition) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (condition) + { + sb.Append(value); + } + return sb; + } + + /// + /// 条件追加字符串(带分隔符) + /// + public static StringBuilder AppendWithSeparator(this StringBuilder sb, string? value, string separator = ", ") + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (sb.Length > 0 && !string.IsNullOrEmpty(separator)) + { + sb.Append(separator); + } + sb.Append(value); + return sb; + } + + /// + /// 条件追加字符串(带分隔符,仅在非空时追加) + /// + public static StringBuilder AppendWithSeparatorIfNotEmpty(this StringBuilder sb, string? value, string separator = ", ") + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (!string.IsNullOrEmpty(value)) + { + sb.AppendWithSeparator(value, separator); + } + return sb; + } + + /// + /// 追加行(仅当值非空时) + /// + public static StringBuilder AppendLineIfNotEmpty(this StringBuilder sb, string? value) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (!string.IsNullOrEmpty(value)) + { + sb.AppendLine(value); + } + return sb; + } + + /// + /// 条件追加行 + /// + public static StringBuilder AppendLineIf(this StringBuilder sb, string? value, bool condition) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (condition) + { + sb.AppendLine(value); + } + return sb; + } + + #endregion + + #region 括号包裹 + + /// + /// 用括号包裹内容(如果非空) + /// + public static StringBuilder AppendInParentheses(this StringBuilder sb, string? value, string open = "(", string close = ")") + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (!string.IsNullOrEmpty(value)) + { + sb.Append(open).Append(value).Append(close); + } + return sb; + } + + /// + /// 用方括号包裹内容(如果非空) + /// + public static StringBuilder AppendInBrackets(this StringBuilder sb, string? value) + { + return sb.AppendInParentheses(value, "[", "]"); + } + + /// + /// 用花括号包裹内容(如果非空) + /// + public static StringBuilder AppendInBraces(this StringBuilder sb, string? value) + { + return sb.AppendInParentheses(value, "{", "}"); + } + + /// + /// 用引号包裹内容(如果非空) + /// + public static StringBuilder AppendInQuotes(this StringBuilder sb, string? value, string quote = "\"") + { + return sb.AppendInParentheses(value, quote, quote); + } + + #endregion + + #region 缩进操作 + + /// + /// 增加缩进 + /// + /// StringBuilder + /// 缩进字符串,默认为两个空格 + public static StringBuilder Indent(this StringBuilder sb, string indent = " ") + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + return sb.Append(indent); + } + + /// + /// 增加指定层数的缩进 + /// + public static StringBuilder Indent(this StringBuilder sb, int level, string indent = " ") + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + for (int i = 0; i < level; i++) + { + sb.Append(indent); + } + return sb; + } + + /// + /// 添加缩进行 + /// + public static StringBuilder AppendIndentedLine(this StringBuilder sb, string value, int level = 1, string indent = " ") + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + return sb.Indent(level, indent).AppendLine(value); + } + + #endregion + + #region 清除操作 + + /// + /// 清除最后 N 个字符 + /// + public static StringBuilder RemoveLast(this StringBuilder sb, int count) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (count > 0 && sb.Length >= count) + { + sb.Remove(sb.Length - count, count); + } + return sb; + } + + /// + /// 清除最后的一个字符 + /// + public static StringBuilder RemoveLastChar(this StringBuilder sb) + { + return sb.RemoveLast(1); + } + + /// + /// 清除最后指定的字符串(如果匹配) + /// + public static StringBuilder RemoveLastIf(this StringBuilder sb, string? value) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + if (!string.IsNullOrEmpty(value) && sb.Length >= value.Length) + { + int startIndex = sb.Length - value.Length; + string end = sb.ToString(startIndex, value.Length); + if (end == value) + { + sb.Remove(startIndex, value.Length); + } + } + return sb; + } + + /// + /// 清除最后的空白字符 + /// + public static StringBuilder TrimEnd(this StringBuilder sb) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + while (sb.Length > 0 && char.IsWhiteSpace(sb[sb.Length - 1])) + { + sb.Remove(sb.Length - 1, 1); + } + return sb; + } + + /// + /// 清除开头的空白字符 + /// + public static StringBuilder TrimStart(this StringBuilder sb) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + int index = 0; + while (index < sb.Length && char.IsWhiteSpace(sb[index])) + { + index++; + } + + if (index > 0) + { + sb.Remove(0, index); + } + return sb; + } + + /// + /// 清除开头和结尾的空白字符 + /// + public static StringBuilder Trim(this StringBuilder sb) + { + return sb.TrimStart().TrimEnd(); + } + + #endregion + + #region 转换操作 + + /// + /// 转换为只读字符串 + /// [Obsolete("请直接使用 sb?.ToString() ?? string.Empty")] + /// + [Obsolete("请直接使用 sb?.ToString() ?? string.Empty", false)] + public static string ToReadOnly(this StringBuilder sb) + { + return sb?.ToString() ?? string.Empty; + } + + /// + /// 转换为 MemoryStream + /// + public static MemoryStream ToMemoryStream(this StringBuilder sb, Encoding? encoding = null) + { + if (sb == null) + throw new ArgumentNullException(nameof(sb)); + + encoding ??= Encoding.UTF8; + var bytes = encoding.GetBytes(sb.ToString()); + return new MemoryStream(bytes); + } + + #endregion + + #region 检查操作 + + /// + /// 判断是否为空 + /// + public static bool IsNullOrEmpty(this StringBuilder? sb) + { + return sb == null || sb.Length == 0; + } + + /// + /// 判断是否包含指定字符串 + /// + public static bool Contains(this StringBuilder? sb, string? value) + { + if (sb == null) + return false; + + return sb.IndexOf(value) >= 0; + } + + /// + /// 查找字符串的位置 + /// + public static int IndexOf(this StringBuilder? sb, string? value, int startIndex = 0, bool ignoreCase = false) + { + if (sb == null || string.IsNullOrEmpty(value)) + return -1; + + if (startIndex < 0 || startIndex >= sb.Length) + return -1; + + var comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + + for (int i = startIndex; i <= sb.Length - value.Length; i++) + { + bool match = true; + for (int j = 0; j < value.Length; j++) + { + if (char.ToLower(sb[i + j]) != char.ToLower(value[j])) + { + match = false; + break; + } + } + + if (match) + return i; + } + + return -1; + } + + /// + /// 替换字符串 + /// + public static StringBuilder Replace(this StringBuilder? sb, string oldValue, string? newValue, bool ignoreCase = false) + { + if (sb == null || string.IsNullOrEmpty(oldValue)) + return sb ?? throw new ArgumentNullException(nameof(sb)); + + int index; + int searchIndex = 0; + + while ((index = sb.IndexOf(oldValue, searchIndex, ignoreCase)) >= 0) + { + sb.Remove(index, oldValue.Length); + sb.Insert(index, newValue); + searchIndex = index + (newValue?.Length ?? 0); + } + + return sb; + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/StringComparisonExtension.cs b/EasyTool.Core/ToolCategory/StringComparisonExtension.cs new file mode 100644 index 0000000..1304e6b --- /dev/null +++ b/EasyTool.Core/ToolCategory/StringComparisonExtension.cs @@ -0,0 +1,403 @@ +using System; +using System.Globalization; + +namespace EasyTool.Extension +{ + /// + /// String 字符串比较扩展方法 + /// + public static class StringComparisonExtension + { + #region 忽略大小写比较 + + /// + /// 忽略大小写判断字符串相等 + /// + public static bool EqualsIgnoreCase(this string? str, string? value) + { + return string.Equals(str, value, StringComparison.OrdinalIgnoreCase); + } + + /// + /// 忽略大小写判断字符串包含 + /// + public static bool ContainsIgnoreCase(this string? str, string? value) + { + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(value)) + return false; + + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0; + } + + /// + /// 忽略大小写判断字符串是否以指定字符串开头 + /// + public static bool StartsWithIgnoreCase(this string? str, string? value) + { + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(value)) + return false; + + return str.StartsWith(value, StringComparison.OrdinalIgnoreCase); + } + + /// + /// 忽略大小写判断字符串是否以指定字符串结尾 + /// + public static bool EndsWithIgnoreCase(this string? str, string? value) + { + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(value)) + return false; + + return str.EndsWith(value, StringComparison.OrdinalIgnoreCase); + } + + #endregion + + #region 模糊匹配 + + /// + /// 判断字符串是否包含指定字符(忽略大小写) + /// + public static bool ContainsCharIgnoreCase(this string? str, char value) + { + if (string.IsNullOrEmpty(str)) + return false; + + return str.IndexOf(char.ToLower(value), StringComparison.OrdinalIgnoreCase) >= 0; + } + + /// + /// 判断字符串是否包含任意指定字符串 + /// + public static bool ContainsAny(this string? str, params string?[] values) + { + if (string.IsNullOrEmpty(str) || values == null || values.Length == 0) + return false; + + foreach (var value in values) + { + if (str.Contains(value)) + return true; + } + + return false; + } + + /// + /// 忽略大小写判断字符串是否包含任意指定字符串 + /// + public static bool ContainsAnyIgnoreCase(this string? str, params string?[] values) + { + if (string.IsNullOrEmpty(str) || values == null || values.Length == 0) + return false; + + foreach (var value in values) + { + if (str.ContainsIgnoreCase(value)) + return true; + } + + return false; + } + + /// + /// 判断字符串是否包含所有指定字符串 + /// + public static bool ContainsAll(this string? str, params string?[] values) + { + if (string.IsNullOrEmpty(str) || values == null || values.Length == 0) + return false; + + foreach (var value in values) + { + if (!str.Contains(value)) + return false; + } + + return true; + } + + /// + /// 忽略大小写判断字符串是否包含所有指定字符串 + /// + public static bool ContainsAllIgnoreCase(this string? str, params string?[] values) + { + if (string.IsNullOrEmpty(str) || values == null || values.Length == 0) + return false; + + foreach (var value in values) + { + if (!str.ContainsIgnoreCase(value)) + return false; + } + + return true; + } + + #endregion + + #region 通配符匹配 + + /// + /// 使用通配符匹配字符串 + /// + public static bool Like(this string? str, string? pattern, bool ignoreCase = true) + { + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(pattern)) + return false; + + var comparisonType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + + // 转换通配符模式为正则表达式 + var regexPattern = "^" + System.Text.RegularExpressions.Regex.Escape(pattern) + .Replace("\\*", ".*") + .Replace("\\?", ".") + "$"; + + var regex = new System.Text.RegularExpressions.Regex( + regexPattern, + ignoreCase ? System.Text.RegularExpressions.RegexOptions.IgnoreCase : System.Text.RegularExpressions.RegexOptions.None); + + return regex.IsMatch(str); + } + + #endregion + + #region 前缀/后缀检查 + + /// + /// 判断字符串是否以任意指定前缀开头 + /// + public static bool StartsWithAny(this string? str, params string?[] prefixes) + { + if (string.IsNullOrEmpty(str) || prefixes == null || prefixes.Length == 0) + return false; + + foreach (var prefix in prefixes) + { + if (!string.IsNullOrEmpty(prefix) && str.StartsWith(prefix)) + return true; + } + + return false; + } + + /// + /// 忽略大小写判断字符串是否以任意指定前缀开头 + /// + public static bool StartsWithAnyIgnoreCase(this string? str, params string?[] prefixes) + { + if (string.IsNullOrEmpty(str) || prefixes == null || prefixes.Length == 0) + return false; + + foreach (var prefix in prefixes) + { + if (!string.IsNullOrEmpty(prefix) && str.StartsWithIgnoreCase(prefix)) + return true; + } + + return false; + } + + /// + /// 判断字符串是否以任意指定后缀结尾 + /// + public static bool EndsWithAny(this string? str, params string?[] suffixes) + { + if (string.IsNullOrEmpty(str) || suffixes == null || suffixes.Length == 0) + return false; + + foreach (var suffix in suffixes) + { + if (!string.IsNullOrEmpty(suffix) && str.EndsWith(suffix)) + return true; + } + + return false; + } + + /// + /// 忽略大小写判断字符串是否以任意指定后缀结尾 + /// + public static bool EndsWithAnyIgnoreCase(this string? str, params string?[] suffixes) + { + if (string.IsNullOrEmpty(str) || suffixes == null || suffixes.Length == 0) + return false; + + foreach (var suffix in suffixes) + { + if (!string.IsNullOrEmpty(suffix) && str.EndsWithIgnoreCase(suffix)) + return true; + } + + return false; + } + + #endregion + + #region 字符串相似度 + + /// + /// 计算字符串相似度(0-1之间,1表示完全相同) + /// 使用 Levenshtein 距离算法 + /// + public static double Similarity(this string? str, string? value) + { + if (string.IsNullOrEmpty(str) && string.IsNullOrEmpty(value)) + return 1; + + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(value)) + return 0; + + int distance = LevenshteinDistance(str, value); + int maxLength = Math.Max(str.Length, value.Length); + + return 1 - (double)distance / maxLength; + } + + /// + /// 计算 Levenshtein 距离 + /// + private static int LevenshteinDistance(string str1, string str2) + { + int[,] distance = new int[str1.Length + 1, str2.Length + 1]; + + for (int i = 0; i <= str1.Length; i++) + distance[i, 0] = i; + + for (int j = 0; j <= str2.Length; j++) + distance[0, j] = j; + + for (int i = 1; i <= str1.Length; i++) + { + for (int j = 1; j <= str2.Length; j++) + { + int cost = str1[i - 1] == str2[j - 1] ? 0 : 1; + + distance[i, j] = Math.Min( + Math.Min(distance[i - 1, j] + 1, distance[i, j - 1] + 1), + distance[i - 1, j - 1] + cost); + } + } + + return distance[str1.Length, str2.Length]; + } + + /// + /// 判断字符串相似度是否超过指定阈值 + /// + public static bool IsSimilarTo(this string? str, string? value, double threshold = 0.8) + { + return str.Similarity(value) >= threshold; + } + + #endregion + + #region 字符串比较 + + /// + /// 比较字符串(忽略大小写) + /// + public static int CompareToIgnoreCase(this string? str, string? value) + { + return string.Compare(str, value, StringComparison.OrdinalIgnoreCase); + } + + /// + /// 比较字符串(使用指定的文化信息) + /// + public static int CompareToCulture(this string? str, string? value, CultureInfo culture, bool ignoreCase = false) + { + return string.Compare(str, value, culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None); + } + + #endregion + + #region 首字母大小写 + + /// + /// 首字母大写 + /// + public static string ToTitleCase(this string str) + { + if (string.IsNullOrEmpty(str)) + return str; + + return char.ToUpper(str[0]) + str.Substring(1); + } + + /// + /// 首字母小写 + /// + public static string ToCamelCase(this string str) + { + if (string.IsNullOrEmpty(str)) + return str; + + return char.ToLower(str[0]) + str.Substring(1); + } + + /// + /// 将字符串转换为标题格式(每个单词首字母大写) + /// + public static string ToTitleCase(this string str, CultureInfo? culture = null) + { + if (string.IsNullOrEmpty(str)) + return str; + + culture ??= CultureInfo.CurrentCulture; + return culture.TextInfo.ToTitleCase(str); + } + + #endregion + + #region 大小写转换 + + /// + /// 转换为大写(不变则为返回原字符串) + /// [Obsolete("请直接使用 value.ToUpper()")] + /// + [Obsolete("请直接使用 value.ToUpper()", false)] + public static string ToUpperSafe(this string str) + { + if (string.IsNullOrEmpty(str)) + return str; + + return str.ToUpper(); + } + + /// + /// 转换为小写(不变则为返回原字符串) + /// [Obsolete("请直接使用 value.ToLower()")] + /// + [Obsolete("请直接使用 value.ToLower()", false)] + public static string ToLowerSafe(this string str) + { + if (string.IsNullOrEmpty(str)) + return str; + + return str.ToLower(); + } + + /// + /// 转换为单词首字母大写(如:helloWorld -> HelloWorld) + /// + public static string ToPascalCase(this string str) + { + if (string.IsNullOrEmpty(str)) + return str; + + var words = str.Split(new[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < words.Length; i++) + { + if (words[i].Length > 0) + { + words[i] = char.ToUpper(words[i][0]) + words[i].Substring(1).ToLower(); + } + } + + return string.Join("", words); + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/TaskExtension.cs b/EasyTool.Core/ToolCategory/TaskExtension.cs new file mode 100644 index 0000000..c3e0312 --- /dev/null +++ b/EasyTool.Core/ToolCategory/TaskExtension.cs @@ -0,0 +1,381 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace EasyTool.Extension +{ + /// + /// Task 异步任务扩展方法 + /// + public static class TaskExtension + { + #region 任务忽略 + + /// + /// 忽略任务(Fire-and-forget),捕获并记录异常但不抛出 + /// + /// 要忽略的任务 + /// 异常回调(可选) + public static void Forget(this Task? task, Action? onException = null) + { + task?.ContinueWith(t => + { + if (t.IsFaulted && t.Exception != null) + { + onException?.Invoke(t.Exception); + } + }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); + } + + /// + /// 忽略任务(Fire-and-forget),捕获并记录异常但不抛出 + /// + /// 任务返回类型 + /// 要忽略的任务 + /// 异常回调(可选) + public static void Forget(this Task? task, Action? onException = null) + { + task?.ContinueWith(t => + { + if (t.IsFaulted && t.Exception != null) + { + onException?.Invoke(t.Exception); + } + }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); + } + + #endregion + + #region 超时处理 + + /// + /// 为任务添加超时处理 + /// + /// 原始任务 + /// 超时时间 + public static async Task OrTimeout(this Task task, TimeSpan timeout) + { + var delayTask = Task.Delay(timeout); + + var completedTask = await Task.WhenAny(task, delayTask); + + if (completedTask == delayTask) + throw new TimeoutException($"操作超时({timeout.TotalSeconds}秒)"); + + return await task; + } + + /// + /// 为任务添加超时处理 + /// + /// 原始任务 + /// 超时时间 + public static async Task OrTimeout(this Task task, TimeSpan timeout) + { + var delayTask = Task.Delay(timeout); + + var completedTask = await Task.WhenAny(task, delayTask); + + if (completedTask == delayTask) + throw new TimeoutException($"操作超时({timeout.TotalSeconds}秒)"); + + await task; + } + + /// + /// 为任务添加超时处理,超时返回默认值 + /// + /// 原始任务 + /// 超时时间 + /// 默认值 + public static async Task OrTimeoutOrDefault(this Task task, TimeSpan timeout, T? defaultValue = default) + { + var delayTask = Task.Delay(timeout); + + var completedTask = await Task.WhenAny(task, delayTask); + + if (completedTask == delayTask) + return defaultValue; + + return await task; + } + + #endregion + + #region 重试机制 + + /// + /// 任务失败时自动重试 + /// + /// 任务工厂函数 + /// 重试次数 + /// 重试延迟时间 + public static async Task Retry(Func> taskFactory, int retryCount = 3, TimeSpan? delay = null) + { + if (taskFactory == null) + throw new ArgumentNullException(nameof(taskFactory)); + + Exception? lastException = null; + + for (int i = 0; i <= retryCount; i++) + { + try + { + return await taskFactory(); + } + catch (Exception ex) + { + lastException = ex; + + if (i < retryCount && delay.HasValue) + await Task.Delay(delay.Value); + } + } + + throw lastException ?? new Exception("Retry failed"); + } + + /// + /// 任务失败时自动重试 + /// + /// 任务工厂函数 + /// 重试次数 + /// 重试延迟时间 + public static async Task Retry(Func taskFactory, int retryCount = 3, TimeSpan? delay = null) + { + if (taskFactory == null) + throw new ArgumentNullException(nameof(taskFactory)); + + Exception? lastException = null; + + for (int i = 0; i <= retryCount; i++) + { + try + { + await taskFactory(); + return; + } + catch (Exception ex) + { + lastException = ex; + + if (i < retryCount && delay.HasValue) + await Task.Delay(delay.Value); + } + } + + throw lastException ?? new Exception("Retry failed"); + } + + /// + /// 任务失败时自动重试(带条件判断) + /// + /// 任务工厂函数 + /// 重试条件函数 + /// 重试次数 + /// 重试延迟时间 + public static async Task Retry(Func> taskFactory, Func shouldRetry, int retryCount = 3, TimeSpan? delay = null) + { + if (taskFactory == null) + throw new ArgumentNullException(nameof(taskFactory)); + if (shouldRetry == null) + throw new ArgumentNullException(nameof(shouldRetry)); + + Exception? lastException = null; + + for (int i = 0; i <= retryCount; i++) + { + try + { + return await taskFactory(); + } + catch (Exception ex) + { + lastException = ex; + + if (i < retryCount && shouldRetry(ex)) + { + if (delay.HasValue) + await Task.Delay(delay.Value); + } + else + { + break; + } + } + } + + throw lastException ?? new Exception("Retry failed"); + } + + #endregion + + #region 任务组合 + + /// + /// 在所有任务完成时返回,无论成功或失败 + /// + public static async Task WhenAllOrAnyFailed(this IEnumerable tasks) + { + if (tasks == null) + throw new ArgumentNullException(nameof(tasks)); + + var taskArray = tasks.ToArray(); + if (taskArray.Length == 0) + return taskArray; + + var tcs = new TaskCompletionSource(); + + int remaining = taskArray.Length; + var results = new Task[taskArray.Length]; + + for (int i = 0; i < taskArray.Length; i++) + { + int index = i; + taskArray[i].ContinueWith(t => + { + results[index] = t; + if (Interlocked.Decrement(ref remaining) == 0) + { + tcs.TrySetResult(results); + } + }, TaskContinuationOptions.ExecuteSynchronously); + } + + return await tcs.Task; + } + + /// + /// 返回第一个完成的任务(无论成功或失败) + /// + public static async Task WhenAnyFirstOrDefault(this IEnumerable tasks) + { + if (tasks == null) + throw new ArgumentNullException(nameof(tasks)); + + var taskArray = tasks.ToArray(); + if (taskArray.Length == 0) + throw new ArgumentException("至少需要一个任务", nameof(tasks)); + + return await Task.WhenAny(taskArray); + } + + #endregion + + #region 任务超时与取消组合 + + /// + /// 创建一个带超时和取消令牌的任务 + /// + public static async Task WithTimeoutAndCancellation(this Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + var timeoutCts = new CancellationTokenSource(timeout); + var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + + try + { + return await task; + } + catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested) + { + throw new TimeoutException($"操作超时({timeout.TotalSeconds}秒)"); + } + finally + { + timeoutCts.Dispose(); + combinedCts.Dispose(); + } + } + + /// + /// 创建一个带超时和取消令牌的任务 + /// + public static async Task WithTimeoutAndCancellation(this Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + var timeoutCts = new CancellationTokenSource(timeout); + var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + + try + { + await task; + } + catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested) + { + throw new TimeoutException($"操作超时({timeout.TotalSeconds}秒)"); + } + finally + { + timeoutCts.Dispose(); + combinedCts.Dispose(); + } + } + + #endregion + + #region 任务结果处理 + + /// + /// 处理任务结果,无论成功或失败 + /// + public static async Task Finally(this Task task, Func onSuccess, Func onFailure) + { + try + { + var result = await task; + return onSuccess(result); + } + catch (Exception ex) + { + return onFailure(ex); + } + } + + /// + /// 处理任务结果,无论成功或失败 + /// + public static async Task Finally(this Task task, Action onSuccess, Action onFailure) + { + try + { + await task; + onSuccess(); + } + catch (Exception ex) + { + onFailure(ex); + } + } + + #endregion + + #region 任务延迟执行 + + /// + /// 延迟执行任务 + /// + public static async Task Delayed(this Func> taskFactory, TimeSpan delay) + { + if (taskFactory == null) + throw new ArgumentNullException(nameof(taskFactory)); + + await Task.Delay(delay); + return await taskFactory(); + } + + /// + /// 延迟执行任务 + /// + public static async Task Delayed(this Func taskFactory, TimeSpan delay) + { + if (taskFactory == null) + throw new ArgumentNullException(nameof(taskFactory)); + + await Task.Delay(delay); + await taskFactory(); + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/TypeExtension.cs b/EasyTool.Core/ToolCategory/TypeExtension.cs new file mode 100644 index 0000000..5e35baa --- /dev/null +++ b/EasyTool.Core/ToolCategory/TypeExtension.cs @@ -0,0 +1,432 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace EasyTool.Extension +{ + /// + /// Type 类型扩展方法 + /// + public static class TypeExtension + { + #region 类型判断 + + /// + /// 判断是否是简单类型(值类型、字符串等) + /// + public static bool IsSimpleType(this Type? type) + { + if (type == null) + return false; + + return type.IsValueType || + type == typeof(string) || + type == typeof(decimal) || + type == typeof(DateTime) || + type == typeof(DateTimeOffset) || + type == typeof(TimeSpan) || + type == typeof(Guid); + } + + /// + /// 判断是否是数字类型 + /// + public static bool IsNumericType(this Type? type) + { + if (type == null) + return false; + + type = Nullable.GetUnderlyingType(type) ?? type; + + return type == typeof(byte) || + type == typeof(sbyte) || + type == typeof(short) || + type == typeof(ushort) || + type == typeof(int) || + type == typeof(uint) || + type == typeof(long) || + type == typeof(ulong) || + type == typeof(float) || + type == typeof(double) || + type == typeof(decimal); + } + + /// + /// 判断是否是集合类型 + /// + public static bool IsCollectionType(this Type? type) + { + if (type == null) + return false; + + return type != typeof(string) && typeof(System.Collections.IEnumerable).IsAssignableFrom(type); + } + + /// + /// 判断是否是字典类型 + /// + public static bool IsDictionaryType(this Type? type) + { + if (type == null) + return false; + + return typeof(System.Collections.IDictionary).IsAssignableFrom(type) || + (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>)); + } + + /// + /// 判断是否是可空值类型 + /// + public static bool IsNullableType(this Type? type) + { + if (type == null) + return false; + + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + /// + /// 判断是否是泛型类型 + /// + public static bool IsGenericType(this Type? type, Type genericTypeDefinition) + { + if (type == null || !type.IsGenericType) + return false; + + return type.GetGenericTypeDefinition() == genericTypeDefinition; + } + + /// + /// 判断是否是某个泛型类型的子类 + /// + public static bool IsSubclassOfGeneric(this Type? type, Type genericTypeDefinition) + { + if (type == null || genericTypeDefinition == null) + return false; + + while (type != null && type != typeof(object)) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == genericTypeDefinition) + return true; + + type = type.BaseType; + } + + return false; + } + + /// + /// 判断是否是匿名类型 + /// + public static bool IsAnonymousType(this Type? type) + { + if (type == null) + return false; + + return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) && + type.IsGenericType && + type.Name.Contains("AnonymousType") && + (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")); + } + + #endregion + + #region 类型名称 + + /// + /// 获取友好的类型名称 + /// + public static string GetFriendlyName(this Type? type) + { + if (type == null) + return "null"; + + if (type == typeof(int)) + return "int"; + if (type == typeof(uint)) + return "uint"; + if (type == typeof(long)) + return "long"; + if (type == typeof(ulong)) + return "ulong"; + if (type == typeof(short)) + return "short"; + if (type == typeof(ushort)) + return "ushort"; + if (type == typeof(byte)) + return "byte"; + if (type == typeof(sbyte)) + return "sbyte"; + if (type == typeof(float)) + return "float"; + if (type == typeof(double)) + return "double"; + if (type == typeof(decimal)) + return "decimal"; + if (type == typeof(bool)) + return "bool"; + if (type == typeof(string)) + return "string"; + if (type == typeof(char)) + return "char"; + if (type == typeof(object)) + return "object"; + if (type == typeof(void)) + return "void"; + + if (type.IsGenericType) + { + var genericArgs = type.GetGenericArguments(); + var genericTypeName = type.GetGenericTypeDefinition().GetFriendlyName(); + var genericArgsNames = string.Join(", ", genericArgs.Select(t => t.GetFriendlyName())); + return genericTypeName + "<" + genericArgsNames + ">"; + } + + if (type.IsArray) + { + var elementType = type.GetElementType(); + return $"{elementType.GetFriendlyName()}[]"; + } + + return type.Name; + } + + /// + /// 获取类型的显示名称 + /// + public static string GetDisplayName(this Type? type) + { + if (type == null) + return string.Empty; + + var attr = type.GetCustomAttribute(); + return attr?.DisplayName ?? type.Name; + } + + /// + /// 获取类型的描述 + /// + public static string GetDescription(this Type? type) + { + if (type == null) + return string.Empty; + + var attr = type.GetCustomAttribute(); + return attr?.Description ?? string.Empty; + } + + #endregion + + #region 默认值 + + /// + /// 获取类型的默认值 + /// + public static object? GetDefaultValue(this Type? type) + { + if (type == null) + return null; + + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + + #endregion + + #region 泛型处理 + + /// + /// 获取可空类型的实际类型 + /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type")] + /// + [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type", false)] + public static Type? GetNullableType(this Type? type) + { + if (type == null) + return null; + + return Nullable.GetUnderlyingType(type) ?? type; + } + + /// + /// 获取集合的元素类型 + /// + public static Type? GetElementType(this Type? type) + { + if (type == null) + return null; + + if (type.IsArray) + return type.GetElementType(); + + if (type.IsCollectionType()) + { + var genericArgs = type.GetGenericArguments(); + if (genericArgs.Length > 0) + return genericArgs[0]; + } + + return null; + } + + /// + /// 获取字典的键值对类型 + /// + public static (Type? KeyType, Type? ValueType) GetDictionaryKeyValueTypes(this Type? type) + { + if (type == null) + return (null, null); + + if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) + { + var interfaces = type.GetInterfaces(); + var dictInterface = interfaces.FirstOrDefault(i => + i.IsGenericType && i.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>)); + + if (dictInterface != null) + { + var genericArgs = dictInterface.GetGenericArguments(); + return (genericArgs[0], genericArgs[1]); + } + } + + return (null, null); + } + + #endregion + + #region 特性操作 + + /// + /// 判断是否有指定特性 + /// + public static bool HasAttribute(this Type? type) where T : Attribute + { + if (type == null) + return false; + + return type.GetCustomAttribute() != null; + } + + /// + /// 判断是否有指定特性 + /// + public static bool HasAttribute(this Type? type, Type? attributeType) + { + if (type == null || attributeType == null) + return false; + + return type.GetCustomAttributes(attributeType, false).Any(); + } + + /// + /// 获取指定特性 + /// + public static T? GetAttribute(this Type? type) where T : Attribute + { + if (type == null) + return null; + + return type.GetCustomAttribute(); + } + + /// + /// 获取所有指定特性 + /// + public static T[] GetAttributes(this Type? type) where T : Attribute + { + if (type == null) + return Array.Empty(); + + return type.GetCustomAttributes().ToArray(); + } + + #endregion + + #region 成员获取 + + /// + /// 获取所有属性(包含继承的) + /// + public static PropertyInfo[] GetAllProperties(this Type? type) + { + if (type == null) + return Array.Empty(); + + var properties = new List(); + var currentType = type; + + while (currentType != null && currentType != typeof(object)) + { + var currentProps = currentType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); + properties.AddRange(currentProps); + currentType = currentType.BaseType; + } + + return properties.ToArray(); + } + + /// + /// 获取所有字段(包含继承的) + /// + public static FieldInfo[] GetAllFields(this Type? type) + { + if (type == null) + return Array.Empty(); + + var fields = new List(); + var currentType = type; + + while (currentType != null && currentType != typeof(object)) + { + var currentFields = currentType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + fields.AddRange(currentFields); + currentType = currentType.BaseType; + } + + return fields.ToArray(); + } + + #endregion + + #region 类型转换 + + /// + /// 尝试将值转换为指定类型 + /// + public static object? ChangeType(this Type type, object? value) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (value == null) + { + if (type.IsValueType) + return Activator.CreateInstance(type); + return null; + } + + var valueType = value.GetType(); + + // 如果类型相同或目标类型是源类型的父类 + if (type.IsAssignableFrom(valueType)) + return value; + + // 可空类型处理 + var underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType != null) + return ChangeType(underlyingType, value); + + // 枚举处理 + if (type.IsEnum && value is string str) + return Enum.Parse(type, str, true); + + // 标准转换 + return Convert.ChangeType(value, type); + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/TypeUtil.cs b/EasyTool.Core/ToolCategory/TypeUtil.cs index a74ff6a..6fdd218 100644 --- a/EasyTool.Core/ToolCategory/TypeUtil.cs +++ b/EasyTool.Core/ToolCategory/TypeUtil.cs @@ -13,9 +13,11 @@ public class TypeUtil { /// /// 判断类型是否是可空类型 + /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(typeof(T)) != null")] /// /// 要判断的类型 /// 是否是可空类型 + [Obsolete("请直接使用 Nullable.GetUnderlyingType(typeof(T)) != null", false)] public static bool IsNullable() where T : struct { return Nullable.GetUnderlyingType(typeof(T)) != null; @@ -23,9 +25,11 @@ public static bool IsNullable() where T : struct /// /// 判断类型是否是枚举类型 + /// [Obsolete("请直接使用 typeof(T).IsEnum")] /// /// 要判断的类型 /// 是否是枚举类型 + [Obsolete("请直接使用 typeof(T).IsEnum", false)] public static bool IsEnum() { return typeof(T).IsEnum; @@ -33,9 +37,11 @@ public static bool IsEnum() /// /// 获取泛型类型的参数类型 + /// [Obsolete("请直接使用 typeof(T).GetGenericArguments()")] /// /// 要获取参数类型的泛型类型 /// 泛型类型的参数类型数组 + [Obsolete("请直接使用 typeof(T).GetGenericArguments()", false)] public static Type[] GetGenericArguments() { return typeof(T).GetGenericArguments(); @@ -43,9 +49,11 @@ public static Type[] GetGenericArguments() /// /// 获取类型的所有属性 + /// [Obsolete("请直接使用 typeof(T).GetProperties()")] /// /// 要获取属性的类型 /// 属性数组 + [Obsolete("请直接使用 typeof(T).GetProperties()", false)] public static PropertyInfo[] GetProperties() { return typeof(T).GetProperties(); @@ -53,9 +61,11 @@ public static PropertyInfo[] GetProperties() /// /// 获取类型的所有字段 + /// [Obsolete("请直接使用 typeof(T).GetFields()")] /// /// 要获取字段的类型 /// 字段数组 + [Obsolete("请直接使用 typeof(T).GetFields()", false)] public static FieldInfo[] GetFields() { return typeof(T).GetFields(); @@ -63,9 +73,11 @@ public static FieldInfo[] GetFields() /// /// 获取类型的所有方法 + /// [Obsolete("请直接使用 typeof(T).GetMethods()")] /// /// 要获取方法的类型 /// 方法数组 + [Obsolete("请直接使用 typeof(T).GetMethods()", false)] public static MethodInfo[] GetMethods() { return typeof(T).GetMethods(); @@ -73,9 +85,11 @@ public static MethodInfo[] GetMethods() /// /// 获取类型的所有事件 + /// [Obsolete("请直接使用 typeof(T).GetEvents()")] /// /// 要获取事件的类型 /// 事件数组 + [Obsolete("请直接使用 typeof(T).GetEvents()", false)] public static EventInfo[] GetEvents() { return typeof(T).GetEvents(); @@ -83,9 +97,11 @@ public static EventInfo[] GetEvents() /// /// 获取类型的所有属性、字段、方法和事件 + /// [Obsolete("请直接使用 typeof(T).GetMembers()")] /// /// 要获取成员的类型 /// 成员数组 + [Obsolete("请直接使用 typeof(T).GetMembers()", false)] public static MemberInfo[] GetMembers() { return typeof(T).GetMembers(); @@ -93,9 +109,11 @@ public static MemberInfo[] GetMembers() /// /// 获取类型的所有构造函数 + /// [Obsolete("请直接使用 typeof(T).GetConstructors()")] /// /// 要获取构造函数的类型 /// 构造函数数组 + [Obsolete("请直接使用 typeof(T).GetConstructors()", false)] public static ConstructorInfo[] GetConstructors() { return typeof(T).GetConstructors(); @@ -114,10 +132,12 @@ public static bool ImplementsInterface() /// /// 判断类型是否继承了指定的基类 + /// [Obsolete("请直接使用 typeof(T).IsSubclassOf(typeof(TBase))")] /// /// 要判断的类型 /// 要判断的基类类型 /// 是否继承了指定的基类 + [Obsolete("请直接使用 typeof(T).IsSubclassOf(typeof(TBase))", false)] public static bool InheritsFrom() { return typeof(T).IsSubclassOf(typeof(TBase)); @@ -125,9 +145,11 @@ public static bool InheritsFrom() /// /// 创建指定类型的实例 + /// [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T))")] /// /// 要创建实例的类型 /// 类型的实例 + [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T))", false)] public static T CreateInstance() { return (T)Activator.CreateInstance(typeof(T)); @@ -135,10 +157,12 @@ public static T CreateInstance() /// /// 创建指定类型的实例 + /// [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T), args)")] /// /// 要创建实例的类型 /// 构造函数的参数 /// 类型的实例 + [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T), args)", false)] public static T CreateInstance(params object[] args) { return (T)Activator.CreateInstance(typeof(T), args); @@ -161,10 +185,12 @@ public static IEnumerable GetEnumValues() /// /// 将字符串转换为指定类型的值 + /// [Obsolete("请直接使用 (T)Convert.ChangeType(value, typeof(T))")] /// /// 要转换的类型 /// 要转换的字符串 /// 转换后的值 + [Obsolete("请直接使用 (T)Convert.ChangeType(value, typeof(T))", false)] public static T ConvertFromString(string value) { return (T)Convert.ChangeType(value, typeof(T)); @@ -172,10 +198,12 @@ public static T ConvertFromString(string value) /// /// 将值转换为指定类型的字符串 + /// [Obsolete("请直接使用 Convert.ToString(value)")] /// /// 要转换的类型 /// 要转换的值 /// 转换后的字符串 + [Obsolete("请直接使用 Convert.ToString(value)", false)] public static string ConvertToString(T value) { return Convert.ToString(value); diff --git a/EasyTool.Core/ToolCategory/URLUtil.cs b/EasyTool.Core/ToolCategory/URLUtil.cs index d281985..82edd17 100644 --- a/EasyTool.Core/ToolCategory/URLUtil.cs +++ b/EasyTool.Core/ToolCategory/URLUtil.cs @@ -140,9 +140,11 @@ public static string UrlDecodeQuery(string value) /// /// 从URL中提取域名。 + /// [Obsolete("请直接使用 new Uri(url).Host")] /// /// 要提取域名的URL。 /// URL中的域名。 + [Obsolete("请直接使用 new Uri(url).Host", false)] public static string ExtractDomain(string url) { var uri = new Uri(url); @@ -151,9 +153,11 @@ public static string ExtractDomain(string url) /// /// 从URL中提取路径。 + /// [Obsolete("请直接使用 new Uri(url).AbsolutePath")] /// /// 要提取路径的URL。 /// URL中的路径。 + [Obsolete("请直接使用 new Uri(url).AbsolutePath", false)] public static string ExtractPath(string url) { var uri = new Uri(url); @@ -173,9 +177,11 @@ public static bool IsHttps(string url) /// /// 从URL中提取查询字符串。 + /// [Obsolete("请直接使用 new Uri(url).Query")] /// /// 要提取查询字符串的URL。 /// URL中的查询字符串。 + [Obsolete("请直接使用 new Uri(url).Query", false)] public static string ExtractQueryString(string url) { var uri = new Uri(url); @@ -184,9 +190,11 @@ public static string ExtractQueryString(string url) /// /// 从URL中提取片段。 + /// [Obsolete("请直接使用 new Uri(url).Fragment")] /// /// 要提取片段的URL。 /// URL中的片段。 + [Obsolete("请直接使用 new Uri(url).Fragment", false)] public static string ExtractFragment(string url) { var uri = new Uri(url); diff --git a/EasyTool.Web/DevelopmentCategory/BuildDtoToTS.cs b/EasyTool.Web/DevelopmentCategory/BuildDtoToTS.cs index f259e81..4b2d623 100644 --- a/EasyTool.Web/DevelopmentCategory/BuildDtoToTS.cs +++ b/EasyTool.Web/DevelopmentCategory/BuildDtoToTS.cs @@ -152,14 +152,27 @@ public static List GetDtos(Assembly assembly) foreach (var propertyType in propertyTypes) { var property = new DtoProperty(propertyType.PropertyType, propertyType.Name); - property.Title = propertyType.GetCustomAttribute()?.DisplayName ?? ""; - property.IsInverseProperty = propertyType.GetCustomAttribute() != null; + // 优先使用 DisplayAttribute 或 DescriptionAttribute,然后是 DisplayNameAttribute + property.Title = propertyType.GetCustomAttribute()?.GetName() + ?? propertyType.GetCustomAttribute()?.Description + ?? propertyType.GetCustomAttribute()?.DisplayName + ?? ""; + + property.IsInverseProperty = propertyType.GetCustomAttribute() != null; property.IsKey = propertyType.GetCustomAttribute() != null; property.IsRequired = propertyType.GetCustomAttribute() != null; property.StringLength = propertyType.GetCustomAttribute()?.MaximumLength ?? 0; - dto.Propertys.Add(property); + // 支持更多 .NET 约定特性 + property.IsEditable = propertyType.GetCustomAttribute() == null; + property.DataType = GetDataType(propertyType.GetCustomAttribute()); + property.RegularExpression = propertyType.GetCustomAttribute()?.Pattern; + property.RangeMinimum = propertyType.GetCustomAttribute()?.Minimum as double?; + property.RangeMaximum = propertyType.GetCustomAttribute()?.Maximum as double?; + property.IsForeignKey = propertyType.GetCustomAttribute() != null; + + dto.Propertys.Add(property); } dtos.Add(dto); @@ -188,7 +201,9 @@ public DtoClass(string name, string _namespace) } - //TODO:需要改造成使用.net约定的属性 + /// + /// DTO 属性信息,支持标准 .NET DataAnnotations 特性 + /// public class DtoProperty { public DtoProperty(Type type, string name) @@ -196,18 +211,79 @@ public DtoProperty(Type type, string name) Type = type; Name = name; } - public Type Type { get; set; } - public string Name { get; set; } - - public string Title { get; set; }//字段名称 - public bool IsInverseProperty { get; set; }//是否关联属性 + /// + /// 属性类型 + /// + public Type Type { get; set; } - public bool IsRequired { get; set; }//是否必填 + /// + /// 属性名称 + /// + public string Name { get; set; } - public int StringLength { get; set; }//字符串长度 + /// + /// 显示名称(支持 DisplayAttribute、DescriptionAttribute、DisplayNameAttribute) + /// + public string Title { get; set; } + + /// + /// 是否关联属性(InversePropertyAttribute) + /// + public bool IsInverseProperty { get; set; } + + /// + /// 是否必填(RequiredAttribute) + /// + public bool IsRequired { get; set; } + + /// + /// 字符串长度(StringLengthAttribute) + /// + public int StringLength { get; set; } + + /// + /// 是否主键(KeyAttribute) + /// + public bool IsKey { get; set; } + + /// + /// 是否可编辑(EditableAttribute) + /// + public bool IsEditable { get; set; } = true; + + /// + /// 数据类型(DataTypeAttribute) + /// + public string DataType { get; set; } + + /// + /// 正则表达式验证(RegularExpressionAttribute) + /// + public string RegularExpression { get; set; } + + /// + /// 范围最小值(RangeAttribute) + /// + public double? RangeMinimum { get; set; } + + /// + /// 范围最大值(RangeAttribute) + /// + public double? RangeMaximum { get; set; } + + /// + /// 是否外键(ForeignKeyAttribute) + /// + public bool IsForeignKey { get; set; } + } - public bool IsKey { get; set; }//是否主键 + /// + /// 获取 DataTypeAttribute 的数据类型名称 + /// + private static string GetDataType(DataTypeAttribute attribute) + { + return attribute?.DataType.ToString() ?? string.Empty; } } From a5d0d9f5dba2a2038aeeb8e618c32ea8cc2bd401 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 15:33:02 +0800 Subject: [PATCH 03/14] =?UTF-8?q?refactor:=20=E6=95=B4=E5=90=88=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=BA=93=E5=B9=B6=E4=BC=98=E5=8C=96=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 28 个冗余工具类(IoUtil, ObjectUtil, ArrayUtil 等) - 合并功能到对应扩展方法类(ObjectExtension, ArrayExtension 等) - 优化 StrUtil 和 PageUtil 中的字符串拼接,改用 StringBuilder - 移动 HashUtil, HexUtil, RandomUtil, RegexUtil, URLUtil 到正确类别 - 标记 DateTimeExtension 中 11 个包装方法为 Obsolete - 删除 CloneCategory 目录,功能已整合到 ObjectExtension 减少约 4000+ 行冗余代码,提升整体代码质量和性能 --- EasyTool.Core/CloneCategory/CloneExtension.cs | 12 - EasyTool.Core/CloneCategory/CloneUtil.cs | 84 -- EasyTool.Core/CodeCategory/Base32Util.cs | 163 --- EasyTool.Core/CodeCategory/Base62Util.cs | 73 - EasyTool.Core/CodeCategory/Base64Util.cs | 141 -- EasyTool.Core/CodeCategory/EncodingUtil.cs | 377 ++++++ .../HashUtil.cs | 24 +- .../{ToolCategory => CodeCategory}/HexUtil.cs | 17 +- EasyTool.Core/CodeCategory/MorseUtil.cs | 107 -- EasyTool.Core/CodeCategory/PunycodeUtil.cs | 254 ---- EasyTool.Core/CodeCategory/RotUtil.cs | 51 - .../CollectionsCategory/ArrayExtension.cs | 203 ++- .../DictionaryExtension.cs | 23 - .../CollectionsCategory/IteratorUtil.cs | 203 --- .../LinkedListExtension.cs | 30 - .../CollectionsCategory/ListExtension.cs | 81 +- EasyTool.Core/CollectionsCategory/ListUtil.cs | 272 ---- .../CollectionsCategory/QueueExtension.cs | 33 - .../CollectionsCategory/StackExtension.cs | 19 - .../ConvertCategory/ByteExtension.cs | 78 -- .../ConvertCategory/ConvertExtension.cs | 47 +- EasyTool.Core/ConvertCategory/ConvertUtil.cs | 161 --- .../ConvertCategory/NumberExtension.cs | 128 -- .../DateTimeCategory/DateTimeExtension.cs | 61 +- EasyTool.Core/DateTimeCategory/TimerUtil.cs | 26 +- .../DateTimeCategory/TimestampUtil.cs | 84 -- .../IOCategory/FileSystemExtension.cs | 59 - EasyTool.Core/IOCategory/FileTypeExtension.cs | 64 +- EasyTool.Core/IOCategory/FileTypeUtil.cs | 82 -- EasyTool.Core/IOCategory/FileUtil.cs | 156 +-- EasyTool.Core/IOCategory/IoUtil.cs | 178 --- EasyTool.Core/IOCategory/StreamExtension.cs | 70 - EasyTool.Core/LanguageCategory/BCDUtil.cs | 200 --- .../LanguageCategory/SingletonUtil.cs | 46 - EasyTool.Core/LanguageCategory/TreeUtil.cs | 302 ----- EasyTool.Core/MathCategory/MathUtil.cs | 305 +++-- EasyTool.Core/MathCategory/NumberUtil.cs | 352 ----- .../RandomUtil.cs | 0 EasyTool.Core/NetCategory/NetUtil.cs | 129 -- .../{ToolCategory => NetCategory}/URLUtil.cs | 8 - EasyTool.Core/TextCategory/CsvUtil.cs | 127 -- .../RegexUtil.cs | 27 - EasyTool.Core/TextCategory/UnicodeUtil.cs | 180 --- EasyTool.Core/ToolCategory/ArrayUtil.cs | 255 ---- EasyTool.Core/ToolCategory/ClassExtension.cs | 89 -- EasyTool.Core/ToolCategory/ClassUtil.cs | 240 ---- EasyTool.Core/ToolCategory/ColorExtension.cs | 16 +- EasyTool.Core/ToolCategory/DLLUtil.cs | 148 -- EasyTool.Core/ToolCategory/EnumExtension.cs | 71 +- EasyTool.Core/ToolCategory/EnumUtil.cs | 253 ---- EasyTool.Core/ToolCategory/EscapeUtil.cs | 211 --- .../ToolCategory/ExceptionExtension.cs | 12 - EasyTool.Core/ToolCategory/GuidExtension.cs | 18 - EasyTool.Core/ToolCategory/IdcardUtil.cs | 470 ------- EasyTool.Core/ToolCategory/MEFUtil.cs | 126 -- EasyTool.Core/ToolCategory/ObjectExtension.cs | 497 ++++++- EasyTool.Core/ToolCategory/ObjectUtil.cs | 1193 ----------------- EasyTool.Core/ToolCategory/PageUtil.cs | 35 +- EasyTool.Core/ToolCategory/ProcessUtil.cs | 202 --- EasyTool.Core/ToolCategory/ReflectUtil.cs | 307 +++-- EasyTool.Core/ToolCategory/RuntimeUtil.cs | 147 -- EasyTool.Core/ToolCategory/StrExtension.cs | 48 - EasyTool.Core/ToolCategory/StrUtil.cs | 152 +-- .../ToolCategory/StringBuilderExtension.cs | 10 - .../ToolCategory/StringComparisonExtension.cs | 25 - EasyTool.Core/ToolCategory/SystemUtil.cs | 331 +++++ EasyTool.Core/ToolCategory/TypeExtension.cs | 12 - EasyTool.Core/ToolCategory/TypeUtil.cs | 212 --- .../CloneCategory/CloneExtensionTests.cs | 4 +- 69 files changed, 1931 insertions(+), 8190 deletions(-) delete mode 100644 EasyTool.Core/CloneCategory/CloneExtension.cs delete mode 100644 EasyTool.Core/CloneCategory/CloneUtil.cs delete mode 100644 EasyTool.Core/CodeCategory/Base32Util.cs delete mode 100644 EasyTool.Core/CodeCategory/Base62Util.cs delete mode 100644 EasyTool.Core/CodeCategory/Base64Util.cs create mode 100644 EasyTool.Core/CodeCategory/EncodingUtil.cs rename EasyTool.Core/{ToolCategory => CodeCategory}/HashUtil.cs (99%) rename EasyTool.Core/{ToolCategory => CodeCategory}/HexUtil.cs (95%) delete mode 100644 EasyTool.Core/CodeCategory/MorseUtil.cs delete mode 100644 EasyTool.Core/CodeCategory/PunycodeUtil.cs delete mode 100644 EasyTool.Core/CodeCategory/RotUtil.cs delete mode 100644 EasyTool.Core/CollectionsCategory/IteratorUtil.cs delete mode 100644 EasyTool.Core/CollectionsCategory/LinkedListExtension.cs delete mode 100644 EasyTool.Core/CollectionsCategory/ListUtil.cs delete mode 100644 EasyTool.Core/CollectionsCategory/QueueExtension.cs delete mode 100644 EasyTool.Core/CollectionsCategory/StackExtension.cs delete mode 100644 EasyTool.Core/ConvertCategory/ConvertUtil.cs delete mode 100644 EasyTool.Core/DateTimeCategory/TimestampUtil.cs delete mode 100644 EasyTool.Core/IOCategory/FileTypeUtil.cs delete mode 100644 EasyTool.Core/IOCategory/IoUtil.cs delete mode 100644 EasyTool.Core/LanguageCategory/BCDUtil.cs delete mode 100644 EasyTool.Core/LanguageCategory/SingletonUtil.cs delete mode 100644 EasyTool.Core/LanguageCategory/TreeUtil.cs delete mode 100644 EasyTool.Core/MathCategory/NumberUtil.cs rename EasyTool.Core/{ToolCategory => MathCategory}/RandomUtil.cs (100%) delete mode 100644 EasyTool.Core/NetCategory/NetUtil.cs rename EasyTool.Core/{ToolCategory => NetCategory}/URLUtil.cs (94%) delete mode 100644 EasyTool.Core/TextCategory/CsvUtil.cs rename EasyTool.Core/{ToolCategory => TextCategory}/RegexUtil.cs (79%) delete mode 100644 EasyTool.Core/TextCategory/UnicodeUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/ArrayUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/ClassExtension.cs delete mode 100644 EasyTool.Core/ToolCategory/ClassUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/DLLUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/EnumUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/EscapeUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/IdcardUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/MEFUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/ObjectUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/ProcessUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/RuntimeUtil.cs create mode 100644 EasyTool.Core/ToolCategory/SystemUtil.cs delete mode 100644 EasyTool.Core/ToolCategory/TypeUtil.cs diff --git a/EasyTool.Core/CloneCategory/CloneExtension.cs b/EasyTool.Core/CloneCategory/CloneExtension.cs deleted file mode 100644 index aa32c1a..0000000 --- a/EasyTool.Core/CloneCategory/CloneExtension.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool.Extension -{ - public static class CloneExtension - { - //定义一个泛型方法,接受一个泛型参数 T,并返回一个 T 类型的对象 - public static T? Clone(this T? obj) => CloneUtil.Clone(obj); - } -} diff --git a/EasyTool.Core/CloneCategory/CloneUtil.cs b/EasyTool.Core/CloneCategory/CloneUtil.cs deleted file mode 100644 index 7483124..0000000 --- a/EasyTool.Core/CloneCategory/CloneUtil.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; -using System.Runtime.Serialization; -using System.Threading.Tasks; - -namespace EasyTool -{ - /// - /// 静态工具类 CloneHelper,用于深度克隆对象 - /// - public static class CloneUtil - { - // 定义一个泛型方法,接受一个泛型参数 T,并返回一个 T 类型的对象 - public static T? Clone(T? obj) - { - // 检查类型是否可序列化 - if (!typeof(T).IsSerializable) - { - throw new ArgumentException("The type must be serializable.", nameof(obj)); - } - - // 如果对象为 null,则返回 null - if (ReferenceEquals(obj, null)) - { - return default; - } - - // 创建一个二进制序列化器 - IFormatter formatter = new BinaryFormatter(); - - // 创建一个内存流 - using (var stream = new MemoryStream()) - { - // 使用二进制序列化将对象写入内存流 - formatter.Serialize(stream, obj); - - // 将内存流位置重置为开头 - stream.Seek(0, SeekOrigin.Begin); - - // 使用反序列化从内存流中读取并返回克隆的对象 - return (T)formatter.Deserialize(stream)!; - } - } - - /// - /// 静态工具类 CloneHelper,用于异步深度克隆对象 - /// - /// - /// - /// - /// - public static async Task CloneAsync(T? obj) - { - // 检查类型是否可序列化 - if (!typeof(T).IsSerializable) - { - throw new ArgumentException("The type must be serializable.", nameof(obj)); - } - - // 如果对象为 null,则返回 null - if (ReferenceEquals(obj, null)) - { - return default; - } - - // 创建一个二进制序列化器 - IFormatter formatter = new BinaryFormatter(); - - // 创建一个内存流 - using (var stream = new MemoryStream()) - { - // 使用二进制序列化将对象写入内存流 - formatter.Serialize(stream, obj); - - // 将内存流位置重置为开头 - stream.Seek(0, SeekOrigin.Begin); - - // 使用反序列化从内存流中读取并返回克隆的对象 - return await Task.FromResult((T?)formatter.Deserialize(stream)); - } - } - } -} diff --git a/EasyTool.Core/CodeCategory/Base32Util.cs b/EasyTool.Core/CodeCategory/Base32Util.cs deleted file mode 100644 index c3c5641..0000000 --- a/EasyTool.Core/CodeCategory/Base32Util.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - /// - /// Base32 编码解码工具类 - /// - public static class Base32Util - { - // Base32 字符集,共 32 个字符 - private static readonly char[] BASE32_CHARS = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray(); - - // Base32 填充字符 - private const char BASE32_PADDING_CHAR = '='; - - /// - /// 将给定的字节数组转换为 Base32 编码字符串。 - /// - /// 要转换的字节数组 - /// 转换后的 Base32 编码字符串 - public static string Encode(byte[] bytes) - { - if (bytes == null) - { - throw new ArgumentNullException(nameof(bytes)); - } - - int length = bytes.Length; - if (length == 0) - { - return string.Empty; - } - - char[] chars = new char[(length + 4) / 5 * 8]; - int index = 0; - for (int i = 0; i < length; i += 5) - { - int val = (bytes[i] << 24) + ((i + 1 < length ? bytes[i + 1] : 0) << 16) + - ((i + 2 < length ? bytes[i + 2] : 0) << 8) + ((i + 3 < length ? bytes[i + 3] : 0) << 0); - chars[index++] = BASE32_CHARS[(val >> 35) & 0x1F]; - chars[index++] = BASE32_CHARS[(val >> 30) & 0x1F]; - chars[index++] = BASE32_CHARS[(val >> 25) & 0x1F]; - chars[index++] = BASE32_CHARS[(val >> 20) & 0x1F]; - chars[index++] = BASE32_CHARS[(val >> 15) & 0x1F]; - chars[index++] = BASE32_CHARS[(val >> 10) & 0x1F]; - chars[index++] = BASE32_CHARS[(val >> 5) & 0x1F]; - chars[index++] = BASE32_CHARS[val & 0x1F]; - } - - // 添加填充字符 - int paddingCount = length % 5; - if (paddingCount > 0) - { - chars[chars.Length - 1] = BASE32_PADDING_CHAR; - if (paddingCount == 1) - { - chars[chars.Length - 2] = BASE32_PADDING_CHAR; - } - if (paddingCount <= 2) - { - chars[chars.Length - 3] = BASE32_PADDING_CHAR; - } - if (paddingCount <= 3) - { - chars[chars.Length - 4] = BASE32_PADDING_CHAR; - } - if (paddingCount <= 4) - { - chars[chars.Length - 5] = BASE32_PADDING_CHAR; - } - } - - return new string(chars); - } - - /// - /// 将给定的 Base32 编码字符串转换为字节数组。 - /// - /// 要转换的 Base32 编码字符串 - /// 转换后的字节数组 - public static byte[] Decode(string str) - { - if (string.IsNullOrEmpty(str)) - { - throw new ArgumentException("String is null or empty.", nameof(str)); - } - - int length = str.Length; - if (length % 8 != 0) - { - throw new ArgumentException("Invalid length of input string: " + length, nameof(str)); - } - - int paddingCount = 0; - if (length > 0 && str[length - 1] == BASE32_PADDING_CHAR) - { - paddingCount++; - } - if (length > 1 && str[length - 2] == BASE32_PADDING_CHAR) - { - paddingCount++; - } - if (length > 3 && str[length - 3] == BASE32_PADDING_CHAR) - { - paddingCount++; - } - if (length > 4 && str[length - 4] == BASE32_PADDING_CHAR) - { - paddingCount++; - } - if (length > 6 && str[length - 6] == BASE32_PADDING_CHAR) - { - paddingCount++; - } - - byte[] bytes = new byte[length / 8 * 5 - paddingCount]; - int index = 0; - for (int i = 0; i < length; i += 8) - { - int val = (DecodeBase32Char(str[i]) << 35) + - (DecodeBase32Char(str[i + 1]) << 30) + - (DecodeBase32Char(str[i + 2]) << 25) + - (DecodeBase32Char(str[i + 3]) << 20) + - (DecodeBase32Char(str[i + 4]) << 15) + - (DecodeBase32Char(str[i + 5]) << 10) + - (DecodeBase32Char(str[i + 6]) << 5) + - DecodeBase32Char(str[i + 7]); - bytes[index++] = (byte)(val >> 24); - if (index < bytes.Length) - { - bytes[index++] = (byte)(val >> 16); - } - if (index < bytes.Length) - { - bytes[index++] = (byte)(val >> 8); - } - if (index < bytes.Length) - { - bytes[index++] = (byte)val; - } - } - - return bytes; - } - - // 解码 Base32 字符 - private static int DecodeBase32Char(char c) - { - if (c >= 'A' && c <= 'Z') - { - return c - 'A'; - } - if (c >= '2' && c <= '7') - { - return c - '2' + 26; - } - throw new ArgumentException("Invalid character in input string: " + c, nameof(c)); - } - } -} diff --git a/EasyTool.Core/CodeCategory/Base62Util.cs b/EasyTool.Core/CodeCategory/Base62Util.cs deleted file mode 100644 index cac4b89..0000000 --- a/EasyTool.Core/CodeCategory/Base62Util.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - /// - /// Base62 编码解码工具类 - /// - public static class Base62Util - { - // Base62 字符集,共 62 个字符 - private static readonly char[] BASE62_CHARS = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(); - - /// - /// 将给定的整数转换为 Base62 编码字符串。 - /// - /// 要转换的整数 - /// 转换后的 Base62 编码字符串 - public static string Encode(long number) - { - if (number < 0) - { - throw new ArgumentOutOfRangeException(nameof(number), "Number must be non-negative."); - } - - if (number == 0) - { - return BASE62_CHARS[0].ToString(); - } - - List chars = new List(); - int targetBase = BASE62_CHARS.Length; - while (number > 0) - { - int index = (int)(number % targetBase); - chars.Add(BASE62_CHARS[index]); - number = number / targetBase; - } - chars.Reverse(); - return new string(chars.ToArray()); - } - - /// - /// 将给定的 Base62 编码字符串转换为整数。 - /// - /// 要转换的 Base62 编码字符串 - /// 转换后的整数 - public static long Decode(string str) - { - if (string.IsNullOrEmpty(str)) - { - throw new ArgumentException("String is null or empty.", nameof(str)); - } - - long result = 0; - int sourceBase = BASE62_CHARS.Length; - long multiplier = 1; - for (int i = str.Length - 1; i >= 0; i--) - { - int digit = Array.IndexOf(BASE62_CHARS, str[i]); - if (digit == -1) - { - throw new ArgumentException("Invalid character in string: " + str[i], nameof(str)); - } - result += digit * multiplier; - multiplier *= sourceBase; - } - return result; - } - } -} diff --git a/EasyTool.Core/CodeCategory/Base64Util.cs b/EasyTool.Core/CodeCategory/Base64Util.cs deleted file mode 100644 index 20098c5..0000000 --- a/EasyTool.Core/CodeCategory/Base64Util.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - /// - /// Base64 编码解码工具类 - /// - public static class Base64Util - { - // Base64 字符集,共 64 个字符 - private static readonly char[] BASE64_CHARS = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".ToCharArray(); - - // Base64 填充字符 - private const char BASE64_PADDING_CHAR = '='; - - /// - /// 将给定的字节数组转换为 Base64 编码字符串。 - /// [Obsolete("请直接使用 Convert.ToBase64String(bytes)")] - /// - /// 要转换的字节数组 - /// 转换后的 Base64 编码字符串 - [Obsolete("请直接使用 Convert.ToBase64String(bytes)", false)] - public static string Encode(byte[] bytes) - { - if (bytes == null) - { - throw new ArgumentNullException(nameof(bytes)); - } - - int length = bytes.Length; - if (length == 0) - { - return string.Empty; - } - - char[] chars = new char[(length + 2) / 3 * 4]; - int index = 0; - for (int i = 0; i < length; i += 3) - { - int val = (bytes[i] << 16) + ((i + 1 < length ? bytes[i + 1] : 0) << 8) + (i + 2 < length ? bytes[i + 2] : 0); - chars[index++] = BASE64_CHARS[(val >> 18) & 0x3F]; - chars[index++] = BASE64_CHARS[(val >> 12) & 0x3F]; - chars[index++] = BASE64_CHARS[(val >> 6) & 0x3F]; - chars[index++] = BASE64_CHARS[val & 0x3F]; - } - - // 添加填充字符 - int paddingCount = length % 3; - if (paddingCount > 0) - { - chars[chars.Length - 1] = BASE64_PADDING_CHAR; - if (paddingCount == 1) - { - chars[chars.Length - 2] = BASE64_PADDING_CHAR; - } - } - - return new string(chars); - } - - /// - /// 将给定的 Base64 编码字符串转换为字节数组。 - /// [Obsolete("请直接使用 Convert.FromBase64String(str)")] - /// - /// 要转换的 Base64 编码字符串 - /// 转换后的字节数组 - [Obsolete("请直接使用 Convert.FromBase64String(str)", false)] - public static byte[] Decode(string str) - { - if (string.IsNullOrEmpty(str)) - { - throw new ArgumentException("String is null or empty.", nameof(str)); - } - int length = str.Length; - if (length % 4 != 0) - { - throw new ArgumentException("Invalid length of input string: " + length, nameof(str)); - } - - int paddingCount = 0; - if (length > 0 && str[length - 1] == BASE64_PADDING_CHAR) - { - paddingCount++; - } - if (length > 1 && str[length - 2] == BASE64_PADDING_CHAR) - { - paddingCount++; - } - - byte[] bytes = new byte[length / 4 * 3 - paddingCount]; - int index = 0; - for (int i = 0; i < length; i += 4) - { - int val = (DecodeBase64Char(str[i]) << 18) + - (DecodeBase64Char(str[i + 1]) << 12) + - (DecodeBase64Char(str[i + 2]) << 6) + - DecodeBase64Char(str[i + 3]); - bytes[index++] = (byte)(val >> 16); - if (index < bytes.Length) - { - bytes[index++] = (byte)(val >> 8); - } - if (index < bytes.Length) - { - bytes[index++] = (byte)val; - } - } - - return bytes; - } - - // 解码 Base64 字符 - private static int DecodeBase64Char(char c) - { - if (c >= 'A' && c <= 'Z') - { - return c - 'A'; - } - if (c >= 'a' && c <= 'z') - { - return c - 'a' + 26; - } - if (c >= '0' && c <= '9') - { - return c - '0' + 52; - } - if (c == '+') - { - return 62; - } - if (c == '/') - { - return 63; - } - throw new ArgumentException("Invalid character in input string: " + c, nameof(c)); - } - } -} diff --git a/EasyTool.Core/CodeCategory/EncodingUtil.cs b/EasyTool.Core/CodeCategory/EncodingUtil.cs new file mode 100644 index 0000000..67a3042 --- /dev/null +++ b/EasyTool.Core/CodeCategory/EncodingUtil.cs @@ -0,0 +1,377 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace EasyTool +{ + /// + /// 编码工具类,提供各种编码格式的转换功能 + /// + public static class EncodingUtil + { + #region Base32 编码 + + // Base32 字符集,共 32 个字符 + private static readonly char[] BASE32_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray(); + + // Base32 填充字符 + private const char BASE32_PADDING_CHAR = '='; + + /// + /// 将给定的字节数组转换为 Base32 编码字符串 + /// + /// 要转换的字节数组 + /// 转换后的 Base32 编码字符串 + public static string Base32Encode(byte[] bytes) + { + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes)); + } + + int length = bytes.Length; + if (length == 0) + { + return string.Empty; + } + + char[] chars = new char[(length + 4) / 5 * 8]; + int index = 0; + for (int i = 0; i < length; i += 5) + { + int val = (bytes[i] << 24) + ((i + 1 < length ? bytes[i + 1] : 0) << 16) + + ((i + 2 < length ? bytes[i + 2] : 0) << 8) + ((i + 3 < length ? bytes[i + 3] : 0) << 0); + chars[index++] = BASE32_CHARS[(val >> 35) & 0x1F]; + chars[index++] = BASE32_CHARS[(val >> 30) & 0x1F]; + chars[index++] = BASE32_CHARS[(val >> 25) & 0x1F]; + chars[index++] = BASE32_CHARS[(val >> 20) & 0x1F]; + chars[index++] = BASE32_CHARS[(val >> 15) & 0x1F]; + chars[index++] = BASE32_CHARS[(val >> 10) & 0x1F]; + chars[index++] = BASE32_CHARS[(val >> 5) & 0x1F]; + chars[index++] = BASE32_CHARS[val & 0x1F]; + } + + // 添加填充字符 + int paddingCount = length % 5; + if (paddingCount > 0) + { + chars[chars.Length - 1] = BASE32_PADDING_CHAR; + if (paddingCount == 1) + { + chars[chars.Length - 2] = BASE32_PADDING_CHAR; + } + else if (paddingCount <= 2) + { + chars[chars.Length - 3] = BASE32_PADDING_CHAR; + } + else if (paddingCount <= 3) + { + chars[chars.Length - 4] = BASE32_PADDING_CHAR; + } + else if (paddingCount <= 4) + { + chars[chars.Length - 5] = BASE32_PADDING_CHAR; + } + } + + return new string(chars); + } + + /// + /// 将给定的 Base32 编码字符串转换为字节数组 + /// + /// 要转换的 Base32 编码字符串 + /// 转换后的字节数组 + public static byte[] Base32Decode(string str) + { + if (string.IsNullOrEmpty(str)) + { + throw new ArgumentException("String is null or empty.", nameof(str)); + } + + // 移除填充字符 + str = str.TrimEnd('='); + + int length = str.Length; + if (length % 8 != 0) + { + throw new ArgumentException("Invalid length of input string: " + length, nameof(str)); + } + + int paddingCount = 0; + if (length > 0 && str[length - 1] == BASE32_PADDING_CHAR) + { + paddingCount++; + } + if (length > 1 && str[length - 2] == BASE32_PADDING_CHAR) + { + paddingCount++; + } + if (length > 3 && str[length - 3] == BASE32_PADDING_CHAR) + { + paddingCount++; + } + if (length > 4 && str[length - 4] == BASE32_PADDING_CHAR) + { + paddingCount++; + } + if (length > 5 && str[length - 5] == BASE32_PADDING_CHAR) + { + paddingCount++; + } + if (length > 6 && str[length - 6] == BASE32_PADDING_CHAR) + { + paddingCount++; + } + + byte[] bytes = new byte[length / 8 * 5 - paddingCount]; + int index = 0; + for (int i = 0; i < length; i += 8) + { + int val = (DecodeBase32Char(str[i]) << 35) + + (DecodeBase32Char(str[i + 1]) << 30) + + (DecodeBase32Char(str[i + 2]) << 25) + + (DecodeBase32Char(str[i + 3]) << 20) + + (DecodeBase32Char(str[i + 4]) << 15) + + (DecodeBase32Char(str[i + 5]) << 10) + + (DecodeBase32Char(str[i + 6]) << 5) + + DecodeBase32Char(str[i + 7]); + bytes[index++] = (byte)(val >> 24); + if (index < bytes.Length) + { + bytes[index++] = (byte)(val >> 16); + } + if (index < bytes.Length) + { + bytes[index++] = (byte)(val >> 8); + } + if (index < bytes.Length) + { + bytes[index++] = (byte)val; + } + } + + return bytes; + } + + // 解码 Base32 字符 + private static int DecodeBase32Char(char c) + { + if (c >= 'A' && c <= 'Z') + { + return c - 'A'; + } + if (c >= '2' && c <= '7') + { + return c - '2' + 26; + } + throw new ArgumentException("Invalid character in input string: " + c, nameof(c)); + } + + #endregion + + #region Base62 编码 + + // Base62 字符集,共 62 个字符 + private static readonly char[] BASE62_CHARS = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(); + + /// + /// 将给定的整数转换为 Base62 编码字符串 + /// + /// 要转换的整数 + /// 转换后的 Base62 编码字符串 + public static string Base62Encode(long number) + { + if (number < 0) + { + throw new ArgumentOutOfRangeException(nameof(number), "Number must be non-negative."); + } + + if (number == 0) + { + return BASE62_CHARS[0].ToString(); + } + + List chars = new List(); + int targetBase = BASE62_CHARS.Length; + while (number > 0) + { + int index = (int)(number % targetBase); + chars.Add(BASE62_CHARS[index]); + number = number / targetBase; + } + chars.Reverse(); + return new string(chars.ToArray()); + } + + /// + /// 将给定的 Base62 编码字符串转换为整数 + /// + /// 要转换的 Base62 编码字符串 + /// 转换后的整数 + public static long Base62Decode(string str) + { + if (string.IsNullOrEmpty(str)) + { + throw new ArgumentException("String is null or empty.", nameof(str)); + } + + long result = 0; + int sourceBase = BASE62_CHARS.Length; + long multiplier = 1; + for (int i = str.Length - 1; i >= 0; i--) + { + int digit = Array.IndexOf(BASE62_CHARS, str[i]); + if (digit == -1) + { + throw new ArgumentException("Invalid character in string: " + str[i], nameof(str)); + } + result += digit * multiplier; + multiplier *= sourceBase; + } + return result; + } + + #endregion + + #region ROT 加密 + + /// + /// 将给定的字符串按照 ROT 加密算法进行加密 + /// + /// 要加密的字符串 + /// 偏移量 + /// 加密后的字符串 + public static string RotEncrypt(string text, int n) + { + if (string.IsNullOrEmpty(text)) + { + return text; + } + + string upperCaseText = text.ToUpper(); + return new string(upperCaseText.Select(c => + { + if (!char.IsLetter(c)) + { + return c; + } + int x = c - 'A'; + int y = (x + n) % 26; + return (char)(y + 'A'); + }).ToArray()); + } + + /// + /// 将给定的字符串按照 ROT 加密算法进行解密 + /// + /// 要解密的字符串 + /// 偏移量 + /// 解密后的字符串 + public static string RotDecrypt(string text, int n) + { + return RotEncrypt(text, 26 - n); + } + + #endregion + + #region Morse 电码 + + // Morse 电码表 + private static readonly Dictionary MORSE_TABLE = new Dictionary + { + {'A', ".-"}, + {'B', "-..."}, + {'C', "-.-."}, + {'D', "-.."}, + {'E', "."}, + {'F', "..-."}, + {'G', "--."}, + {'H', "...."}, + {'I', ".."}, + {'J', ".---"}, + {'K', "-.-"}, + {'L', ".-.."}, + {'M', "--"}, + {'N', "-."}, + {'O', "---"}, + {'P', ".--."}, + {'Q', "--.-"}, + {'R', ".-."}, + {'S', "..."}, + {'T', "-"}, + {'U', "..-"}, + {'V', "...-"}, + {'W', ".--"}, + {'X', "-.."}, + {'Y', "-.--"}, + {'Z', "--.."}, + {'0', "-----"}, + {'1', ".----"}, + {'2', "..---"}, + {'3', "...--"}, + {'4', "....-"}, + {'5', "....."}, + {'6', "-...."}, + {'7', "--..."}, + {'8', "---.."}, + {'9', "----."}, + {' ', " "} + }; + + /// + /// 将给定的字符串转换为 Morse 电码字符串 + /// + /// 要转换的字符串 + /// 转换后的 Morse 电码字符串 + public static string MorseEncode(string str) + { + if (string.IsNullOrEmpty(str)) + { + return string.Empty; + } + + List morseCodes = new List(); + foreach (char c in str.ToUpper()) + { + if (MORSE_TABLE.ContainsKey(c)) + { + morseCodes.Add(MORSE_TABLE[c]); + } + } + return string.Join(" ", morseCodes); + } + + /// + /// 将给定的 Morse 电码字符串转换为原始字符串 + /// + /// 要转换的 Morse 电码字符串 + /// 转换后的原始字符串 + public static string MorseDecode(string morseCode) + { + if (string.IsNullOrEmpty(morseCode)) + { + return string.Empty; + } + + string[] codes = morseCode.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + List chars = new List(); + foreach (string code in codes) + { + foreach (KeyValuePair kvp in MORSE_TABLE) + { + if (kvp.Value == code) + { + chars.Add(kvp.Key); + break; + } + } + } + return new string(chars.ToArray()); + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/HashUtil.cs b/EasyTool.Core/CodeCategory/HashUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/HashUtil.cs rename to EasyTool.Core/CodeCategory/HashUtil.cs index 6913e7a..5ac7eb3 100644 --- a/EasyTool.Core/ToolCategory/HashUtil.cs +++ b/EasyTool.Core/CodeCategory/HashUtil.cs @@ -21,6 +21,7 @@ public static uint AdditiveHash(string str) { hash += c; } + return hash; } @@ -36,6 +37,7 @@ public static uint RotatingHash(string str) { hash = (hash << 4) ^ (hash >> 28) ^ c; } + return hash; } @@ -53,6 +55,7 @@ public static uint OneByOneHash(string str) hash += (hash << 10); hash ^= (hash >> 6); } + hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); @@ -71,6 +74,7 @@ public static uint Bernstein(string str) { hash = 33 * hash + c; } + return hash; } @@ -81,7 +85,7 @@ public static uint Bernstein(string str) /// 大质数 /// 哈希桶的数量 /// a的取值范围为[1, prime - 1] - /// b的取值范围 + /// b的取值范围 public static uint Universal(string str, uint prime, uint num_buckets, uint a, uint b) { uint hash = a; @@ -89,6 +93,7 @@ public static uint Universal(string str, uint prime, uint num_buckets, uint a, u { hash = hash * prime + c; } + hash = (hash * a + b) % num_buckets; return hash; } @@ -106,6 +111,7 @@ public static uint Zobrist(string str, uint[] table) { hash ^= table[str[i]]; } + return hash; } @@ -123,6 +129,7 @@ public static uint FnvHash(string str) hash *= fnv_prime; hash ^= c; } + return hash; } @@ -157,6 +164,7 @@ public static uint RsHash(string str, uint b, uint a) hash = hash * a + c; a = a * b; } + return hash; } @@ -172,6 +180,7 @@ public static uint JsHash(string str) { hash ^= ((hash << 5) + c + (hash >> 2)); } + return hash; } @@ -196,6 +205,7 @@ public static uint PjwHash(string str) hash = ((hash ^ (test >> (int)ThreeQuarters)) & (~HighBits)); } } + return hash; } @@ -216,8 +226,10 @@ public static uint ElfHash(string str) { hash ^= (x >> 24); } + hash &= ~x; } + return hash; } @@ -234,6 +246,7 @@ public static uint BkdrHash(string str, uint seed) { hash = hash * seed + c; } + return hash; } @@ -249,6 +262,7 @@ public static uint SdbmHash(string str) { hash = c + (hash << 6) + (hash << 16) - hash; } + return hash; } @@ -264,6 +278,7 @@ public static uint DjbHash(string str) { hash = ((hash << 5) + hash) + c; } + return hash; } @@ -279,6 +294,7 @@ public static uint DekHash(string str) { hash = ((hash << 5) ^ (hash >> 27)) ^ c; } + return hash; } @@ -302,6 +318,7 @@ public static uint ApHash(string str) hash ^= (~((hash << 11) ^ str[i] ^ (hash >> 5))); } } + return hash; } @@ -327,6 +344,7 @@ public static uint TianlHash(string str, uint len) hash += (hash << 10); hash ^= (hash >> 6); } + hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); @@ -393,6 +411,7 @@ public static uint JavaDefaultHash(string str) { h = 31 * h + c; } + hash = h; return hash; } @@ -412,8 +431,9 @@ public static ulong MixHash(string str) hash1 = (hash1 * seed) + c; hash2 = (hash2 * seed) + c + 1; } + return hash1 + (hash2 * 1566083941); } } - } +} diff --git a/EasyTool.Core/ToolCategory/HexUtil.cs b/EasyTool.Core/CodeCategory/HexUtil.cs similarity index 95% rename from EasyTool.Core/ToolCategory/HexUtil.cs rename to EasyTool.Core/CodeCategory/HexUtil.cs index 52b892f..102a626 100644 --- a/EasyTool.Core/ToolCategory/HexUtil.cs +++ b/EasyTool.Core/CodeCategory/HexUtil.cs @@ -86,28 +86,15 @@ public static string IntToHex(int number) } /// - /// 将16进制字符串中的所有字符转换为大写 - /// [Obsolete("请直接使用 hex.ToUpper()")] + /// 将16进制字符串转换为大写形式 /// /// 16进制字符串 - /// 大写16进制字符串 - [Obsolete("请直接使用 hex.ToUpper()", false)] + /// 大写形式的16进制字符串 public static string HexToUpper(string hex) { return hex.ToUpper(); } - /// - /// 将16进制字符串中的所有字符转换为小写 - /// [Obsolete("请直接使用 hex.ToLower()")] - /// - /// 16进制字符串 - /// 小写16进制字符串 - [Obsolete("请直接使用 hex.ToLower()", false)] - public static string HexToLower(string hex) - { - return hex.ToLower(); - } /// /// 获取16进制字符串中指定位置的字符 diff --git a/EasyTool.Core/CodeCategory/MorseUtil.cs b/EasyTool.Core/CodeCategory/MorseUtil.cs deleted file mode 100644 index 4341231..0000000 --- a/EasyTool.Core/CodeCategory/MorseUtil.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - /// - /// Morse 电码工具类 - /// - public static class MorseUtil - { - // Morse 电码表 - private static readonly Dictionary MORSE_TABLE = new Dictionary() - { - {'A', ".-"}, - {'B', "-..."}, - {'C', "-.-."}, - {'D', "-.."}, - {'E', "."}, - {'F', "..-."}, - {'G', "--."}, - {'H', "...."}, - {'I', ".."}, - {'J', ".---"}, - {'K', "-.-"}, - {'L', ".-.."}, - {'M', "--"}, - {'N', "-."}, - {'O', "---"}, - {'P', ".--."}, - {'Q', "--.-"}, - {'R', ".-."}, - {'S', "..."}, - {'T', "-"}, - {'U', "..-"}, - {'V', "...-"}, - {'W', ".--"}, - {'X', "-..-"}, - {'Y', "-.--"}, - {'Z', "--.."}, - {'0', "-----"}, - {'1', ".----"}, - {'2', "..---"}, - {'3', "...--"}, - {'4', "....-"}, - {'5', "....."}, - {'6', "-...."}, - {'7', "--..."}, - {'8', "---.."}, - {'9', "----."}, - {' ', " "} - }; - - /// - /// 将给定的字符串转换为 Morse 电码字符串。 - /// - /// 要转换的字符串 - /// 转换后的 Morse 电码字符串 - public static string Encode(string str) - { - if (string.IsNullOrEmpty(str)) - { - return string.Empty; - } - - List morseCodes = new List(); - foreach (char c in str.ToUpper()) - { - if (MORSE_TABLE.ContainsKey(c)) - { - morseCodes.Add(MORSE_TABLE[c]); - } - } - return string.Join(" ", morseCodes); - } - - - /// - /// 将给定的 Morse 电码字符串转换为原始字符串。 - /// - /// 要转换的 Morse 电码字符串 - /// 转换后的原始字符串 - public static string Decode(string morseCode) - { - if (string.IsNullOrEmpty(morseCode)) - { - return string.Empty; - } - - string[] codes = morseCode.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - List chars = new List(); - foreach (string code in codes) - { - foreach (KeyValuePair kvp in MORSE_TABLE) - { - if (kvp.Value == code) - { - chars.Add(kvp.Key); - break; - } - } - } - return new string(chars.ToArray()); - } - - } -} diff --git a/EasyTool.Core/CodeCategory/PunycodeUtil.cs b/EasyTool.Core/CodeCategory/PunycodeUtil.cs deleted file mode 100644 index cee3389..0000000 --- a/EasyTool.Core/CodeCategory/PunycodeUtil.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EasyTool -{ - /// - /// Punycode 工具类 - /// - public static class PunycodeUtil - { - private const int BASE = 36; - private const int TMIN = 1; - private const int TMAX = 26; - private const int SKEW = 38; - private const int DAMP = 700; - private const int INITIAL_BIAS = 72; - private const int INITIAL_N = 128; - - private static readonly char[] DELIMITER = { '-' }; - - /// - /// 将给定的 Unicode 字符串按照 Punycode 编码规则进行编码。 - /// - /// 要编码的 Unicode 字符串 - /// 编码后的字符串 - public static string Encode(string input) - { - if (string.IsNullOrEmpty(input)) - { - return input; - } - - List inputChars = input.Select(c => (int)c).ToList(); - List basicChars = inputChars.Where(c => c < 0x80).ToList(); - List extendedChars = inputChars.Except(basicChars).ToList(); - - List output = new List(); - int n = INITIAL_N; - int delta = 0; - int bias = INITIAL_BIAS; - - // Encode the basic code points - foreach (int b in basicChars) - { - output.Add(b); - } - - int h = output.Count; - int bLength = basicChars.Count; - if (bLength > 0 && extendedChars.Count > 0) - { - output.Add('-'); - } - - // Main encoding loop - while (h < inputChars.Count) - { - int m = int.MaxValue; - foreach (int e in extendedChars) - { - if (e >= n && e < m) - { - m = e; - } - } - - delta += (m - n) * (h + 1); - n = m; - foreach (int e in extendedChars) - { - if (e < n) - { - delta++; - } - - if (e == n) - { - int q = delta; - int k = BASE; - while (true) - { - int t = k <= bias ? TMIN : (k >= bias + TMAX ? TMAX : k - bias); - if (q < t) - { - break; - } - - output.Add(GetCodePoint(t + (q - t) % (BASE - t))); - q = (q - t) / (BASE - t); - k += BASE; - } - - output.Add(GetCodePoint(q)); - bias = Adapt(delta, h + 1, h == bLength); - delta = 0; - h++; - } - } - - delta++; - n++; - } - - return new string(output.Select(c => (char)c).ToArray()); - } - - /// - /// 将给定的 Punycode 编码字符串进行解码,得到原始的 Unicode 字符串。 - /// - /// 要解码的 Punycode 编码字符串 - /// 原始的 Unicode 字符串 - public static string Decode(string input) - { - if (string.IsNullOrEmpty(input)) - { - return input; - } - - List output = new List(); - List inputChars = input.Select(c => (int)c).ToList(); - List basicChars = inputChars.Where(c => c < 0x80).ToList(); - int i = 0; - int n = INITIAL_N; - int bias = INITIAL_BIAS; - - // Find the last delimiter - int lastDelim = input.LastIndexOf('-'); - if (lastDelim < 0) - { - lastDelim = 0; - } - - // Decode the basic code points - for (int j = 0; j < lastDelim; j++) - { - int c = inputChars[j]; - if (!IsBasic(c)) - { - throw new ArgumentException("Invalid input string."); - } - - output.Add(c); - } - - // Main decoding loop - int p = lastDelim > 0 ? lastDelim + 1 : 0; - while (p < inputChars.Count) - { - int oldi = i; - int w = 1; - int k = BASE; - while (true) - { - if (p >= inputChars.Count) - { - throw new ArgumentException("Invalid input string."); - } - - int c = inputChars[p++]; - int digit = GetDigit(c); - if (digit >= BASE) - { - throw new ArgumentException("Invalid input string."); - } - - if (digit > (int.MaxValue - i) / w) - { - throw new ArgumentException("Invalid input string."); - } - - i += digit * w; - int t = k <= bias ? TMIN : (k >= bias + TMAX ? TMAX : k - bias); - if (digit < t) - { - break; - } - - if (w > int.MaxValue / (BASE - t)) - { - throw new ArgumentException("Invalid input string."); - } - - w *= BASE - t; - k += BASE; - } - - int delta = i - oldi; - output.Add(GetCodePoint(delta)); - bias = Adapt(delta, output.Count, oldi == 0); - n += i / output.Count; - i %= output.Count; - } - - return new string(output.Select(c => (char)c).ToArray()); - } - - private static bool IsBasic(int codePoint) - { - return codePoint < 0x80; - } - - private static int GetDigit(int codePoint) - { - if (codePoint - '0' < 10) - { - return codePoint - '0' + 26; - } - - if (codePoint - 'a' < 26) - { - return codePoint - 'a'; - } - - if (codePoint - 'A' < 26) - { - return codePoint - 'A'; - } - - throw new ArgumentException("Invalid input string."); - } - - private static int Adapt(int delta, int numPoints, bool firstTime) - { - delta = firstTime ? delta / DAMP : delta >> 1; - delta += delta / numPoints; - - int k = 0; - while (delta > ((BASE - TMIN) * TMAX) / 2) - { - delta /= BASE - TMIN; - k += BASE; - } - - return k + (((BASE - TMIN + 1) * delta) / (delta + SKEW)); - } - - private static int GetCodePoint(int digit) - { - if (digit < 26) - { - return digit + 'a'; - } - - if (digit < 36) - { - return digit - 26 + '0'; - } - - throw new ArgumentException("Invalid input string."); - } - } -} diff --git a/EasyTool.Core/CodeCategory/RotUtil.cs b/EasyTool.Core/CodeCategory/RotUtil.cs deleted file mode 100644 index f2b86c3..0000000 --- a/EasyTool.Core/CodeCategory/RotUtil.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EasyTool -{ - /// - /// ROT 工具类 - /// - public static class RotUtil - { - /// - /// 将给定的字符串按照 ROT 加密算法进行加密。 - /// - /// 要加密的字符串 - /// 偏移量 - /// 加密后的字符串 - public static string Encrypt(string text, int n) - { - if (string.IsNullOrEmpty(text)) - { - return text; - } - - string upperCaseText = text.ToUpper(); - return new string(upperCaseText.Select(c => - { - if (!char.IsLetter(c)) - { - return c; - } - - int x = c - 'A'; - int y = (x + n) % 26; - return (char)(y + 'A'); - }).ToArray()); - } - - /// - /// 将给定的字符串按照 ROT 加密算法进行解密。 - /// - /// 要解密的字符串 - /// 偏移量 - /// 解密后的字符串 - public static string Decrypt(string text, int n) - { - return Encrypt(text, 26 - n); - } - } -} diff --git a/EasyTool.Core/CollectionsCategory/ArrayExtension.cs b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs index f482e24..c2ce442 100644 --- a/EasyTool.Core/CollectionsCategory/ArrayExtension.cs +++ b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs @@ -32,6 +32,93 @@ public static bool IsNotEmpty(this T[]? array) #region 数组操作 + /// + /// 数组排序 + /// + public static T[] Sort(this T[]? array) where T : IComparable + { + if (array.IsEmpty()) + { + throw new ArgumentException("Array is empty."); + } + + T[] sortedArray = new T[array.Length]; + array.CopyTo(sortedArray, 0); + Array.Sort(sortedArray); + + return sortedArray; + } + + /// + /// 数组反转 + /// + public static T[] Reverse(this T[]? array) + { + if (array.IsEmpty()) + { + throw new ArgumentException("Array is empty."); + } + + T[] reversedArray = new T[array.Length]; + array.CopyTo(reversedArray, 0); + Array.Reverse(reversedArray); + + return reversedArray; + } + + /// + /// 判断两个数组是否完全相等 + /// + public static bool EqualsTo(this T[]? array, T[]? other) + { + if (array.IsEmpty() && other.IsEmpty()) + { + return true; + } + + if (array.IsEmpty() || other.IsEmpty()) + { + return false; + } + + if (array!.Length != other!.Length) + { + return false; + } + + for (int i = 0; i < array.Length; i++) + { + if (!array[i].Equals(other[i])) + { + return false; + } + } + + return true; + } + + /// + /// 合并两个数组 + /// + public static T[] Concat(this T[]? array, T[]? other) + { + if (array.IsEmpty()) + { + return other ?? Array.Empty(); + } + + if (other.IsEmpty()) + { + return array; + } + + T[] result = new T[array.Length + other.Length]; + array.CopyTo(result, 0); + other.CopyTo(result, array.Length); + + return result; + } + /// /// 随机打乱数组顺序(Fisher-Yates 洗牌算法) /// @@ -75,26 +162,6 @@ public static IEnumerable Chunk(this T[]? array, int chunkSize) } } - /// - /// 将数组的元素连接成字符串 - /// [Obsolete("请直接使用 string.Join(separator, array)")] - /// - /// 数组 - /// 分隔符,默认为逗号 - [Obsolete("请直接使用 string.Join(separator, array)", false)] - public static string Join(this T[]? array, string separator = ",") - { - if (array == null || array.Length == 0) - return string.Empty; - - return string.Join(separator, array); - } - - /// - /// 清除数组中的重复元素 - /// [Obsolete("请直接使用 array.Distinct().ToArray() (LINQ)")] - /// - [Obsolete("请直接使用 array.Distinct().ToArray() (LINQ)", false)] public static T[]? Distinct(this T[]? array) { if (array == null) @@ -133,18 +200,6 @@ public static string JoinFormat(this T[]? array, string separator, string for #region 数组查找 - /// - /// 查找数组中满足条件的第一个元素的索引 - /// [Obsolete("请直接使用 Array.FindIndex(array, predicate)")] - /// - [Obsolete("请直接使用 Array.FindIndex(array, predicate)", false)] - public static int FindIndex(this T[]? array, Predicate predicate) - { - if (array == null) - return -1; - - return Array.FindIndex(array, predicate); - } /// /// 查找数组中满足条件的所有元素的索引 @@ -180,57 +235,6 @@ public static bool Contains(this T[]? array, T value, IEqualityComparer co #region 数组转换 - /// - /// 将数组转换为 HashSet - /// [Obsolete("请直接使用 new HashSet(array)")] - /// - [Obsolete("请直接使用 new HashSet(array)", false)] - public static HashSet ToHashSet(this T[]? array) - { - if (array == null) - return new HashSet(); - - return new HashSet(array); - } - - /// - /// 将数组转换为 Queue - /// [Obsolete("请直接使用 new Queue(array)")] - /// - [Obsolete("请直接使用 new Queue(array)", false)] - public static Queue ToQueue(this T[]? array) - { - if (array == null) - return new Queue(); - - return new Queue(array); - } - - /// - /// 将数组转换为 Stack - /// [Obsolete("请直接使用 new Stack(array)")] - /// - [Obsolete("请直接使用 new Stack(array)", false)] - public static Stack ToStack(this T[]? array) - { - if (array == null) - return new Stack(); - - return new Stack(array); - } - - /// - /// 将数组转换为 LinkedList - /// [Obsolete("请直接使用 new LinkedList(array)")] - /// - [Obsolete("请直接使用 new LinkedList(array)", false)] - public static LinkedList ToLinkedList(this T[]? array) - { - if (array == null) - return new LinkedList(); - - return new LinkedList(array); - } /// /// 将二维数组展平为一维数组 @@ -372,21 +376,6 @@ public static T[] Append(this T[]? array, params T[]? items) #region 数组遍历 - /// - /// 遍历数组并对每个元素执行指定操作 - /// [Obsolete("请直接使用 Array.ForEach(array, action) 或 foreach 循环")] - /// - [Obsolete("请直接使用 Array.ForEach(array, action) 或 foreach 循环", false)] - public static void ForEach(this T[]? array, Action action) - { - if (array == null || action == null) - return; - - foreach (var item in array) - { - action(item); - } - } /// /// 遍历数组并对每个元素及其索引执行指定操作 @@ -406,24 +395,6 @@ public static void ForEach(this T[]? array, Action action) #region 数组统计 - /// - /// 统计数组中满足条件的元素数量 - /// [Obsolete("请直接使用 array.Count(predicate) (LINQ)")] - /// - [Obsolete("请直接使用 array.Count(predicate) (LINQ)", false)] - public static int Count(this T[]? array, Func predicate) - { - if (array == null) - return 0; - - int count = 0; - foreach (var item in array) - { - if (predicate(item)) - count++; - } - return count; - } #endregion } diff --git a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs index eaaa2ea..22ba15b 100644 --- a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs +++ b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs @@ -42,29 +42,6 @@ public static void AddRange(this IDictionary destina } } - /// - /// 返回字典中键的集合 - /// [Obsolete("请直接使用 dictionary.Keys")] - /// - /// 要获取键的字典 - /// 字典中所有键的集合 - [Obsolete("请直接使用 dictionary.Keys", false)] - public static IEnumerable GetKeys(this IDictionary dictionary) - { - return dictionary.Keys; - } - - /// - /// 返回字典中值的集合 - /// [Obsolete("请直接使用 dictionary.Values")] - /// - /// 要获取值的字典 - /// 字典中所有值的集合 - [Obsolete("请直接使用 dictionary.Values", false)] - public static IEnumerable GetValues(this IDictionary dictionary) - { - return dictionary.Values; - } /// /// 从字典中删除指定的键 diff --git a/EasyTool.Core/CollectionsCategory/IteratorUtil.cs b/EasyTool.Core/CollectionsCategory/IteratorUtil.cs deleted file mode 100644 index f4aa6e6..0000000 --- a/EasyTool.Core/CollectionsCategory/IteratorUtil.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EasyTool -{ - /// - /// 迭代器工具类 - /// - /// 注意:此类中的方法与 System.Linq 提供的功能高度相似。 - /// 对于新代码,建议优先使用 LINQ 标准查询运算符(如 Where、Select、Take、Skip、OrderBy、GroupBy 等)。 - /// 此类保留用于向后兼容和特定场景需求。 - /// - public static class IteratorUtil - { - /// - /// 将一个数组转换为一个迭代器 - /// - public static IEnumerable AsIterator(this T[] array) - { - foreach (var item in array) - { - yield return item; - } - } - - /// - /// 过滤掉一个迭代器中不符合条件的元素 - /// [Obsolete("请直接使用 source.Where(predicate) (LINQ)")] - /// - [Obsolete("请直接使用 source.Where(predicate) (LINQ)", false)] - public static IEnumerable Filter(this IEnumerable source, Func predicate) - { - foreach (var item in source) - { - if (predicate(item)) - { - yield return item; - } - } - } - - /// - /// 对一个迭代器中的每个元素进行转换 - /// [Obsolete("请直接使用 source.Select(selector) (LINQ)")] - /// - [Obsolete("请直接使用 source.Select(selector) (LINQ)", false)] - public static IEnumerable Map(this IEnumerable source, Func selector) - { - foreach (var item in source) - { - yield return selector(item); - } - } - - /// - /// 从一个迭代器中取出前 n 个元素 - /// [Obsolete("请直接使用 source.Take(count) (LINQ)")] - /// - [Obsolete("请直接使用 source.Take(count) (LINQ)", false)] - public static IEnumerable Take(this IEnumerable source, int count) - { - foreach (var item in source) - { - if (count-- > 0) - { - yield return item; - } - else - { - break; - } - } - } - - /// - /// 跳过一个迭代器中的前 n 个元素 - /// [Obsolete("请直接使用 source.Skip(count) (LINQ)")] - /// - [Obsolete("请直接使用 source.Skip(count) (LINQ)", false)] - public static IEnumerable Skip(this IEnumerable source, int count) - { - foreach (var item in source) - { - if (count-- > 0) - { - continue; - } - else - { - yield return item; - } - } - } - - /// - /// 将一个迭代器的元素分组 - /// [Obsolete("请直接使用 source.GroupBy(keySelector, x => x) (LINQ)")] - /// - [Obsolete("请直接使用 source.GroupBy(keySelector, x => x) (LINQ)", false)] - public static IEnumerable> GroupBy(this IEnumerable source, Func keySelector) - { - return source.GroupBy(keySelector, x => x); - } - - /// - /// 将一个迭代器的元素按照指定的方式分组 - /// - public static IEnumerable> GroupBy(this IEnumerable source, Func keySelector, Func elementSelector) - { - var dictionary = new Dictionary>(); - foreach (var item in source) - { - var key = keySelector(item); - var element = elementSelector(item); - if (!dictionary.ContainsKey(key)) - { - dictionary[key] = new List(); - } - dictionary[key].Add(element); - } - foreach (var group in dictionary) - { - yield return new Grouping(group.Key, group.Value); - } - } - - private class Grouping : IGrouping - { - private readonly List _elements; - - public Grouping(TKey key, List elements) - { - Key = key; - _elements = elements; - } - public TKey Key { get; } - - public IEnumerator GetEnumerator() - { - return _elements.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - /// - /// 对一个迭代器中的元素进行排序 - /// [Obsolete("请直接使用 source.OrderBy(keySelector) (LINQ)")] - /// - [Obsolete("请直接使用 source.OrderBy(keySelector) (LINQ)", false)] - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector) - { - return source.OrderBy(keySelector, Comparer.Default); - } - - /// - /// 对一个迭代器中的元素按照指定的方式进行排序 - /// - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer comparer) - { - return source.OrderBy(keySelector, comparer, false); - } - - /// - /// 对一个迭代器中的元素按照指定的方式进行排序,并指定排序方向 - /// - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer comparer, bool descending) - { - return descending ? source.OrderByDescending(keySelector, comparer) : source.OrderBy(keySelector, comparer); - } - - /// - /// 对一个迭代器中的元素进行倒序排序 - /// [Obsolete("请直接使用 source.OrderByDescending(keySelector) (LINQ)")] - /// - [Obsolete("请直接使用 source.OrderByDescending(keySelector) (LINQ)", false)] - public static IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector) - { - return source.OrderByDescending(keySelector, Comparer.Default); - } - - /// - /// 对一个迭代器中的元素按照指定的方式进行倒序排序 - /// - public static IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector, IComparer comparer) - { - return source.OrderByDescending(keySelector, comparer, false); - } - - /// - /// 对一个迭代器中的元素按照指定的方式进行倒序排序,并指定排序方向 - /// - public static IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector, IComparer comparer, bool descending) - { - return descending ? source.OrderBy(keySelector, comparer) : source.OrderByDescending(keySelector, comparer); - } - } -} diff --git a/EasyTool.Core/CollectionsCategory/LinkedListExtension.cs b/EasyTool.Core/CollectionsCategory/LinkedListExtension.cs deleted file mode 100644 index 3a1487a..0000000 --- a/EasyTool.Core/CollectionsCategory/LinkedListExtension.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool.Extension -{ - - /// - /// 双向链表工具类 - /// - public static class LinkedListExtension - { - /// - /// 将双向链表中的某个节点移动到链表的结尾处。 - /// - /// 双向链表元素类型 - /// 双向链表 - /// 要移动的节点 - public static void MoveLast(this LinkedList list, LinkedListNode node) => LinkedListUtil.MoveLast(list,node); - - /// - /// 将双向链表中移动到最前方 - /// - /// 双向链表元素类型 - /// 双向链表 - /// 要移动的节点 - public static void MoveFirst(this LinkedList list, LinkedListNode node) => LinkedListUtil.MoveFirst(list,node); - - } -} diff --git a/EasyTool.Core/CollectionsCategory/ListExtension.cs b/EasyTool.Core/CollectionsCategory/ListExtension.cs index 31af6e4..15cd2b4 100644 --- a/EasyTool.Core/CollectionsCategory/ListExtension.cs +++ b/EasyTool.Core/CollectionsCategory/ListExtension.cs @@ -5,8 +5,58 @@ namespace EasyTool.Extension { + /// + /// List 集合扩展方法 + /// public static class ListExtension - { + { + #region 列表合并 + + /// + /// 将指定的列表连接起来,形成一个新的列表。 + /// + /// 列表元素类型 + /// 要连接的列表 + /// 连接后的新列表 + public static List Concat(this IEnumerable> lists) + { + return lists.SelectMany(x => x).ToList(); + } + + /// + /// 将指定的列表连接起来,形成一个新的列表。 + /// + /// 列表元素类型 + /// 要连接的列表 + /// 连接后的新列表 + public static List Concat(this List list, params List[] lists) + { + return Concat(new[] { list }.Concat(lists)); + } + + #endregion + + #region 列表分页 + + /// + /// 将列表中的元素分页显示。 + /// + /// 列表元素类型 + /// 要分页的列表 + /// 每页显示的元素数量 + /// 要显示的页码,从 0 开始 + /// 指定页的元素列表 + public static List Page(this List list, int pageSize, int pageIndex) + { + return list.Skip(pageIndex * pageSize) + .Take(pageSize) + .ToList(); + } + + #endregion + + #region 列表比较 + /// /// 判断两个列表是否相等。 /// @@ -14,6 +64,33 @@ public static class ListExtension /// 要比较的第一个列表 /// 要比较的第二个列表 /// 如果两个列表相等,则返回 true;否则返回 false - public static bool Equals(this List list1, List list2)=> ListUtil.Equals(list1, list2); + public static bool Equals(this List? list1, List? list2) + { + if (list1 == null && list2 == null) + { + return true; + } + else if (list1 == null || list2 == null) + { + return false; + } + else if (list1.Count != list2.Count) + { + return false; + } + else + { + for (int i = 0; i < list1.Count; i++) + { + if (!EqualityComparer.Default.Equals(list1[i], list2[i])) + { + return false; + } + } + return true; + } + } + + #endregion } } diff --git a/EasyTool.Core/CollectionsCategory/ListUtil.cs b/EasyTool.Core/CollectionsCategory/ListUtil.cs deleted file mode 100644 index cb49685..0000000 --- a/EasyTool.Core/CollectionsCategory/ListUtil.cs +++ /dev/null @@ -1,272 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EasyTool -{ - public class ListUtil - { - /// - /// 在列表中查找元素,并返回其索引。如果未找到,则返回 -1。 - /// [Obsolete("请直接使用 list.IndexOf(item)")] - /// - /// 列表元素类型 - /// 要查找的列表 - /// 要查找的元素 - /// 元素在列表中的索引,如果未找到则返回 -1 - [Obsolete("请直接使用 list.IndexOf(item)", false)] - public static int IndexOf(List list, T item) - { - return list.IndexOf(item); - } - - /// - /// 向列表中添加多个元素。 - /// [Obsolete("请直接使用 list.AddRange(items)")] - /// - /// 列表元素类型 - /// 要添加元素的列表 - /// 要添加到列表中的元素 - [Obsolete("请直接使用 list.AddRange(items)", false)] - public static void AddRange(List list, IEnumerable items) - { - list.AddRange(items); - } - - /// - /// 在列表中删除指定索引处的元素。 - /// [Obsolete("请直接使用 list.RemoveAt(index)")] - /// - /// 列表元素类型 - /// 要删除元素的列表 - /// 要删除元素的索引 - [Obsolete("请直接使用 list.RemoveAt(index)", false)] - public static void RemoveAt(List list, int index) - { - list.RemoveAt(index); - } - - /// - /// 从列表中删除指定元素的第一个匹配项。 - /// [Obsolete("请直接使用 list.Remove(item)")] - /// - /// 列表元素类型 - /// 要删除元素的列表 - /// 要删除的元素 - /// 如果找到并成功删除元素,则返回 true;否则返回 false - [Obsolete("请直接使用 list.Remove(item)", false)] - public static bool Remove(List list, T item) - { - return list.Remove(item); - } - - /// - /// 将指定的列表连接起来,形成一个新的列表。 - /// - /// 列表元素类型 - /// 要连接的列表 - /// 连接后的新列表 - public static List Concat(IEnumerable> lists) - { - return lists.SelectMany(x => x).ToList(); - } - - /// - /// 将指定的列表连接起来,形成一个新的列表。 - /// - /// 列表元素类型 - /// 要连接的列表 - /// 连接后的新列表 - public static List Concat(params List[] lists) - { - return Concat((IEnumerable>)lists); - } - - /// - /// 返回一个新的列表,其中包含指定列表中的元素,但不包括重复元素。 - /// [Obsolete("请直接使用 list.Distinct().ToList() (LINQ)")] - /// - /// 列表元素类型 - /// 要去重的列表 - /// 去重后的新列表 - [Obsolete("请直接使用 list.Distinct().ToList() (LINQ)", false)] - public static List Distinct(List list) - { - return list.Distinct().ToList(); - } - - /// - /// 根据指定的条件筛选出列表中符合条件的元素。 - /// [Obsolete("请直接使用 list.Where(predicate).ToList() (LINQ)")] - /// - /// 列表元素类型 - /// 要筛选的列表 - /// 筛选条件 - /// 符合条件的元素列表 - [Obsolete("请直接使用 list.Where(predicate).ToList() (LINQ)", false)] - public static List Where(List list, Func predicate) - { - return list.Where(predicate).ToList(); - } - - /// - /// 将列表中的每个元素应用到指定的转换函数,并返回转换后的新列表。 - /// [Obsolete("请直接使用 list.Select(selector).ToList() (LINQ)")] - /// - /// 列表元素类型 - /// 转换后的元素类型 - /// 要转换的列表 - /// 转换函数 - /// 转换后的新列表 - [Obsolete("请直接使用 list.Select(selector).ToList() (LINQ)", false)] - public static List Select(List list, Func selector) - { - return list.Select(selector).ToList(); - } - - /// - /// 对列表中的每个元素应用指定的操作。 - /// [Obsolete("请直接使用 list.ForEach(action)")] - /// - /// 列表元素类型 - /// 要应用操作的列表 - /// 要应用的操作 - [Obsolete("请直接使用 list.ForEach(action)", false)] - public static void ForEach(List list, Action action) - { - list.ForEach(action); - } - - /// - /// 将列表中的元素排序。 - /// [Obsolete("请直接使用 list.Sort()")] - /// - /// 列表元素类型 - /// 要排序的列表 - [Obsolete("请直接使用 list.Sort()", false)] - public static void Sort(List list) - { - list.Sort(); - } - - /// - /// 将列表中的元素按指定的比较器排序。 - /// [Obsolete("请直接使用 list.Sort(comparer)")] - /// - /// 列表元素类型 - /// 要排序的列表 - /// 比较器 - [Obsolete("请直接使用 list.Sort(comparer)", false)] - public static void Sort(List list, IComparer comparer) - { - list.Sort(comparer); - } - - /// - /// 将列表中的元素分页显示。 - /// - /// 列表元素类型 - /// 要分页的列表 - /// 每页显示的元素数量 - /// 要显示的页码,从 0 开始 - /// 指定页的元素列表 - public static List Page(List list, int pageSize, int pageIndex) - { - return list.Skip(pageIndex * pageSize) - .Take(pageSize) - .ToList(); - } - - /// - /// 向列表中批量添加元素。 - /// [Obsolete("请直接使用 list.AddRange(items)")] - /// - /// 列表元素类型 - /// 要添加元素的列表 - /// 要添加到列表中的元素 - [Obsolete("请直接使用 list.AddRange(items)", false)] - public static void AddRange(List list, params T[] items) - { - list.AddRange(items); - } - - /// - /// 判断两个列表是否相等。 - /// - /// 列表元素类型 - /// 要比较的第一个列表 - /// 要比较的第二个列表 - /// 如果两个列表相等,则返回 true;否则返回 false - public static bool Equals(List list1, List list2) - { - if (list1 == null && list2 == null) - { - return true; - } - else if (list1 == null || list2 == null) - { - return false; - } - else if (list1.Count != list2.Count) - { - return false; - } - else - { - for (int i = 0; i < list1.Count; i++) - { - if (!EqualityComparer.Default.Equals(list1[i], list2[i])) - { - return false; - } - } - - return true; - } - } - - /// - /// 返回两个列表的交集。 - /// [Obsolete("请直接使用 list1.Intersect(list2).ToList() (LINQ)")] - /// - /// 列表元素类型 - /// 要比较的第一个列表 - /// 要比较的第二个列表 - /// 交集列表 - [Obsolete("请直接使用 list1.Intersect(list2).ToList() (LINQ)", false)] - public static List Intersect(List list1, List list2) - { - return list1.Intersect(list2).ToList(); - } - - /// - /// 返回两个列表的并集。 - /// [Obsolete("请直接使用 list1.Union(list2).ToList() (LINQ)")] - /// - /// 列表元素类型 - /// 要比较的第一个列表 - /// 要比较的第二个列表 - /// 并集列表 - [Obsolete("请直接使用 list1.Union(list2).ToList() (LINQ)", false)] - public static List Union(List list1, List list2) - { - return list1.Union(list2).ToList(); - } - - /// - /// 返回两个列表的差集。 - /// [Obsolete("请直接使用 list1.Except(list2).ToList() (LINQ)")] - /// - /// 列表元素类型 - /// 要比较的第一个列表 - /// 要比较的第二个列表 - /// 差集列表 - [Obsolete("请直接使用 list1.Except(list2).ToList() (LINQ)", false)] - public static List Except(List list1, List list2) - { - return list1.Except(list2).ToList(); - } - - } -} diff --git a/EasyTool.Core/CollectionsCategory/QueueExtension.cs b/EasyTool.Core/CollectionsCategory/QueueExtension.cs deleted file mode 100644 index 4f8dfc0..0000000 --- a/EasyTool.Core/CollectionsCategory/QueueExtension.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; - -namespace EasyTool.Extension -{ - /// - /// 队列工具类 - /// - public static class QueueExtension - { - - /// - /// 将指定集合中的元素添加到队列的末尾。 - /// - /// 队列元素类型 - /// 队列 - /// 要添加到队列中的集合 - public static void EnqueueRange(this Queue queue, IEnumerable collection) => QueueUtil.EnqueueRange(queue, collection); - - /// - /// 从队列中移除指定元素的第一个匹配项。 - /// - /// 队列元素类型 - /// 队列 - /// 要移除的元素 - /// 如果已成功移除元素,则为 true;否则为 false。 - public static bool Remove(this Queue queue, T item) => QueueUtil.Remove(queue, item); - - } -} diff --git a/EasyTool.Core/CollectionsCategory/StackExtension.cs b/EasyTool.Core/CollectionsCategory/StackExtension.cs deleted file mode 100644 index a1477f9..0000000 --- a/EasyTool.Core/CollectionsCategory/StackExtension.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EasyTool.CollectionsCategory -{ - public static class StackExtension - { - /// - /// 从堆栈中移除指定元素的第一个匹配项。 - /// - /// 堆栈元素类型 - /// 堆栈 - /// 要移除的元素 - /// 如果已成功移除元素,则为 true;否则为 false。 - public static bool Remove(this Stack stack, T item)=> StackUtil.Remove(stack, item); - } -} diff --git a/EasyTool.Core/ConvertCategory/ByteExtension.cs b/EasyTool.Core/ConvertCategory/ByteExtension.cs index 7208906..9a2be43 100644 --- a/EasyTool.Core/ConvertCategory/ByteExtension.cs +++ b/EasyTool.Core/ConvertCategory/ByteExtension.cs @@ -127,31 +127,6 @@ public static byte[] FromHexToBytes(this string hex) return bytes; } - /// - /// 将字节数组转换为Base64字符串 - /// [Obsolete("请直接使用 Convert.ToBase64String(bytes)")] - /// - [Obsolete("请直接使用 Convert.ToBase64String(bytes)", false)] - public static string ToBase64(this byte[] bytes) - { - if (bytes == null || bytes.Length == 0) - return string.Empty; - - return Convert.ToBase64String(bytes); - } - - /// - /// 从Base64字符串转换为字节数组 - /// [Obsolete("请直接使用 Convert.FromBase64String(base64)")] - /// - [Obsolete("请直接使用 Convert.FromBase64String(base64)", false)] - public static byte[] FromBase64ToBytes(this string base64) - { - if (string.IsNullOrWhiteSpace(base64)) - return Array.Empty(); - - return Convert.FromBase64String(base64); - } /// /// 将字节数组转换为二进制字符串 @@ -430,59 +405,6 @@ public static byte[] ToBytes(this short value) #region 字节数组编码解码 - /// - /// 将字节数组按UTF-8编码转换为字符串 - /// [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)")] - /// - [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)", false)] - public static string ToUtf8String(this byte[] bytes) - { - if (bytes == null || bytes.Length == 0) - return string.Empty; - - return Encoding.UTF8.GetString(bytes); - } - - /// - /// 将字节数组按指定编码转换为字符串 - /// [Obsolete("请直接使用 encoding.GetString(bytes)")] - /// - [Obsolete("请直接使用 encoding.GetString(bytes)", false)] - public static string ToString(this byte[] bytes, Encoding encoding) - { - if (bytes == null || bytes.Length == 0) - return string.Empty; - - encoding ??= Encoding.UTF8; - return encoding.GetString(bytes); - } - - /// - /// 将字符串按UTF-8编码转换为字节数组 - /// [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)")] - /// - [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)", false)] - public static byte[] ToUtf8Bytes(this string str) - { - if (string.IsNullOrEmpty(str)) - return Array.Empty(); - - return Encoding.UTF8.GetBytes(str); - } - - /// - /// 将字符串按指定编码转换为字节数组 - /// [Obsolete("请直接使用 encoding.GetBytes(str)")] - /// - [Obsolete("请直接使用 encoding.GetBytes(str)", false)] - public static byte[] ToBytes(this string str, Encoding encoding) - { - if (string.IsNullOrEmpty(str)) - return Array.Empty(); - - encoding ??= Encoding.UTF8; - return encoding.GetBytes(str); - } #endregion diff --git a/EasyTool.Core/ConvertCategory/ConvertExtension.cs b/EasyTool.Core/ConvertCategory/ConvertExtension.cs index 907c9bf..1402df8 100644 --- a/EasyTool.Core/ConvertCategory/ConvertExtension.cs +++ b/EasyTool.Core/ConvertCategory/ConvertExtension.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; @@ -203,16 +203,6 @@ public static string ToIntString(this bool b) return b ? "1" : "0"; } - /// - /// 布尔值转换为整数1或者0 - /// [Obsolete("请直接使用 Convert.ToInt32(b)")] - /// - [Obsolete("请直接使用 Convert.ToInt32(b)", false)] - public static int ToInt(this bool b) - { - return b ? 1 : 0; - } - /// /// 布尔值转换为中文 /// @@ -268,21 +258,6 @@ public static string ToHex(this byte[] bytes, bool lowerCase = true) return bytes; } - /// - /// 转换为Base64 - /// [Obsolete("请直接使用 Convert.ToBase64String(bytes)")] - /// - /// - /// - [Obsolete("请直接使用 Convert.ToBase64String(bytes)", false)] - public static string ToBase64(this byte[] bytes) - { - if (bytes == null) - return string.Empty; - - return Convert.ToBase64String(bytes); - } - /// @@ -313,26 +288,6 @@ public static DateTime TimestampToDateTime(this string timeStamp) return dd.Add(ts); } - /// - /// 字符串转Guid - /// [Obsolete("请直接使用 Guid.TryParse(guid, out var result) 或 new Guid(guid)")] - /// - /// - /// - [Obsolete("请直接使用 Guid.TryParse(guid, out var result)", false)] - public static Guid? ToGuid(this string guid) - { - try - { - return new Guid(guid); - } - catch (Exception) - { - - throw; - } - } - #endregion #region 数字转字符串前面补零 diff --git a/EasyTool.Core/ConvertCategory/ConvertUtil.cs b/EasyTool.Core/ConvertCategory/ConvertUtil.cs deleted file mode 100644 index 015812a..0000000 --- a/EasyTool.Core/ConvertCategory/ConvertUtil.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; - -namespace EasyTool -{ - /// - /// 类型转换工具类 - /// - public static class ConvertUtil - { - /// - /// 将对象转换为指定类型,转换失败返回指定类型的默认值 - /// - public static T? To(object? value) - { - try - { - return (T)Convert.ChangeType(value, typeof(T))!; - } - catch - { - return default; - } - } - - /// - /// 将字符串转换为整型,转换失败返回0 - /// - public static int ToInt32(string? value) - { - int result; - if (int.TryParse(value, out result)) - { - return result; - } - return 0; - } - - /// - /// 将字符串转换为长整型,转换失败返回0 - /// - public static long ToInt64(string? value) - { - long result; - if (long.TryParse(value, out result)) - { - return result; - } - return 0; - } - - /// - /// 将字符串转换为布尔型,转换失败返回默认值,默认值false - /// - public static bool ToBoolean(string? data, bool defValue = false) - { - //如果为空则返回默认值 - if (string.IsNullOrEmpty(data)) - { - return defValue; - } - - bool temp = false; - if (bool.TryParse(data, out temp)) - { - return temp; - } - else - { - return defValue; - } - } - - /// - /// 将对象转换为布尔型,转换失败返回默认值,默认值false - /// - public static bool ToBoolean(object? data, bool defValue = false) - { - //如果为空则返回默认值 - if (data == null || Convert.IsDBNull(data)) - { - return defValue; - } - - try - { - return Convert.ToBoolean(data); - } - catch - { - return defValue; - } - } - - /// - /// 将字符串转换为单精度浮点型,转换失败返回0 - /// - public static float ToSingle(string? value) - { - float result; - if (float.TryParse(value, out result)) - { - return result; - } - return 0; - } - - /// - /// 将字符串转换为双精度浮点型,转换失败返回0 - /// - public static double ToDouble(string? value) - { - double result; - if (double.TryParse(value, out result)) - { - return result; - } - return 0; - } - - /// - /// 将字符串转换为十进制数,转换失败返回0 - /// - public static decimal ToDecimal(string? value) - { - decimal result; - if (decimal.TryParse(value, out result)) - { - return result; - } - return 0; - } - - /// - /// 将字符串转换为日期时间,转换失败返回DateTime.MinValue - /// - public static DateTime ToDateTime(string? value) - { - DateTime result; - if (DateTime.TryParse(value, out result)) - { - return result; - } - return DateTime.MinValue; - } - - /// - /// 将字符串转换为枚举类型,转换失败返回默认值 - /// - public static T ToEnum(string? value, T? defaultValue = default) where T : struct - { - T result; - if (Enum.TryParse(value, out result)) - { - return result; - } - return defaultValue ?? default; - } - - - } -} diff --git a/EasyTool.Core/ConvertCategory/NumberExtension.cs b/EasyTool.Core/ConvertCategory/NumberExtension.cs index 128b7b8..a9cae99 100644 --- a/EasyTool.Core/ConvertCategory/NumberExtension.cs +++ b/EasyTool.Core/ConvertCategory/NumberExtension.cs @@ -289,139 +289,11 @@ public static double Cube(this double value) return value * value * value; } - /// - /// 计算数值的绝对值 - /// [Obsolete("请直接使用 Math.Abs(value)")] - /// - [Obsolete("请直接使用 Math.Abs(value)", false)] - public static int Abs(this int value) - { - return Math.Abs(value); - } - - /// - /// 计算数值的绝对值 - /// [Obsolete("请直接使用 Math.Abs(value)")] - /// - [Obsolete("请直接使用 Math.Abs(value)", false)] - public static long Abs(this long value) - { - return Math.Abs(value); - } - - /// - /// 计算数值的绝对值 - /// [Obsolete("请直接使用 Math.Abs(value)")] - /// - [Obsolete("请直接使用 Math.Abs(value)", false)] - public static float Abs(this float value) - { - return Math.Abs(value); - } - - /// - /// 计算数值的绝对值 - /// [Obsolete("请直接使用 Math.Abs(value)")] - /// - [Obsolete("请直接使用 Math.Abs(value)", false)] - public static double Abs(this double value) - { - return Math.Abs(value); - } - - /// - /// 计算数值的绝对值 - /// [Obsolete("请直接使用 Math.Abs(value)")] - /// - [Obsolete("请直接使用 Math.Abs(value)", false)] - public static decimal Abs(this decimal value) - { - return Math.Abs(value); - } #endregion #region 数值判断 - /// - /// 判断浮点数是否为 NaN - /// [Obsolete("请直接使用 float.IsNaN(value)")] - /// - [Obsolete("请直接使用 float.IsNaN(value)", false)] - public static bool IsNaN(this float value) - { - return float.IsNaN(value); - } - - /// - /// 判断双精度浮点数是否为 NaN - /// [Obsolete("请直接使用 double.IsNaN(value)")] - /// - [Obsolete("请直接使用 double.IsNaN(value)", false)] - public static bool IsNaN(this double value) - { - return double.IsNaN(value); - } - - /// - /// 判断浮点数是否为无穷大 - /// [Obsolete("请直接使用 float.IsInfinity(value)")] - /// - [Obsolete("请直接使用 float.IsInfinity(value)", false)] - public static bool IsInfinity(this float value) - { - return float.IsInfinity(value); - } - - /// - /// 判断双精度浮点数是否为无穷大 - /// [Obsolete("请直接使用 double.IsInfinity(value)")] - /// - [Obsolete("请直接使用 double.IsInfinity(value)", false)] - public static bool IsInfinity(this double value) - { - return double.IsInfinity(value); - } - - /// - /// 判断浮点数是否为正无穷大 - /// [Obsolete("请直接使用 float.IsPositiveInfinity(value)")] - /// - [Obsolete("请直接使用 float.IsPositiveInfinity(value)", false)] - public static bool IsPositiveInfinity(this float value) - { - return float.IsPositiveInfinity(value); - } - - /// - /// 判断双精度浮点数是否为正无穷大 - /// [Obsolete("请直接使用 double.IsPositiveInfinity(value)")] - /// - [Obsolete("请直接使用 double.IsPositiveInfinity(value)", false)] - public static bool IsPositiveInfinity(this double value) - { - return double.IsPositiveInfinity(value); - } - - /// - /// 判断浮点数是否为负无穷大 - /// [Obsolete("请直接使用 float.IsNegativeInfinity(value)")] - /// - [Obsolete("请直接使用 float.IsNegativeInfinity(value)", false)] - public static bool IsNegativeInfinity(this float value) - { - return float.IsNegativeInfinity(value); - } - - /// - /// 判断双精度浮点数是否为负无穷大 - /// [Obsolete("请直接使用 double.IsNegativeInfinity(value)")] - /// - [Obsolete("请直接使用 double.IsNegativeInfinity(value)", false)] - public static bool IsNegativeInfinity(this double value) - { - return double.IsNegativeInfinity(value); - } #endregion diff --git a/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs b/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs index bbf5b8d..bad196b 100644 --- a/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs +++ b/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs @@ -15,6 +15,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在周的第一天的日期。 + [Obsolete("请直接使用 DateTimeUtil.GetFirstDayOfWeek(date)")] public static DateTime GetFirstDayOfWeek(this DateTime date) => DateTimeUtil.GetFirstDayOfWeek(date); /// @@ -22,6 +23,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在月份的第一天的日期。 + [Obsolete("请直接使用 DateTimeUtil.GetFirstDayOfMonth(date)")] public static DateTime GetFirstDayOfMonth(this DateTime date) => DateTimeUtil.GetFirstDayOfMonth(date); @@ -30,6 +32,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在季度的第一天的日期。 + [Obsolete("请直接使用 DateTimeUtil.GetFirstDayOfQuarter(date)")] public static DateTime GetFirstDayOfQuarter(this DateTime date) => DateTimeUtil.GetFirstDayOfQuarter(date); /// @@ -37,6 +40,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在年份的第一天的日期。 + [Obsolete("请直接使用 DateTimeUtil.GetFirstDayOfYear(date)")] public static DateTime GetFirstDayOfYear(this DateTime date) => DateTimeUtil.GetFirstDayOfYear(date); /// @@ -44,6 +48,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期和当前日期之间的天数差。 + [Obsolete("请直接使用 DateTimeUtil.GetDaysBetween(date)")] public static int GetDaysBetween(this DateTime date) => DateTimeUtil.GetDaysBetween(date); /// @@ -52,6 +57,7 @@ public static class DateTimeExtension /// 第一个日期。 /// 第二个日期。 /// 两个日期之间的天数差。 + [Obsolete("请直接使用 DateTimeUtil.GetDaysBetween(date1, date2)")] public static int GetDaysBetween(this DateTime date1, DateTime date2) => DateTimeUtil.GetDaysBetween(date1, date2); /// @@ -59,6 +65,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期和当前日期之间的工作日数差。 + [Obsolete("请直接使用 DateTimeUtil.GetWorkDaysBetween(date)")] public static int GetWorkDaysBetween(this DateTime date) => DateTimeUtil.GetWorkDaysBetween(date); /// @@ -67,6 +74,7 @@ public static class DateTimeExtension /// 第一个日期。 /// 第二个日期。 /// 两个日期之间的工作日数差。 + [Obsolete("请直接使用 DateTimeUtil.GetWorkDaysBetween(date1, date2)")] public static int GetWorkDaysBetween(this DateTime date1, DateTime date2) => DateTimeUtil.GetWorkDaysBetween(date1, date2); /// @@ -74,6 +82,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 如果是工作日,则返回 true;否则返回 false。 + [Obsolete("请直接使用 DateTimeUtil.IsWorkDay(date)")] public static bool IsWorkDay(this DateTime date) => DateTimeUtil.IsWorkDay(date); /// @@ -81,6 +90,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在周的所有日期。 + [Obsolete("请直接使用 DateTimeUtil.GetWeekDays(date)")] public static List GetWeekDays(this DateTime date) => DateTimeUtil.GetWeekDays(date); /// @@ -88,6 +98,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在月份的所有日期。 + [Obsolete("请直接使用 DateTimeUtil.GetMonthDays(date)")] public static List GetMonthDays(this DateTime date) => DateTimeUtil.GetMonthDays(date); /// @@ -95,6 +106,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在季度的所有日期。 + [Obsolete("请直接使用 DateTimeUtil.GetQuarterDays(date)")] public static List GetQuarterDays(this DateTime date) => DateTimeUtil.GetQuarterDays(date); /// @@ -102,6 +114,7 @@ public static class DateTimeExtension /// /// 指定日期。 /// 指定日期所在年份的所有日期。 + [Obsolete("请直接使用 DateTimeUtil.GetYearDays(date)")] public static List GetYearDays(this DateTime date) => DateTimeUtil.GetYearDays(date); @@ -182,45 +195,6 @@ public static int ToAge(this DateTime birthDate) return age; } - /// - /// 将日期转换为 Unix 时间戳(秒) - /// [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeSeconds()")] - /// - [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeSeconds()", false)] - public static long ToUnixTimestamp(this DateTime date) - { - return new DateTimeOffset(date).ToUnixTimeSeconds(); - } - - /// - /// 将日期转换为 Unix 时间戳(毫秒) - /// [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeMilliseconds()")] - /// - [Obsolete("请使用 new DateTimeOffset(date).ToUnixTimeMilliseconds()", false)] - public static long ToUnixTimestampMilliseconds(this DateTime date) - { - return new DateTimeOffset(date).ToUnixTimeMilliseconds(); - } - - /// - /// 从 Unix 时间戳(秒)转换为日期 - /// [Obsolete("请使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).LocalDateTime")] - /// - [Obsolete("请使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).LocalDateTime", false)] - public static DateTime FromUnixTimestamp(this long timestamp) - { - return DateTimeOffset.FromUnixTimeSeconds(timestamp).LocalDateTime; - } - - /// - /// 从 Unix 时间戳(毫秒)转换为日期 - /// [Obsolete("请使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime")] - /// - [Obsolete("请使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime", false)] - public static DateTime FromUnixTimestampMilliseconds(this long timestamp) - { - return DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime; - } /// /// 判断是否是周末(周六或周日) @@ -301,15 +275,6 @@ public static string ToChineseWeekDayShort(this DateTime date) }; } - /// - /// 判断是否是闰年 - /// [Obsolete("请直接使用 DateTime.IsLeapYear(date.Year)")] - /// - [Obsolete("请直接使用 DateTime.IsLeapYear(date.Year)", false)] - public static bool IsLeapYear(this DateTime date) - { - return DateTime.IsLeapYear(date.Year); - } /// /// 获取日期所在季度的数字(1-4) diff --git a/EasyTool.Core/DateTimeCategory/TimerUtil.cs b/EasyTool.Core/DateTimeCategory/TimerUtil.cs index e87f551..afd9082 100644 --- a/EasyTool.Core/DateTimeCategory/TimerUtil.cs +++ b/EasyTool.Core/DateTimeCategory/TimerUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; namespace EasyTool @@ -40,19 +40,6 @@ public static TimeSpan GetElapsedTime() return DateTime.Now - _startTime; } - /// - /// 创建一个新的 Stopwatch 并启动计时。 - /// [Obsolete("请直接使用 Stopwatch.StartNew()")] - /// - /// 一个新的 Stopwatch。 - [Obsolete("请直接使用 Stopwatch.StartNew()", false)] - public static Stopwatch StartNew() - { - Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); - return stopwatch; - } - /// /// 计算指定操作的执行时间。 /// @@ -100,17 +87,6 @@ public static void MeasureAndLog(Action action, string fileName) System.IO.File.AppendAllText(fileName, $"{DateTime.Now}: {elapsedTime.TotalMilliseconds}ms{Environment.NewLine}"); } - /// - /// 等待指定的时间 - /// [Obsolete("请直接使用 Thread.Sleep(milliseconds)")] - /// - /// 要等待的毫秒数。 - [Obsolete("请直接使用 Thread.Sleep(milliseconds)", false)] - public static void Wait(int milliseconds) - { - System.Threading.Thread.Sleep(milliseconds); - } - /// /// 计算两个时间的时间间隔。 /// diff --git a/EasyTool.Core/DateTimeCategory/TimestampUtil.cs b/EasyTool.Core/DateTimeCategory/TimestampUtil.cs deleted file mode 100644 index 6c7912d..0000000 --- a/EasyTool.Core/DateTimeCategory/TimestampUtil.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; - -namespace EasyTool -{ - /// - /// 时间戳处理工具类 - /// - public static class TimestampUtil - { - /// - /// 获取当前时间戳(毫秒级) - /// [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()")] - /// - /// 当前时间戳(毫秒级) - [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()", false)] - public static long GetCurrentTimestamp() - { - DateTime dt = DateTime.UtcNow; - TimeSpan ts = dt - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - return (long)ts.TotalMilliseconds; - } - - /// - /// 将时间戳(毫秒级)转换为 DateTime 类型 - /// [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime")] - /// - /// 时间戳(毫秒级) - /// 转换后的 DateTime 类型 - [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime", false)] - public static DateTime ConvertToDateTime(long timestamp) - { - DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - return dt.AddMilliseconds(timestamp); - } - - /// - /// 将 DateTime 类型转换为时间戳(毫秒级) - /// [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeMilliseconds()")] - /// - /// DateTime 类型 - /// 转换后的时间戳(毫秒级) - [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeMilliseconds()", false)] - public static long ConvertToTimestamp(DateTime dateTime) - { - TimeSpan ts = dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - return (long)ts.TotalMilliseconds; - } - - /// - /// 获取当前时间戳(秒级) - /// [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeSeconds()")] - /// - /// 当前时间戳(秒级) - [Obsolete("请直接使用 DateTimeOffset.UtcNow.ToUnixTimeSeconds()", false)] - public static long GetCurrentTimestampSeconds() - { - return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; - } - - /// - /// 将时间戳(秒级)转换为 DateTime 类型 - /// [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime")] - /// - /// 时间戳(秒级) - /// 转换后的 DateTime 类型 - [Obsolete("请直接使用 DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime", false)] - public static DateTime ConvertToDateTimeSeconds(long timestamp) - { - return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp); - } - - /// - /// 将 DateTime 类型转换为时间戳(秒级) - /// [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeSeconds()")] - /// - /// DateTime 类型 - /// 转换后的时间戳(秒级) - [Obsolete("请直接使用 new DateTimeOffset(dateTime).ToUnixTimeSeconds()", false)] - public static long ConvertToTimestampSeconds(DateTime dateTime) - { - return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; - } - } -} diff --git a/EasyTool.Core/IOCategory/FileSystemExtension.cs b/EasyTool.Core/IOCategory/FileSystemExtension.cs index 2cd7865..5a5c5ca 100644 --- a/EasyTool.Core/IOCategory/FileSystemExtension.cs +++ b/EasyTool.Core/IOCategory/FileSystemExtension.cs @@ -170,65 +170,6 @@ public static FileInfo CopyToDirectory(this FileInfo file, string targetDirector return new FileInfo(targetPath); } - /// - /// 读取文件的所有文本内容 - /// [Obsolete("请直接使用 File.ReadAllText(file.FullName)")] - /// - [Obsolete("请直接使用 File.ReadAllText(file.FullName)", false)] - public static string ReadAllText(this FileInfo file) - { - if (file == null || !file.Exists) - return string.Empty; - - return File.ReadAllText(file.FullName); - } - - /// - /// 读取文件的所有字节 - /// [Obsolete("请直接使用 File.ReadAllBytes(file.FullName)")] - /// - [Obsolete("请直接使用 File.ReadAllBytes(file.FullName)", false)] - public static byte[] ReadAllBytes(this FileInfo file) - { - if (file == null || !file.Exists) - return Array.Empty(); - - return File.ReadAllBytes(file.FullName); - } - - /// - /// 写入文本内容到文件 - /// [Obsolete("请直接使用 File.WriteAllText(file.FullName, content)")] - /// - [Obsolete("请直接使用 File.WriteAllText(file.FullName, content)", false)] - public static void WriteAllText(this FileInfo file, string content) - { - if (file == null) - throw new ArgumentNullException(nameof(file)); - - // 确保目录存在 - if (!file.Directory.Exists) - file.Directory.Create(); - - File.WriteAllText(file.FullName, content); - } - - /// - /// 写入字节到文件 - /// [Obsolete("请直接使用 File.WriteAllBytes(file.FullName, content)")] - /// - [Obsolete("请直接使用 File.WriteAllBytes(file.FullName, content)", false)] - public static void WriteAllBytes(this FileInfo file, byte[] content) - { - if (file == null) - throw new ArgumentNullException(nameof(file)); - - // 确保目录存在 - if (!file.Directory.Exists) - file.Directory.Create(); - - File.WriteAllBytes(file.FullName, content); - } #endregion diff --git a/EasyTool.Core/IOCategory/FileTypeExtension.cs b/EasyTool.Core/IOCategory/FileTypeExtension.cs index 3216dda..f8a5d0b 100644 --- a/EasyTool.Core/IOCategory/FileTypeExtension.cs +++ b/EasyTool.Core/IOCategory/FileTypeExtension.cs @@ -5,11 +5,14 @@ namespace EasyTool.Extension { + /// + /// 文件类型扩展方法 + /// public static class FileTypeExtension { /// /// 文件流头部信息获得文件类型 - /// + /// /// 说明: /// 1、无法识别类型默认按照扩展名识别 /// 2、xls、doc、msi、ppt、vsd头信息无法区分,按照扩展名区分 @@ -17,6 +20,63 @@ public static class FileTypeExtension /// /// 文件 /// 类型,文件的扩展名,未找到为null - public static string GetType(this FileInfo file) => FileTypeUtil.GetType(file); + public static string GetFileType(this FileInfo file) + { + byte[] buffer = new byte[256]; + using (FileStream fs = file.OpenRead()) + { + int readLength = fs.Read(buffer, 0, buffer.Length); + if (readLength < buffer.Length) + { + // 处理读取不足的情况,虽然对于头部检测通常前几个字节就够了,但为了严谨性 + } + } + + string header = ""; + for (int i = 0; i < buffer.Length; i++) + { + header += buffer[i].ToString(); + } + + string? type = null; + switch (header) + { + case "255216": // jpg + type = ".jpg"; + break; + case "13780": // png + type = ".png"; + break; + case "7173": // gif + type = ".gif"; + break; + case "6677": // bmp + type = ".bmp"; + break; + case "7790": // exe dll + type = ".exe"; + break; + case "6063": // xml + type = ".xml"; + break; + case "6033": // htm html + type = ".html"; + break; + case "4742": // js + type = ".js"; + break; + case "5144": // txt + type = ".txt"; + break; + default: + case "8297": // rar + case "8075": // zip + case "D0CF11E0": // doc xls ppt vsd + type = file.Extension; + break; + } + + return type; + } } } diff --git a/EasyTool.Core/IOCategory/FileTypeUtil.cs b/EasyTool.Core/IOCategory/FileTypeUtil.cs deleted file mode 100644 index 09b077b..0000000 --- a/EasyTool.Core/IOCategory/FileTypeUtil.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace EasyTool -{ - /// - /// 文件类型判断工具类 - /// - public class FileTypeUtil - { - /// - /// 文件流头部信息获得文件类型 - /// - /// 说明: - /// 1、无法识别类型默认按照扩展名识别 - /// 2、xls、doc、msi、ppt、vsd头信息无法区分,按照扩展名区分 - /// 3、zip可能为docx、xlsx、pptx、jar、war头信息无法区分,按照扩展名区分 - /// - /// 文件 - /// 类型,文件的扩展名,未找到为null - public static string GetType(FileInfo file) - { - byte[] buffer = new byte[256]; - using (FileStream fs = file.OpenRead()) - { - int readLength = fs.Read(buffer, 0, buffer.Length); - if (readLength < buffer.Length) - { - // 处理读取不足的情况,虽然对于头部检测通常前几个字节就够了,但为了严谨性 - } - } - - string header = ""; - for (int i = 0; i < buffer.Length; i++) - { - header += buffer[i].ToString(); - } - - string? type = null; - switch (header) - { - case "255216": // jpg - type = ".jpg"; - break; - case "13780": // png - type = ".png"; - break; - case "7173": // gif - type = ".gif"; - break; - case "6677": // bmp - type = ".bmp"; - break; - case "7790": // exe dll - type = ".exe"; - break; - case "6063": // xml - type = ".xml"; - break; - case "6033": // htm html - type = ".html"; - break; - case "4742": // js - type = ".js"; - break; - case "5144": // txt - type = ".txt"; - break; - default: - case "8297": // rar - case "8075": // zip - case "D0CF11E0": // doc xls ppt vsd - type = file.Extension; - break; - } - - return type; - } - } -} diff --git a/EasyTool.Core/IOCategory/FileUtil.cs b/EasyTool.Core/IOCategory/FileUtil.cs index 857d294..9104d26 100644 --- a/EasyTool.Core/IOCategory/FileUtil.cs +++ b/EasyTool.Core/IOCategory/FileUtil.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Text; using System.Web; +using EasyTool.Extension; namespace EasyTool { @@ -302,24 +303,6 @@ public static FileInfo Touch(string path) } - /// - /// 拷贝文件 - /// [Obsolete("请直接使用 File.Copy(src, dest)")] - /// - /// 源文件路径 - /// 目标文件路径 - [Obsolete("请直接使用 File.Copy(src, dest)", false)] - public static void Cp(string src, string dest) - { - try - { - File.Copy(src, dest); - } - catch (Exception ex) - { - throw new Exception($"拷贝文件 {src} 到 {dest} 失败:{ex.Message}", ex); - } - } /// /// 复制文件或目录 @@ -398,24 +381,6 @@ public static bool Copy(string src, string dest, bool isOverride) } } - /// - /// 移动文件或重命名文件 - /// [Obsolete("请直接使用 File.Move(src, dest)")] - /// - /// 源文件路径 - /// 目标文件路径 - [Obsolete("请直接使用 File.Move(src, dest)", false)] - public static void Mv(string src, string dest) - { - try - { - File.Move(src, dest); - } - catch (Exception ex) - { - throw new Exception($"移动/重命名文件 {src} 到 {dest} 失败:{ex.Message}", ex); - } - } /// /// 移动文件或者目录 @@ -539,22 +504,6 @@ public static FileInfo Rename(FileInfo file, string newName, bool isRetainExt, b } } - /// - /// 获取绝对路径 - /// [Obsolete("请直接使用 Path.GetFullPath(path)")] - /// - /// 相对路径 - /// 绝对路径 - [Obsolete("请直接使用 Path.GetFullPath(path)", false)] - public static string GetAbsolutePath(string path) - { - if (!Path.IsPathRooted(path)) - { - path = Path.Combine(Directory.GetCurrentDirectory(), path); - } - - return Path.GetFullPath(path); - } /// /// 判断给定路径是否是绝对路径 @@ -770,76 +719,7 @@ public static string SubPath(string dirPath, string filePath) return filePath.Substring(startIndex); } - /// - /// 删除文件 - /// [Obsolete("请直接使用 File.Delete(path)")] - /// - /// 文件路径 - [Obsolete("请直接使用 File.Delete(path)", false)] - public static void Rm(string path) - { - try - { - File.Delete(path); - } - catch (Exception ex) - { - throw new Exception($"删除文件 {path} 失败:{ex.Message}"); - } - } - /// - /// 创建目录 - /// [Obsolete("请直接使用 Directory.CreateDirectory(path)")] - /// - /// 目录路径 - [Obsolete("请直接使用 Directory.CreateDirectory(path)", false)] - public static void Mkdir(string path) - { - try - { - Directory.CreateDirectory(path); - } - catch (Exception ex) - { - throw new Exception($"创建目录 {path} 失败:{ex.Message}", ex); - } - } - - /// - /// 删除目录 - /// [Obsolete("请直接使用 Directory.Delete(path)")] - /// - /// 目录路径 - [Obsolete("请直接使用 Directory.Delete(path)", false)] - public static void Rmdir(string path) - { - try - { - Directory.Delete(path); - Console.WriteLine($"目录 {path} 已成功删除"); - } - catch (Exception ex) - { - throw new Exception($"删除目录 {path} 失败:{ex.Message}",ex); - } - } - - /// - /// 获取文件名 - /// [Obsolete("请直接使用 file?.Name")] - /// - /// 文件 - /// 文件名 - [Obsolete("请直接使用 file?.Name", false)] - public static string GetFileName(FileInfo file) - { - if (file == null) - { - return null; - } - return file.Name; - } /// /// 获取文件名 @@ -938,7 +818,8 @@ public static string GetFilePrefix(string filePath) /// 类型,文件的扩展名,未找到为null public static string? GetType(FileInfo file) { - return FileTypeUtil.GetType(file); + // 通过文件头部获取类型 + return file.GetFileType(); } /// @@ -1158,27 +1039,6 @@ public static string ReadString(Uri url, Encoding? encoding = null) return result; } - /// - /// 从文件中读取每一行数据 - /// [Obsolete("请直接使用 File.ReadAllLines(path, encoding)")] - /// - /// 文件路径 - /// 编码格式,默认为UTF-8 - /// - [Obsolete("请直接使用 File.ReadAllLines(path, encoding)", false)] - public static string[] ReadAllLines(string path, Encoding? encoding = null) - { - // 如果未指定编码格式,则默认为 UTF-8 - if (encoding == null) - { - encoding = Encoding.UTF8; - } - - // 读取文件所有行数据 - string[] lines = File.ReadAllLines(path, encoding); - - return lines; - } /// @@ -1208,16 +1068,6 @@ public static Stream GetOutputStream(string path) } - /// - /// 获取当前系统的换行分隔符 - /// [Obsolete("请直接使用 Environment.NewLine")] - /// - /// 换行分隔符 - [Obsolete("请直接使用 Environment.NewLine", false)] - public static string GetLineSeparator() - { - return Environment.NewLine; - } /// /// 将string写入文件,覆盖模式 diff --git a/EasyTool.Core/IOCategory/IoUtil.cs b/EasyTool.Core/IOCategory/IoUtil.cs deleted file mode 100644 index d2a647f..0000000 --- a/EasyTool.Core/IOCategory/IoUtil.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Text; - -namespace EasyTool -{ - /// - /// Io流处理工具类 - /// - public static class IoUtil - { - /// - /// 读取文件的所有行到一个字符串数组中 - /// [Obsolete("请直接使用 File.ReadAllLines(path)")] - /// - /// 文件路径 - /// 字符串数组,其中包含文件的所有行。 - [Obsolete("请直接使用 File.ReadAllLines(path)", false)] - public static string[] ReadAllLines(string path) - { - return File.ReadAllLines(path); - } - - /// - /// 将字符串数组写入文件,覆盖原有内容 - /// [Obsolete("请直接使用 File.WriteAllLines(path, lines)")] - /// - /// 文件路径 - /// 待写入的字符串数组 - [Obsolete("请直接使用 File.WriteAllLines(path, lines)", false)] - public static void WriteAllLines(string path, string[] lines) - { - File.WriteAllLines(path, lines); - } - - /// - /// 读取整个文件到一个字符串中 - /// [Obsolete("请直接使用 File.ReadAllText(path)")] - /// - /// 文件路径 - /// 文件的所有内容 - [Obsolete("请直接使用 File.ReadAllText(path)", false)] - public static string ReadAllText(string path) - { - return File.ReadAllText(path); - } - - /// - /// 将字符串写入文件,覆盖原有内容 - /// [Obsolete("请直接使用 File.WriteAllText(path, text)")] - /// - /// 文件路径 - /// 待写入的字符串 - [Obsolete("请直接使用 File.WriteAllText(path, text)", false)] - public static void WriteAllText(string path, string text) - { - File.WriteAllText(path, text); - } - - /// - /// 读取二进制数据到一个字节数组中 - /// [Obsolete("请直接使用 File.ReadAllBytes(path)")] - /// - /// 文件路径 - /// - [Obsolete("请直接使用 File.ReadAllBytes(path)", false)] - public static byte[] ReadAllBytes(string path) - { - return File.ReadAllBytes(path); - } - - /// - /// 将字节数组写入二进制文件,覆盖原有内容 - /// [Obsolete("请直接使用 File.WriteAllBytes(path, bytes)")] - /// - /// 文件路径 - /// 待写入的字节数组 - [Obsolete("请直接使用 File.WriteAllBytes(path, bytes)", false)] - public static void WriteAllBytes(string path, byte[] bytes) - { - File.WriteAllBytes(path, bytes); - } - - /// - /// 读取指定 URL 的文本内容 - /// - /// URL 地址 - /// URL 返回的文本内容 - public static string ReadUrl(string url) - { - WebClient client = new WebClient(); - return client.DownloadString(url); - } - - /// - /// 将字符串写入指定 URL - /// - /// URL 地址 - /// 待写入的字符串 - public static void WriteUrl(string url, string text) - { - WebClient client = new WebClient(); - client.UploadString(url, text); - } - - /// - /// 读取网络流到一个字符串中 - /// - /// 网络流 - /// 网络流的所有内容 - public static string ReadStream(Stream stream) - { - using (StreamReader reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - } - - /// - /// 将字符串写入网络流 - /// - /// 网络流 - /// 待写入的字符串 - public static void WriteStream(Stream stream, string text) - { - using (StreamWriter writer = new StreamWriter(stream)) - { - writer.Write(text); - } - } - - /// - /// 读取二进制数据到一个内存流中 - /// - /// 二进制数据 - /// 内存流,其中包含输入的二进制数据 - public static MemoryStream ReadMemoryStream(byte[] bytes) - { - return new MemoryStream(bytes); - } - - /// - /// 将二进制数据写入一个内存流中 - /// - /// 内存流 - /// 待写入的字节数组 - public static void WriteMemoryStream(MemoryStream stream, byte[] bytes) - { - stream.Write(bytes, 0, bytes.Length); - } - - /// - /// 将一个字符串转换为字节数组 - /// [Obsolete("请直接使用 Encoding.UTF8.GetBytes(text)")] - /// - /// 待转换的字符串 - /// 字节数组,其中包含输入字符串的编码数据 - [Obsolete("请直接使用 Encoding.UTF8.GetBytes(text)", false)] - public static byte[] StringToBytes(string text) - { - return Encoding.UTF8.GetBytes(text); - } - - /// - /// 将一个字节数组转换为字符串 - /// [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)")] - /// - /// 待转换的字节数组 - /// 字符串,其中包含输入字节数组的编码数据 - [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)", false)] - public static string BytesToString(byte[] bytes) - { - return Encoding.UTF8.GetString(bytes); - } - } -} diff --git a/EasyTool.Core/IOCategory/StreamExtension.cs b/EasyTool.Core/IOCategory/StreamExtension.cs index a8c729b..d3c93bd 100644 --- a/EasyTool.Core/IOCategory/StreamExtension.cs +++ b/EasyTool.Core/IOCategory/StreamExtension.cs @@ -189,45 +189,6 @@ public static async Task WriteTextAsync(this Stream stream, string text, Encodin #region 复制操作 - /// - /// 将流复制到另一个流 - /// [Obsolete("请直接使用 source.CopyTo(destination)")] - /// - [Obsolete("请直接使用 source.CopyTo(destination)", false)] - public static void CopyTo(this Stream source, Stream destination) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (destination == null) - throw new ArgumentNullException(nameof(destination)); - - source.CopyTo(destination, 81920); - } - - /// - /// 将流复制到另一个流(指定缓冲区大小) - /// [Obsolete("请直接使用 source.CopyTo(destination)")] - /// - [Obsolete("请直接使用 source.CopyTo(destination)", false)] - public static void CopyTo(this Stream source, Stream destination, int bufferSize) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (destination == null) - throw new ArgumentNullException(nameof(destination)); - - if (!source.CanRead) - throw new InvalidOperationException("Source stream does not support reading."); - if (!destination.CanWrite) - throw new InvalidOperationException("Destination stream does not support writing."); - - var buffer = new byte[bufferSize]; - int bytesRead; - while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) - { - destination.Write(buffer, 0, bytesRead); - } - } /// /// 将流复制到字节数组 @@ -254,37 +215,6 @@ public static MemoryStream CopyToMemoryStream(this Stream stream) #region 位置操作 - /// - /// 将流位置重置到开头 - /// [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.Begin)")] - /// - [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.Begin)", false)] - public static void ResetPosition(this Stream stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - if (stream.CanSeek) - { - stream.Seek(0, SeekOrigin.Begin); - } - } - - /// - /// 将流位置重置到末尾 - /// [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.End)")] - /// - [Obsolete("请直接使用 stream.Seek(0, SeekOrigin.End)", false)] - public static void SeekToEnd(this Stream stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - if (stream.CanSeek) - { - stream.Seek(0, SeekOrigin.End); - } - } #endregion diff --git a/EasyTool.Core/LanguageCategory/BCDUtil.cs b/EasyTool.Core/LanguageCategory/BCDUtil.cs deleted file mode 100644 index 8e07e35..0000000 --- a/EasyTool.Core/LanguageCategory/BCDUtil.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - /// - /// BCD工具 - /// - public class BCDUtil - { - /// - /// 将一个十进制数转换成对应的二进制码数组 - /// - /// 需要转换的十进制数 - /// 二进制码数组 - public static int[] DecToBinaryArray(int dec) - { - if (dec < 0) - { - throw new ArgumentException("dec必须是非负整数。"); - } - - if (dec == 0) - { - return new int[] { 0 }; - } - - int[] binaryArray = new int[32]; - int index = 0; - - while (dec > 0) - { - binaryArray[index++] = dec % 2; - dec /= 2; - } - - Array.Resize(ref binaryArray, index); - Array.Reverse(binaryArray); - - return binaryArray; - } - - /// - /// 将一个二进制码数组转换成对应的十进制数 - /// - /// 需要转换的二进制码数组 - /// 对应的十进制数 - public static int BinaryArrayToDec(int[] binaryArray) - { - if (binaryArray == null) - { - throw new ArgumentNullException("binaryArray不能为null。"); - } - - int dec = 0; - int power = 1; - - for (int i = binaryArray.Length - 1; i >= 0; i--) - { - dec += binaryArray[i] * power; - power *= 2; - } - - return dec; - } - - /// - /// 将一个十进制数转换成对应的BCD码数组 - /// - /// 需要转换的十进制数 - /// BCD码数组 - public static int[] DecToBCDArray(int dec) - { - if (dec < 0) - { - throw new ArgumentException("dec必须是非负整数。"); - } - - if (dec == 0) - { - return new int[] { 0 }; - } - - int[] bcdArray = new int[10]; - int index = 0; - - while (dec > 0) - { - int remainder = dec % 10; - int[] binaryArray = DecToBinaryArray(remainder); - int paddingCount = 4 - binaryArray.Length; - - for (int i = 0; i < paddingCount; i++) - { - bcdArray[index++] = 0; - } - - for (int i = 0; i < binaryArray.Length; i++) - { - bcdArray[index++] = binaryArray[i]; - } - - dec /= 10; - } - - Array.Resize(ref bcdArray, index); - Array.Reverse(bcdArray); - - return bcdArray; - } - - /// - /// 将一个BCD码数组转换成对应的十进制数 - /// - /// 需要转换的BCD码数组 - /// 对应的十进制数 - public static int BCDArrayToDec(int[] bcdArray) - { - if (bcdArray == null) - { - throw new ArgumentNullException("bcdArray不能为null。"); - } - - int dec = 0; - int power = 1; - - for (int i = bcdArray.Length - 1; i >= 0; i -= 4) - { - int binary = 0; - - for (int j = 0; j < 4; j++) - { - int index = i - j; - - if (index < 0) - { - break; - } - - binary += bcdArray[index] * (int)Math.Pow(2, 3 - j); - } - - dec += binary * power; - power *= 10; - } - - return dec; - } - - /// - /// 将给定的十进制数转换为 BCD 码字符串。 - /// - /// 要转换的十进制数 - /// 转换后的 BCD 码字符串 - public static string Encode(int dec) - { - if (dec == 0) - { - return "0"; - } - - string str = dec.ToString(); - int len = str.Length; - char[] bcdChars = new char[len * 2]; - for (int i = 0; i < len; i++) - { - int bcd = ((int)Char.GetNumericValue(str[i])) & 0x0F; - bcdChars[i * 2] = (char)(bcd + ((bcd > 9) ? 0x37 : 0x30)); - - bcd = (((int)Char.GetNumericValue(str[i])) >> 4) & 0x0F; - bcdChars[i * 2 + 1] = (char)(bcd + ((bcd > 9) ? 0x37 : 0x30)); - } - return new string(bcdChars); - } - - /// - /// 将给定的 BCD 码字符串转换为十进制数。 - /// - /// 要转换的 BCD 码字符串 - /// 转换后的十进制数 - public static int Decode(string bcd) - { - if (string.IsNullOrEmpty(bcd)) - { - return 0; - } - - int len = bcd.Length; - int dec = 0; - for (int i = 0; i < len; i += 2) - { - int a = ((int)bcd[i]) & 0x0F; - int b = ((int)bcd[i + 1]) & 0x0F; - dec = dec * 100 + a + b * 10; - } - return dec; - } - } -} diff --git a/EasyTool.Core/LanguageCategory/SingletonUtil.cs b/EasyTool.Core/LanguageCategory/SingletonUtil.cs deleted file mode 100644 index f49d677..0000000 --- a/EasyTool.Core/LanguageCategory/SingletonUtil.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - /// - /// 单例工具类 - /// - public class SingletonUtil where T : class, new() - { - private static readonly Lazy lazyInstance = new Lazy(() => new T()); - - /// - /// 返回单例对象的唯一实例(懒汉模式) - /// - public static T LazyInstance => lazyInstance.Value; - - - private static T instance; - private static readonly object lockObject = new object(); - - /// - /// 返回单例对象的唯一实例(饿汉模式) - /// - [Obsolete] - public static T Instance - { - get - { - if (instance == null) - { - lock (lockObject) - { - if (instance == null) - { - instance = new T(); - } - } - } - - return instance; - } - } - } -} diff --git a/EasyTool.Core/LanguageCategory/TreeUtil.cs b/EasyTool.Core/LanguageCategory/TreeUtil.cs deleted file mode 100644 index 7003b9b..0000000 --- a/EasyTool.Core/LanguageCategory/TreeUtil.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EasyTool -{ - /// - /// 树工具类,用于构建树结构 - /// - public class TreeUtil - { - private List> _nodes; - - /// - /// 构造函数 - /// - /// 树节点列表 - public TreeUtil(List> nodes) - { - _nodes = nodes; - } - - /// - /// 构建树结构 - /// - /// 根节点 - public TreeNode? BuildTree() - { - // 获取根节点 - var root = _nodes.FirstOrDefault(n => n.ParentId == null || n.ParentId.Equals(default(T))); - - if (root == null) - { - return null; - } - - // 构建树结构 - BuildTree(root); - - return root; - } - - private void BuildTree(TreeNode node) - { - node.Children = _nodes.Where(n => n.ParentId != null && n.ParentId.Equals(node.Id)).ToList(); - - if (node.Children.Count > 0) - { - foreach (var child in node.Children) - { - BuildTree(child); - } - } - } - - /// - /// 获取某个节点的所有父节点 - /// - /// 节点 - /// 父节点列表,从根节点到该节点的顺序 - public List> GetParents(TreeNode node) - { - var parents = new List>(); - var parent = GetParent(node.Id); - while (parent != null) - { - parents.Insert(0, parent); - parent = GetParent(parent.Id); - } - return parents; - } - - /// - /// 获取某个节点的深度 - /// - /// 节点 - /// 节点深度,根节点的深度为0 - public int GetDepth(TreeNode node) - { - return GetParents(node).Count; - } - - /// - /// 获取某个节点的所有子孙节点 - /// - /// 节点 - /// 子孙节点列表 - public List> GetDescendants(TreeNode node) - { - var descendants = new List>(); - GetDescendants(node, descendants); - return descendants; - } - - private void GetDescendants(TreeNode node, List> descendants) - { - descendants.Add(node); - if (node.Children.Count > 0) - { - foreach (var child in node.Children) - { - GetDescendants(child, descendants); - } - } - } - - private TreeNode? GetParent(T id) - { - return _nodes.FirstOrDefault(n => n.Id != null && n.Id.Equals(id)); - } - - /// - /// 获取某个节点的所有兄弟节点 - /// - /// 节点 - /// 兄弟节点列表 - public List> GetSiblings(TreeNode node) - { - var parent = GetParent(node.Id); - if (parent == null) - { - return new List>(); - } - return parent.Children.Where(n => n.Id == null || !n.Id.Equals(node.Id)).ToList(); - } - - /// - /// 获取某个节点的所有兄弟节点数量 - /// - /// 节点 - /// 兄弟节点数量 - public int GetSiblingCount(TreeNode node) - { - return GetSiblings(node).Count; - } - - /// - /// 判断某个节点是否是叶子节点 - /// - /// 节点 - /// 是否是叶子节点 - public bool IsLeaf(TreeNode node) - { - return node.Children.Count == 0; - } - - /// - /// 获取树的最大深度 - /// - /// 树的最大深度 - public int GetMaxDepth() - { - return _nodes.Max(n => GetDepth(n)); - } - - /// - /// 获取树的最小深度 - /// - /// 树的最小深度 - public int GetMinDepth() - { - return _nodes.Min(n => GetDepth(n)); - } - - /// - /// 获取某个节点的下一个兄弟节点 - /// - /// 节点 - /// 下一个兄弟节点 - public TreeNode? GetNextSibling(TreeNode node) - { - var siblings = GetSiblings(node); - var index = siblings.FindIndex(n => n.Id != null && n.Id.Equals(node.Id)); - return index + 1 < siblings.Count ? siblings[index + 1] : null; - } - - /// - /// 获取某个节点的上一个兄弟节点 - /// - /// 节点 - /// 上一个兄弟节点 - public TreeNode? GetPreviousSibling(TreeNode node) - { - var siblings = GetSiblings(node); - var index = siblings.FindIndex(n => n.Id != null && n.Id.Equals(node.Id)); - return index - 1 >= 0 ? siblings[index - 1] : null; - } - - /// - /// 获取某个节点的首个子节点 - /// - /// 节点 - /// 首个子节点 - public TreeNode? GetFirstChild(TreeNode node) - { - return node.Children.Count > 0 ? node.Children[0] : null; - } - - /// - /// 获取某个节点的最后一个子节点 - /// - /// 节点 - /// 最后一个子节点 - public TreeNode? GetLastChild(TreeNode node) - { - return node.Children.Count > 0 ? node.Children[node.Children.Count - 1] : null; - } - - /// - /// 获取树的所有节点数量 - /// - /// 树的所有节点数量 - public int GetNodeCount() - { - return _nodes.Count; - } - - /// - /// 获取树的所有叶子节点数量 - /// - /// 树的所有叶子节点数量 - public int GetLeafCount() - { - return _nodes.Count(IsLeaf); - } - - /// - /// 获取树的所有节点的权重和 - /// - /// 树的所有节点的权重和 - public int GetTotalWeight() - { - return _nodes.Sum(n => n.Weight); - } - - /// - /// 获取树的所有叶子节点的权重和 - /// - /// 树的所有叶子节点的权重和 - public int GetLeafWeightTotal() - { - return _nodes.Where(IsLeaf).Sum(n => n.Weight); - } - - /// - /// 获取树的平均深度 - /// - /// 树的平均深度 - public int GetAverageDepth() - { - return (int)_nodes.Average(n => GetDepth(n)); - } - - /// - /// 获取树的平均节点权重 - /// - /// 树的平均节点权重 - public int GetAverageWeight() - { - return (int)_nodes.Average(n => n.Weight); - } - - /// - /// 获取树的最大节点权重 - /// - /// 树的最大节点权重 - public int GetMaxWeight() - { - return _nodes.Max(n => n.Weight); - } - - /// - /// 获取树的最小节点权重 - /// - /// 树的最小节点权重 - public int GetMinWeight() - { - return _nodes.Min(n => n.Weight); - } - } - - public class TreeNode - { - public T Id { get; set; } - public T? ParentId { get; set; } - public string Name { get; set; } = string.Empty; - public int Weight { get; set; } - public D Data { get; set; } = default!; - public List> Children { get; set; } = new List>(); - - public TreeNode(T id, T? parentId, string name, int weight, D data) - { - this.Id = id; - this.ParentId = parentId; - this.Name = name; - this.Weight = weight; - this.Data = data; - this.Children = new List>(); - } - } -} diff --git a/EasyTool.Core/MathCategory/MathUtil.cs b/EasyTool.Core/MathCategory/MathUtil.cs index 76010ec..ff4f064 100644 --- a/EasyTool.Core/MathCategory/MathUtil.cs +++ b/EasyTool.Core/MathCategory/MathUtil.cs @@ -1,39 +1,14 @@ -using System; -using System.Collections.Generic; +using System; using System.Text; namespace EasyTool { + /// + /// 数学工具类,提供数字计算和数学运算方法 + /// public static class MathUtil { - /// - /// 计算两个整数的最大公约数 - /// - /// 第一个整数 - /// 第二个整数 - /// 最大公约数 - public static int Gcd(int a, int b) - { - if (b == 0) - { - return a; - } - else - { - return Gcd(b, a % b); - } - } - - /// - /// 计算两个整数的最小公倍数 - /// - /// 第一个整数 - /// 第二个整数 - /// 最小公倍数 - public static int Lcm(int a, int b) - { - return a * b / Gcd(a, b); - } + #region 质数与阶乘 /// /// 判断一个整数是否为质数 @@ -58,18 +33,6 @@ public static bool IsPrime(int n) return true; } - /// - /// 计算两个浮点数的差的绝对值是否小于指定的精度 - /// - /// 第一个浮点数 - /// 第二个浮点数 - /// 指定的精度 - /// 如果两个浮点数的差的绝对值小于指定的精度,则返回 true;否则返回 false - public static bool ApproxEqual(double a, double b, double eps) - { - return Math.Abs(a - b) < eps; - } - /// /// 求一个整数的阶乘 /// @@ -77,62 +40,67 @@ public static bool ApproxEqual(double a, double b, double eps) /// 阶乘结果 public static int Factorial(int n) { - if (n <= 1) + if (n < 0) { - return 1; + throw new ArgumentException("阶乘只能求非负整数"); } - else + + int result = 1; + for (int i = 1; i <= n; i++) { - return n * Factorial(n - 1); + result *= i; } + + return result; } + #endregion + + #region 最大公约数与最小公倍数 + /// - /// 求一个整数的斐波那契数列的值 + /// 计算两个整数的最大公约数 /// - /// 要求斐波那契数列的整数 - /// 斐波那契数列的值 - public static int Fibonacci(int n) + /// 第一个整数 + /// 第二个整数 + /// 最大公约数 + public static int Gcd(int a, int b) { - if (n <= 1) + if (b == 0) { - return n; + return a; } else { - return Fibonacci(n - 1) + Fibonacci(n - 2); + return Gcd(b, a % b); } } /// - /// 求一个整数的二进制表示中 1 的个数 + /// 计算两个整数的最小公倍数 /// - /// 要求二进制表示中 1 的个数的整数 - /// 二进制表示中 1 的个数 - public static int CountBits(int n) + /// 第一个整数 + /// 第二个整数 + /// 最小公倍数 + public static int Lcm(int a, int b) { - int count = 0; + return a * b / Gcd(a, b); + } - while (n != 0) - { - count++; - n &= n - 1; - } + #endregion - return count; - } + #region 数值计算 /// - /// 求两个浮点数的平均值 - /// [Obsolete("请直接使用 (a + b) / 2")] + /// 计算两个浮点数的差的绝对值是否小于指定的精度 /// /// 第一个浮点数 /// 第二个浮点数 - /// 两个浮点数的平均值 - [Obsolete("请直接使用 (a + b) / 2", false)] - public static double Average(double a, double b) + /// 指定的精度 + /// 如果两个浮点数的差的绝对值小于指定的精度,则返回 true;否则返回 false + public static bool ApproxEqual(double a, double b, double eps) { - return (a + b) / 2; + return Math.Abs(a - b) < eps; } /// @@ -170,6 +138,177 @@ public static int Pow(int n, int k) } } + /// + /// 求一个整数的绝对值 + /// + /// 待求绝对值的数字 + /// 该数字的绝对值 + public static int Abs(int number) + { + return number < 0 ? -number : number; + } + + /// + /// 求一个整数的平方 + /// + /// 待求平方的数字 + /// 该数字的平方 + public static int Square(int number) + { + return number * number; + } + + /// + /// 求一个整数的立方 + /// + /// 待求立方的数字 + /// 该数字的立方 + public static int Cube(int number) + { + return number * number * number; + } + + /// + /// 计算两个整数的和,如果结果溢出了 int 类型的取值范围,则返回 int.MaxValue + /// + /// 第一个整数 + /// 第二个整数 + /// 两个整数的和,如果结果溢出了 int 类型的取值范围,则返回 int.MaxValue + public static int SafeAdd(int a, int b) + { + int sum = a + b; + + if (a > 0 && b > 0 && sum < 0) + { + return int.MaxValue; + } + else if (a < 0 && b < 0 && sum > 0) + { + return int.MinValue; + } + else + { + return sum; + } + } + + #endregion + + #region 进制转换 + + /// + /// 把一个数字转换为二进制字符串 + /// + /// 待转换的数字 + /// 该数字的二进制字符串 + public static string ToBinaryString(int number) + { + if (number == 0) + { + return "0"; + } + + string result = string.Empty; + while (number > 0) + { + result = (number % 2).ToString() + result; + number /= 2; + } + + return result; + } + + /// + /// 把一个数字转换为八进制字符串 + /// + /// 待转换的数字 + /// 该数字的八进制字符串 + public static string ToOctalString(int number) + { + if (number == 0) + { + return "0"; + } + + string result = string.Empty; + while (number > 0) + { + result = (number % 8).ToString() + result; + number /= 8; + } + + return result; + } + + /// + /// 把一个数字转换为十六进制字符串 + /// + /// 待转换的数字 + /// 该数字的十六进制字符串 + public static string ToHexString(int number) + { + if (number == 0) + { + return "0"; + } + + string result = string.Empty; + while (number > 0) + { + int remainder = number % 16; + if (remainder < 10) + { + result = remainder.ToString() + result; + } + else + { + result = (char)('A' + remainder - 10) + result; + } + number /= 16; + } + + return result; + } + + #endregion + + #region 高级数学函数 + + /// + /// 求一个整数的斐波那契数列的值 + /// + /// 要求斐波那契数列的整数 + /// 斐波那契数列的值 + public static int Fibonacci(int n) + { + if (n <= 1) + { + return n; + } + else + { + return Fibonacci(n - 1) + Fibonacci(n - 2); + } + } + + /// + /// 求一个整数的二进制表示中 1 的个数 + /// + /// 要求二进制表示中 1 的个数的整数 + /// 二进制表示中 1 的个数 + public static int CountBits(int n) + { + int count = 0; + + while (n != 0) + { + count++; + n &= n - 1; + } + + return count; + } + /// /// 判断一个整数是否为完全平方数 /// @@ -272,28 +411,6 @@ public static int[] GetAllFactors(int n) return factors; } - /// - /// 计算两个整数的和,如果结果溢出了 int 类型的取值范围,则返回 int.MaxValue - /// - /// 第一个整数 - /// 第二个整数 - /// 两个整数的和,如果结果溢出了 int 类型的取值范围,则返回 int.MaxValue - public static int Add(int a, int b) - { - int sum = a + b; - - if (a > 0 && b > 0 && sum < 0) - { - return int.MaxValue; - } - else if (a < 0 && b < 0 && sum > 0) - { - return int.MinValue; - } - else - { - return sum; - } - } + #endregion } } diff --git a/EasyTool.Core/MathCategory/NumberUtil.cs b/EasyTool.Core/MathCategory/NumberUtil.cs deleted file mode 100644 index ad84f16..0000000 --- a/EasyTool.Core/MathCategory/NumberUtil.cs +++ /dev/null @@ -1,352 +0,0 @@ -using System; - -namespace EasyTool -{ - /// - /// 数字工具类,提供了多种对数字的操作方法 - /// - public class NumberUtil - { - /// - /// 针对数字类型做加法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的和 - public static decimal Add(float a, float b) - { - return (decimal)a + (decimal)b; - } - - /// - /// 针对数字类型做加法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的和 - public static decimal Add(double a, double b) - { - return (decimal)a + (decimal)b; - } - - /// - /// 针对数字类型做减法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的差 - public static decimal Sub(float a, float b) - { - return (decimal)a - (decimal)b; - } - - /// - /// 针对数字类型做减法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的差 - public static decimal Sub(double a, double b) - { - return (decimal)a - (decimal)b; - } - - /// - /// 针对数字类型做乘法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的积 - public static decimal Mul(float a, float b) - { - return (decimal)a * (decimal)b; - } - - /// - /// 针对数字类型做乘法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的积 - public static decimal Mul(double a, double b) - { - return (decimal)a * (decimal)b; - } - - /// - /// 针对数字类型做除法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的商 - public static decimal Div(float a, float b) - { - return (decimal)a / (decimal)b; - } - - /// - /// 针对数字类型做除法 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的商 - public static decimal Div(double a, double b) - { - return (decimal)a / (decimal)b; - } - - /// - /// 针对数字类型做除法,并限制返回的小数位数 - /// - /// 第一个数字 - /// 第二个数字 - /// 限制返回的小数位数 - /// 两个数字的商 - public static decimal Div(float a, float b, int decimalPlaces) - { - decimal result = Div(a, b); - return decimal.Round(result, decimalPlaces); - } - - /// - /// 针对数字类型做除法,并限制返回的小数位数 - /// - /// 第一个数字 - /// 第二个数字 - /// 限制返回的小数位数 - /// 两个数字的商 - public static decimal Div(double a, double b, int decimalPlaces) - { - decimal result = Div(a, b); - return decimal.Round(result, decimalPlaces); - } - - /// - /// 格式化一个 decimal 数字 - /// [Obsolete("请直接使用 number.ToString(format)")] - /// - /// 待格式化的数字 - /// 格式化字符串 - /// 格式化后的字符串 - [Obsolete("请直接使用 number.ToString(format)", false)] - public static string DecimalFormat(decimal number, string format) - { - return number.ToString(format); - } - - /// - /// 保留一个 decimal 数字的小数点后指定位数 - /// - /// 待格式化的数字 - /// 小数点后保留的位数 - /// 格式化后的字符串 - public static string DecimalFormat(decimal number, int decimalPlaces) - { - string format = "0."; - for (int i = 0; i < decimalPlaces; i++) - { - format += "0"; - } - return DecimalFormat(number, format); - } - - /// - /// 格式化一个 decimal 数字,并加上千位分隔符 - /// - /// 待格式化的数字 - /// 格式化后的字符串 - public static string DecimalFormatWithCommas(decimal number) - { - return DecimalFormat(number, "0,0.00"); - } - - - - /// - /// 判断一个数字是否是质数 - /// - /// 待判断的数字 - /// 如果是质数,则返回 true;否则返回 false - public static bool IsPrime(int number) - { - if (number <= 1) - { - return false; - } - - for (int i = 2; i <= Math.Sqrt(number); i++) - { - if (number % i == 0) - { - return false; - } - } - - return true; - } - - /// - /// 求一个数字的阶乘 - /// - /// 待求阶乘的数字 - /// 该数字的阶乘 - public static int Factorial(int number) - { - if (number < 0) - { - throw new ArgumentException("阶乘只能求非负整数"); - } - - int result = 1; - for (int i = 1; i <= number; i++) - { - result *= i; - } - - return result; - } - - /// - /// 求两个数字的最大公约数 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的最大公约数 - public static int GCD(int a, int b) - { - if (a < 0 || b < 0) - { - throw new ArgumentException("求最大公约数只能接受非负整数"); - } - - while (b != 0) - { - int temp = b; - b = a % b; - a = temp; - } - - return a; - } - - /// - /// 求两个数字的最小公倍数 - /// - /// 第一个数字 - /// 第二个数字 - /// 两个数字的最小公倍数 - public static int LCM(int a, int b) - { - if (a < 0 || b < 0) - { - throw new ArgumentException("求最小公倍数只能接受非负整数"); - } - - return a * b / GCD(a, b); - } - - /// - /// 把一个数字转换为二进制字符串 - /// - /// 待转换的数字 - /// 该数字的二进制字符串 - public static string ToBinaryString(int number) - { - if (number == 0) - { - return "0"; - } - - string result = string.Empty; - while (number > 0) - { - result = (number % 2).ToString() + result; - number /= 2; - } - - return result; - } - - /// - /// 把一个数字转换为八进制字符串 - /// - /// 待转换的数字 - /// 该数字的八进制字符串 - public static string ToOctalString(int number) - { - if (number == 0) - { - return "0"; - } - - string result = string.Empty; - while (number > 0) - { - result = (number % 8).ToString() + result; - number /= 8; - } - - return result; - } - - /// - /// 把一个数字转换为十六进制字符串 - /// - /// 待转换的数字 - /// 该数字的十六进制字符串 - public static string ToHexString(int number) - { - if (number == 0) - { - return "0"; - } - - string result = string.Empty; - while (number > 0) - { - int remainder = number % 16; - if (remainder < 10) - { - result = remainder.ToString() + result; - } - else - { - result = (char)('A' + remainder - 10) + result; - } - number /= 16; - } - - return result; - } - - /// - /// 求一个数字的绝对值 - /// - /// 待求绝对值的数字 - /// 该数字的绝对值 - public static int Abs(int number) - { - return number < 0 ? -number : number; - } - - /// - /// 求一个数字的平方 - /// - /// 待求平方的数字 - /// 该数字的平方 - public static int Square(int number) - { - return number * number; - } - - /// - /// 求一个数字的立方 - /// - /// 待求立方的数字 - /// 该数字的立方 - public static int Cube(int number) - { - return number * number * number; - } - } -} diff --git a/EasyTool.Core/ToolCategory/RandomUtil.cs b/EasyTool.Core/MathCategory/RandomUtil.cs similarity index 100% rename from EasyTool.Core/ToolCategory/RandomUtil.cs rename to EasyTool.Core/MathCategory/RandomUtil.cs diff --git a/EasyTool.Core/NetCategory/NetUtil.cs b/EasyTool.Core/NetCategory/NetUtil.cs deleted file mode 100644 index 9f5460b..0000000 --- a/EasyTool.Core/NetCategory/NetUtil.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Net; -using System.Text; - -namespace EasyTool -{ - /// - /// 网络工具 - /// - public class NetUtil - { - // Ping a host and return true if the ping was successful - // 对指定主机进行Ping测试,返回是否成功 - public static bool Ping(string host) - { - try - { - Ping pingSender = new Ping(); - PingReply reply = pingSender.Send(host); - - if (reply.Status == IPStatus.Success) - { - return true; - } - else - { - return false; - } - } - catch - { - return false; - } - } - - // Resolve the IP address of a host - // 获取指定主机的IP地址 - public static IPAddress? GetIpAddress(string host) - { - try - { - IPHostEntry hostEntry = Dns.GetHostEntry(host); - - foreach (IPAddress address in hostEntry.AddressList) - { - // 返回IPv4地址 - if (address.AddressFamily == AddressFamily.InterNetwork) - { - return address; - } - } - - return null; - } - catch - { - return null; - } - } - - // Check if a port is open on a given IP address - // 检查给定IP地址上的端口是否开放 - public static bool IsPortOpen(string host, int port) - { - try - { - // 获取IP地址 - IPAddress ipAddress = GetIpAddress(host); - - if (ipAddress == null) - { - return false; - } - - // 创建套接字,连接端口 - IPEndPoint endpoint = new IPEndPoint(ipAddress, port); - using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - socket.Connect(endpoint); - return true; - } - } - catch - { - return false; - } - } - - // Send an HTTP GET request and return the response - // 发送HTTP GET请求并返回响应 - public static string? HttpGet(string url) - { - try - { - using (HttpClient client = new HttpClient()) - { - return client.GetStringAsync(url).GetAwaiter().GetResult(); - } - } - catch - { - return null; - } - } - - // Send an HTTP POST request and return the response - // 发送HTTP POST请求并返回响应 - public static string? HttpPost(string url, string data) - { - try - { - using (HttpClient client = new HttpClient()) - { - StringContent content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded"); - HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult(); - return response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - } - } - catch - { - return null; - } - } - } -} diff --git a/EasyTool.Core/ToolCategory/URLUtil.cs b/EasyTool.Core/NetCategory/URLUtil.cs similarity index 94% rename from EasyTool.Core/ToolCategory/URLUtil.cs rename to EasyTool.Core/NetCategory/URLUtil.cs index 82edd17..d281985 100644 --- a/EasyTool.Core/ToolCategory/URLUtil.cs +++ b/EasyTool.Core/NetCategory/URLUtil.cs @@ -140,11 +140,9 @@ public static string UrlDecodeQuery(string value) /// /// 从URL中提取域名。 - /// [Obsolete("请直接使用 new Uri(url).Host")] /// /// 要提取域名的URL。 /// URL中的域名。 - [Obsolete("请直接使用 new Uri(url).Host", false)] public static string ExtractDomain(string url) { var uri = new Uri(url); @@ -153,11 +151,9 @@ public static string ExtractDomain(string url) /// /// 从URL中提取路径。 - /// [Obsolete("请直接使用 new Uri(url).AbsolutePath")] /// /// 要提取路径的URL。 /// URL中的路径。 - [Obsolete("请直接使用 new Uri(url).AbsolutePath", false)] public static string ExtractPath(string url) { var uri = new Uri(url); @@ -177,11 +173,9 @@ public static bool IsHttps(string url) /// /// 从URL中提取查询字符串。 - /// [Obsolete("请直接使用 new Uri(url).Query")] /// /// 要提取查询字符串的URL。 /// URL中的查询字符串。 - [Obsolete("请直接使用 new Uri(url).Query", false)] public static string ExtractQueryString(string url) { var uri = new Uri(url); @@ -190,11 +184,9 @@ public static string ExtractQueryString(string url) /// /// 从URL中提取片段。 - /// [Obsolete("请直接使用 new Uri(url).Fragment")] /// /// 要提取片段的URL。 /// URL中的片段。 - [Obsolete("请直接使用 new Uri(url).Fragment", false)] public static string ExtractFragment(string url) { var uri = new Uri(url); diff --git a/EasyTool.Core/TextCategory/CsvUtil.cs b/EasyTool.Core/TextCategory/CsvUtil.cs deleted file mode 100644 index 79cbbc5..0000000 --- a/EasyTool.Core/TextCategory/CsvUtil.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace EasyTool -{ - /// - /// 提供读取和写入 CSV 文件的常用方法。 - /// - public static class CsvUtil - { - private const char DEFAULT_DELIMITER = ','; - private const char DEFAULT_QUOTE = '"'; - - /// - /// 从指定路径的 CSV 文件中读取数据。 - /// - /// CSV 文件路径。 - /// CSV 文件中的分隔符。 - /// CSV 文件中的引用符。 - /// 读取到的数据。 - public static List> Read(string path, char delimiter = DEFAULT_DELIMITER, char quote = DEFAULT_QUOTE) - { - List> data = new List>(); - using (var reader = new StreamReader(path)) - { - while (!reader.EndOfStream) - { - string line = reader.ReadLine(); - data.Add(ParseLine(line, delimiter, quote)); - } - } - - return data; - } - - /// - /// 将指定的数据写入到 CSV 文件中。 - /// - /// 要写入的数据。 - /// CSV 文件路径。 - /// CSV 文件中的分隔符。 - /// CSV 文件中的引用符。 - public static void Write(List> data, string path, char delimiter = DEFAULT_DELIMITER, char quote = DEFAULT_QUOTE) - { - using (var writer = new StreamWriter(path)) - { - foreach (var record in data) - { - string line = string.Join(delimiter.ToString(), record.Select(s => Escape(s, delimiter, quote))); - writer.WriteLine(line); - } - } - } - - private static List ParseLine(string line, char delimiter, char quote) - { - List fields = new List(); - int i = 0; - while (i < line.Length) - { - if (line[i] == quote) - { - int j = i + 1; - while (j < line.Length) - { - if (line[j] == quote) - { - if (j + 1 < line.Length && line[j + 1] == delimiter) - { - j++; // skip escaped delimiter - } - else - { - break; // end of quoted field - } - } - j++; - } - - if (j >= line.Length || line[j] != quote) - { - throw new ArgumentException("Invalid CSV format: mismatched quotes."); - } - - fields.Add(Unescape(line.Substring(i + 1, j - i - 1), delimiter, quote)); - i = j + 1; - } - else - { - int j = line.IndexOf(delimiter, i); - if (j < 0) - { - fields.Add(line.Substring(i)); - i = line.Length; - } - else - { - fields.Add(line.Substring(i, j - i)); - i = j + 1; - } - } - } - - return fields; - } - - private static string Escape(string value, char delimiter, char quote) - { - if (value.Contains(delimiter) || value.Contains(quote) || value.Contains(Environment.NewLine)) - { - return quote + value.Replace(quote.ToString(), quote.ToString() + quote.ToString()) + quote; - } - else - { - return value; - } - } - - private static string Unescape(string value, char delimiter, char quote) - { - return value.Replace(quote.ToString() + quote.ToString(), quote.ToString()).Replace(quote.ToString() + delimiter, delimiter.ToString()); - } - } -} diff --git a/EasyTool.Core/ToolCategory/RegexUtil.cs b/EasyTool.Core/TextCategory/RegexUtil.cs similarity index 79% rename from EasyTool.Core/ToolCategory/RegexUtil.cs rename to EasyTool.Core/TextCategory/RegexUtil.cs index 5f5a1d5..2ce88d2 100644 --- a/EasyTool.Core/ToolCategory/RegexUtil.cs +++ b/EasyTool.Core/TextCategory/RegexUtil.cs @@ -11,19 +11,6 @@ namespace EasyTool /// public class RegexUtil { - /// - /// 验证字符串是否与指定的正则表达式匹配 - /// [Obsolete("请直接使用 Regex.IsMatch(input, pattern)")] - /// - /// 要验证的字符串 - /// 正则表达式 - /// 如果字符串与正则表达式匹配,则为true;否则为false - [Obsolete("请直接使用 Regex.IsMatch(input, pattern)", false)] - public static bool IsMatch(string input, string pattern) - { - return Regex.IsMatch(input, pattern); - } - /// /// 验证字符串是否与指定的正则表达式匹配,并返回匹配结果 /// @@ -47,20 +34,6 @@ public static string[] Matches(string input, string pattern) return Regex.Matches(input, pattern).Cast().Select(m => m.Value).ToArray(); } - /// - /// 使用指定的替换字符串替换输入字符串中与指定正则表达式匹配的所有子字符串 - /// [Obsolete("请直接使用 Regex.Replace(input, pattern, replacement)")] - /// - /// 要替换的字符串 - /// 正则表达式 - /// 替换字符串 - /// 替换后的字符串 - [Obsolete("请直接使用 Regex.Replace(input, pattern, replacement)", false)] - public static string Replace(string input, string pattern, string replacement) - { - return Regex.Replace(input, pattern, replacement); - } - /// /// 使用指定的替换字符串替换输入字符串中与指定正则表达式匹配的所有子字符串,并返回替换后的字符串和替换次数 /// diff --git a/EasyTool.Core/TextCategory/UnicodeUtil.cs b/EasyTool.Core/TextCategory/UnicodeUtil.cs deleted file mode 100644 index de3d562..0000000 --- a/EasyTool.Core/TextCategory/UnicodeUtil.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - public class UnicodeUtil - { - - /// - /// 将Unicode编码的字符串转换为普通字符串。 - /// - /// Unicode编码的字符串 - /// 普通字符串 - public static string UnicodeToString(string unicodeStr) - { - StringBuilder sb = new StringBuilder(); - - int len = unicodeStr.Length; - for (int i = 0; i < len; i++) - { - if (unicodeStr[i] == '\\' && (i + 1) < len && unicodeStr[i + 1] == 'u') - { - string hexStr = unicodeStr.Substring(i + 2, 4); - int code = Convert.ToInt32(hexStr, 16); - sb.Append((char)code); - i += 5; - } - else - { - sb.Append(unicodeStr[i]); - } - } - - return sb.ToString(); - } - - /// - /// 将普通字符串转换为Unicode编码的字符串。 - /// - /// 普通字符串 - /// Unicode编码的字符串 - public static string StringToUnicode(string str) - { - StringBuilder sb = new StringBuilder(); - - foreach (char c in str) - { - sb.Append("\\u"); - sb.Append(((int)c).ToString("x4")); - } - - return sb.ToString(); - } - - /// - /// 将Unicode字符转换为普通字符。 - /// - /// Unicode字符 - /// 普通字符 - public static char UnicodeToChar(string unicodeChar) - { - int code = Convert.ToInt32(unicodeChar, 16); - return (char)code; - } - - /// - /// 将普通字符转换为Unicode字符。 - /// - /// 普通字符 - /// Unicode字符 - public static string CharToUnicode(char c) - { - return "\\u" + ((int)c).ToString("x4"); - } - - /// - /// 将Unicode编码的字符数组转换为普通字符串。 - /// - /// Unicode编码的字符数组 - /// 普通字符串 - public static string UnicodeCharsToString(char[] unicodeChars) - { - StringBuilder sb = new StringBuilder(); - - int len = unicodeChars.Length; - for (int i = 0; i < len; i++) - { - if (i + 1 < len && unicodeChars[i] == '\\' && unicodeChars[i + 1] == 'u') - { - string hexStr = new string(unicodeChars, i + 2, 4); - int code = Convert.ToInt32(hexStr, 16); - sb.Append((char)code); - i += 5; - } - else - { - sb.Append(unicodeChars[i]); - } - } - - return sb.ToString(); - } - - /// - /// 将普通字符串转换为Unicode编码的字符数组。 - /// - /// 普通字符串 - /// Unicode编码的字符数组 - public static char[] StringToUnicodeChars(string str) - { - List chars = new List(); - - foreach (char c in str) - { - chars.AddRange(CharToUnicode(c).ToCharArray()); - } - - return chars.ToArray(); - } - - /// - /// 将Unicode编码的字符数组转换为普通字符串数组。 - /// - /// Unicode编码的字符数组 - /// 普通字符串数组 - public static string[] UnicodeCharsToStringArray(char[] unicodeChars) - { - List strs = new List(); - - StringBuilder sb = new StringBuilder(); - - int len = unicodeChars.Length; - for (int i = 0; i < len; i++) - { - if (i + 1 < len && unicodeChars[i] == '\\' && unicodeChars[i + 1] == 'u') - { - string hexStr = new string(unicodeChars, i + 2, 4); - int code = Convert.ToInt32(hexStr, 16); - sb.Append((char)code); - i += 5; - } - else if (unicodeChars[i] == '\0') - { - strs.Add(sb.ToString()); - sb.Clear(); - } - else - { - sb.Append(unicodeChars[i]); - } - } - - if (sb.Length > 0) - { - strs.Add(sb.ToString()); - } - - return strs.ToArray(); - } - - /// - /// 将普通字符串数组转换为Unicode编码的字符数组。 - /// - /// 普通字符串数组 - /// Unicode编码的字符数组 - public static char[] StringArrayToUnicodeChars(string[] strs) - { - List chars = new List(); - - foreach (string str in strs) - { - chars.AddRange(StringToUnicodeChars(str)); - chars.Add('\0'); - } - - return chars.ToArray(); - } - } -} diff --git a/EasyTool.Core/ToolCategory/ArrayUtil.cs b/EasyTool.Core/ToolCategory/ArrayUtil.cs deleted file mode 100644 index fdf234c..0000000 --- a/EasyTool.Core/ToolCategory/ArrayUtil.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EasyTool -{ - /// - /// 数组工具类 - /// - public class ArrayUtil - { - /// - /// 判断数组是否为空 - /// - /// 要判断的数组 - /// 如果数组为空,则返回 true;否则返回 false - public static bool IsEmpty(Array array) - { - return array == null || array.Length == 0; - } - - /// - /// 获取数组的长度 - /// [Obsolete("请直接使用 array?.Length ?? 0")] - /// - /// 要获取长度的数组 - /// 返回数组的长度 - [Obsolete("请直接使用 array?.Length ?? 0", false)] - public static int Length(Array array) - { - if (array == null) - { - return 0; - } - - return array.Length; - } - - /// - /// 获取数组中的最大值 - /// [Obsolete("请直接使用 array.Max() (LINQ)")] - /// - /// 要获取最大值的数组 - /// 返回数组中的最大值 - [Obsolete("请直接使用 array.Max() (LINQ)", false)] - public static T Max(T[] array) where T : IComparable - { - if (IsEmpty(array)) - { - throw new ArgumentException("Array is empty."); - } - - T max = array[0]; - for (int i = 1; i < array.Length; i++) - { - if (array[i].CompareTo(max) > 0) - { - max = array[i]; - } - } - - return max; - } - - /// - /// 获取数组中的最小值 - /// [Obsolete("请直接使用 array.Min() (LINQ)")] - /// - /// 要获取最小值的数组 - /// 返回数组中的最小值 - [Obsolete("请直接使用 array.Min() (LINQ)", false)] - public static T Min(T[] array) where T : IComparable - { - if (IsEmpty(array)) - { - throw new ArgumentException("Array is empty."); - } - - T min = array[0]; - for (int i = 1; i < array.Length; i++) - { - if (array[i].CompareTo(min) < 0) - { - min = array[i]; - } - } - return min; - } - - /// - /// 获取数组中的和 - /// [Obsolete("请直接使用 array.Sum() (LINQ)")] - /// - /// 要获取和的数组 - /// 返回数组的和 - [Obsolete("请直接使用 array.Sum() (LINQ)", false)] - public static int Sum(int[] array) - { - if (IsEmpty(array)) - { - throw new ArgumentException("Array is empty."); - } - - int sum = 0; - for (int i = 0; i < array.Length; i++) - { - sum += array[i]; - } - - return sum; - } - - /// - /// 获取数组的平均值 - /// [Obsolete("请直接使用 array.Average() (LINQ)")] - /// - /// 要获取平均值的数组 - /// 返回数组的平均值 - [Obsolete("请直接使用 array.Average() (LINQ)", false)] - public static double Average(int[] array) - { - if (IsEmpty(array)) - { - throw new ArgumentException("Array is empty."); - } - - int sum = Sum(array); - int length = Length(array); - - return (double)sum / length; - } - - /// - /// 数组排序 - /// - /// 要排序的数组 - /// 返回排序后的数组 - public static T[] Sort(T[] array) where T : IComparable - { - if (IsEmpty(array)) - { - throw new ArgumentException("Array is empty."); - } - - T[] sortedArray = new T[array.Length]; - array.CopyTo(sortedArray, 0); - Array.Sort(sortedArray); - - return sortedArray; - } - - /// - /// 数组反转 - /// - /// 要反转的数组 - /// 返回反转后的数组 - public static T[] Reverse(T[] array) - { - if (IsEmpty(array)) - { - throw new ArgumentException("Array is empty."); - } - - T[] reversedArray = new T[array.Length]; - array.CopyTo(reversedArray, 0); - Array.Reverse(reversedArray); - - return reversedArray; - } - - /// - /// 判断数组是否包含某个元素 - /// [Obsolete("请直接使用 array.Contains(item) (LINQ)")] - /// - /// 要操作的数组 - /// 要判断的元素 - /// 如果数组中包含该元素,则返回 true;否则返回 false - [Obsolete("请直接使用 array.Contains(item) (LINQ)", false)] - public static bool Contains(T[] array, T item) - { - if (IsEmpty(array)) - { - throw new ArgumentException("Array is empty."); - } - for (int i = 0; i < array.Length; i++) - { - if (array[i].Equals(item)) - { - return true; - } - } - - return false; - } - - /// - /// 合并两个数组 - /// - /// 数组1 - /// 数组2 - /// 返回合并后的数组 - public static T[] Concat(T[] array1, T[] array2) - { - if (IsEmpty(array1)) - { - return array2; - } - - if (IsEmpty(array2)) - { - return array1; - } - - T[] concatedArray = new T[array1.Length + array2.Length]; - array1.CopyTo(concatedArray, 0); - array2.CopyTo(concatedArray, array1.Length); - - return concatedArray; - } - - /// - /// 判断两个数组是否完全相等 - /// - /// 数组1 - /// 数组2 - /// 如果两个数组完全相等,则返回 true;否则返回 false - public static bool Equals(T[] array1, T[] array2) - { - if (IsEmpty(array1) && IsEmpty(array2)) - { - return true; - } - - if (IsEmpty(array1) || IsEmpty(array2)) - { - return false; - } - - if (array1.Length != array2.Length) - { - return false; - } - - for (int i = 0; i < array1.Length; i++) - { - if (!array1[i].Equals(array2[i])) - { - return false; - } - } - - return true; - } - } -} diff --git a/EasyTool.Core/ToolCategory/ClassExtension.cs b/EasyTool.Core/ToolCategory/ClassExtension.cs deleted file mode 100644 index efc337d..0000000 --- a/EasyTool.Core/ToolCategory/ClassExtension.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; - -namespace EasyTool.Extension -{ - /// - /// ClassUtil 工具类提供了许多有用的方法,可以帮助您轻松处理和操作C#类 - /// - public static class ClassExtension - { - /// - /// 获取类的继承层次结构 - /// - /// 要获取继承层次结构的类 - /// 类的继承层次结构 - public static Type[] GetClassHierarchy(this Type type) => ClassUtil.GetClassHierarchy(type); - - - - - /// - /// 获取类的静态属性的值 - /// - /// 要获取静态属性的类 - /// 要获取的静态属性的名称 - /// 静态属性的值 - public static object GetStaticPropertyValue(this Type type, string propertyName) => ClassUtil.GetStaticPropertyValue(type, propertyName); - - - /// - /// 设置类的静态属性的值 - /// - /// 要设置静态属性的类 - /// 要设置的静态属性的名称 - /// 要设置的静态属性的值 - public static void SetStaticPropertyValue(this Type type, string propertyName, object value) => ClassUtil.SetStaticPropertyValue(type, propertyName, value); - - - /// - /// 获取类的静态字段的值 - /// - /// 要获取静态字段的类 - /// 要获取的静态字段的名称 - /// 静态字段的值 - public static object GetStaticFieldValue(this Type type, string fieldName) => ClassUtil.GetStaticFieldValue(type, fieldName); - - /// - /// 设置类的静态字段的值 - /// - /// 要设置静态字段的类 - /// 要设置的静态字段的名称 - /// 要设置的静态字段的值 - public static void SetStaticFieldValue(this Type type, string fieldName, object value) => ClassUtil.SetStaticFieldValue(type, fieldName, value); - - - /// - /// 动态调用类的静态方法 - /// - /// 要调用静态方法的类 - /// 要调用的静态方法的名称 - /// 要传递给静态方法的参数 - /// 静态方法的返回值 - public static object InvokeStaticMethod(this Type type, string methodName, object[] arguments) => ClassUtil.InvokeStaticMethod(type, methodName, arguments); - - ///// - ///// 动态调用类的实例方法 - ///// - ///// 要调用实例方法的类实例 - ///// 要调用的实例方法的名称 - ///// 要传递给实例方法的参数 - ///// 实例方法的返回值 - //public static object InvokeMethod(object instance, string methodName, object[] arguments) - //{ - // Type type = instance.GetType(); - // MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); - // return method.Invoke(instance, arguments); - //} - - /// - /// 动态创建类的实例 - /// - /// 要创建实例的类 - /// 要传递给构造函数的参数 - /// 类的新实例 - public static object CreateInstance(this Type type, object[] constructorArguments) => ClassUtil.CreateInstance(type, constructorArguments); - } -} diff --git a/EasyTool.Core/ToolCategory/ClassUtil.cs b/EasyTool.Core/ToolCategory/ClassUtil.cs deleted file mode 100644 index 5ee1647..0000000 --- a/EasyTool.Core/ToolCategory/ClassUtil.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; - -namespace EasyTool -{ - /// - /// ClassUtil 工具类提供了许多有用的方法,可以帮助您轻松处理和操作C#类 - /// - public class ClassUtil - { - /// - /// 获取类的完全限定名 - /// [Obsolete("请直接使用 type.FullName")] - /// - /// 要获取名称的类 - /// 类的完全限定名 - [Obsolete("请直接使用 type.FullName", false)] - public static string GetClassName(Type type) - { - return type.FullName; - } - - /// - /// 获取类的命名空间 - /// [Obsolete("请直接使用 type.Namespace")] - /// - /// 要获取命名空间的类 - /// 类的命名空间 - [Obsolete("请直接使用 type.Namespace", false)] - public static string GetClassNamespace(Type type) - { - return type.Namespace; - } - - /// - /// 获取类的继承层次结构 - /// - /// 要获取继承层次结构的类 - /// 类的继承层次结构 - public static Type[] GetClassHierarchy(Type type) - { - Type[] hierarchy = new Type[0]; - Type currentType = type; - while (currentType != null) - { - Array.Resize(ref hierarchy, hierarchy.Length + 1); - hierarchy[hierarchy.Length - 1] = currentType; - currentType = currentType.BaseType; - } - return hierarchy; - } - - /// - /// 获取类的所有方法 - /// [Obsolete("请直接使用 type.GetMethods()")] - /// - /// 要获取方法的类 - /// 类的所有方法 - [Obsolete("请直接使用 type.GetMethods()", false)] - public static MethodInfo[] GetClassMethods(Type type) - { - return type.GetMethods(); - } - - /// - /// 获取类的所有属性 - /// [Obsolete("请直接使用 type.GetProperties()")] - /// - /// 要获取属性的类 - /// 类的所有属性 - [Obsolete("请直接使用 type.GetProperties()", false)] - public static PropertyInfo[] GetClassProperties(Type type) - { - return type.GetProperties(); - } - - /// - /// 获取类的所有字段 - /// [Obsolete("请直接使用 type.GetFields()")] - /// - /// 要获取字段的类 - /// 类的所有字段 - [Obsolete("请直接使用 type.GetFields()", false)] - public static FieldInfo[] GetClassFields(Type type) - { - return type.GetFields(); - } - - /// - /// 获取类的所有事件 - /// [Obsolete("请直接使用 type.GetEvents()")] - /// - /// 要获取事件的类 - /// 类的所有事件 - [Obsolete("请直接使用 type.GetEvents()", false)] - public static EventInfo[] GetClassEvents(Type type) - { - return type.GetEvents(); - } - - /// - /// 获取类的所有构造函数 - /// [Obsolete("请直接使用 type.GetConstructors()")] - /// - /// 要获取构造函数的类 - /// 类的所有构造函数 - [Obsolete("请直接使用 type.GetConstructors()", false)] - public static ConstructorInfo[] GetClassConstructors(Type type) - { - return type.GetConstructors(); - } - - /// - /// 获取类的默认构造函数 - /// [Obsolete("请直接使用 type.GetConstructor(Type.EmptyTypes)")] - /// - /// 要获取默认构造函数的类 - /// 类的默认构造函数 - [Obsolete("请直接使用 type.GetConstructor(Type.EmptyTypes)", false)] - public static ConstructorInfo GetDefaultClassConstructor(Type type) - { - return type.GetConstructor(Type.EmptyTypes); - } - - /// - /// 获取类的静态属性的值 - /// - /// 要获取静态属性的类 - /// 要获取的静态属性的名称 - /// 静态属性的值 - public static object GetStaticPropertyValue(Type type, string propertyName) - { - PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public); - return property.GetValue(null); - } - - /// - /// 设置类的静态属性的值 - /// - /// 要设置静态属性的类 - /// 要设置的静态属性的名称 - /// 要设置的静态属性的值 - public static void SetStaticPropertyValue(Type type, string propertyName, object value) - { - PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public); - property.SetValue(null, value); - } - - /// - /// 获取类的静态字段的值 - /// - /// 要获取静态字段的类 - /// 要获取的静态字段的名称 - /// 静态字段的值 - public static object GetStaticFieldValue(Type type, string fieldName) - { - FieldInfo field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Public); - return field.GetValue(null); - } - - /// - /// 设置类的静态字段的值 - /// - /// 要设置静态字段的类 - /// 要设置的静态字段的名称 - /// 要设置的静态字段的值 - public static void SetStaticFieldValue(Type type, string fieldName, object value) - { - FieldInfo field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Public); - field.SetValue(null, value); - } - - /// - /// 动态调用类的静态方法 - /// - /// 要调用静态方法的类 - /// 要调用的静态方法的名称 - /// 要传递给静态方法的参数 - /// 静态方法的返回值 - public static object InvokeStaticMethod(Type type, string methodName, object[] arguments) - { - MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public); - return method.Invoke(null, arguments); - } - - /// - /// 动态调用类的实例方法 - /// - /// 要调用实例方法的类实例 - /// 要调用的实例方法的名称 - /// 要传递给实例方法的参数 - /// 实例方法的返回值 - public static object InvokeMethod(object instance, string methodName, object[] arguments) - { - Type type = instance.GetType(); - MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); - return method.Invoke(instance, arguments); - } - - /// - /// 动态创建类的实例 - /// - /// 要创建实例的类 - /// 要传递给构造函数的参数 - /// 类的新实例 - public static object CreateInstance(Type type, params object[] constructorArguments) - { - ConstructorInfo constructor = type.GetConstructor(GetParameterTypes(constructorArguments)); - return constructor.Invoke(constructorArguments); - } - - /// - /// 获取构造函数参数类型的数组 - /// - /// 要获取参数类型的参数数组 - /// 参数类型的数组 - private static Type[] GetParameterTypes(object[] parameters) - { - if (parameters == null) - { - return Type.EmptyTypes; - } - Type[] parameterTypes = new Type[parameters.Length]; - for (int i = 0; i < parameters.Length; i++) - { - if (parameters[i] == null) - { - parameterTypes[i] = typeof(object); - } - else - { - parameterTypes[i] = parameters[i].GetType(); - } - } - return parameterTypes; - } - } -} diff --git a/EasyTool.Core/ToolCategory/ColorExtension.cs b/EasyTool.Core/ToolCategory/ColorExtension.cs index 0298c0a..41f7913 100644 --- a/EasyTool.Core/ToolCategory/ColorExtension.cs +++ b/EasyTool.Core/ToolCategory/ColorExtension.cs @@ -279,19 +279,7 @@ public static bool IsLight(this Color color) #endregion #region 命名颜色 - - /// - /// 从名称创建颜色 - /// [Obsolete("请直接使用 Color.FromName(name)")] - /// - [Obsolete("请直接使用 Color.FromName(name)", false)] - public static Color FromName(string name) - { - if (string.IsNullOrEmpty(name)) - return Color.Empty; - - return Color.FromName(name); - } + #endregion /// /// 获取颜色名称 @@ -304,7 +292,7 @@ public static string GetColorName(this Color color) return color.ToHex(); } - #endregion + // endregion #region 颜色对比 diff --git a/EasyTool.Core/ToolCategory/DLLUtil.cs b/EasyTool.Core/ToolCategory/DLLUtil.cs deleted file mode 100644 index d14a199..0000000 --- a/EasyTool.Core/ToolCategory/DLLUtil.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; - -namespace EasyTool -{ - /// - /// dll工具 - /// - public class DLLUtil - { - /// - /// 根据文件路径加载 DLL 程序集,并返回一个 Assembly 对象 - /// [Obsolete("请直接使用 Assembly.LoadFile(dllFilePath)")] - /// - /// DLL 文件路径 - /// 返回一个 Assembly 对象 - [Obsolete("请直接使用 Assembly.LoadFile(dllFilePath)", false)] - public static Assembly LoadAssembly(string dllFilePath) - { - return Assembly.LoadFile(dllFilePath); - } - - /// - /// 根据类型名称从程序集中获取 Type 对象 - /// [Obsolete("请直接使用 assembly.GetType(typeName)")] - /// - /// 程序集 - /// 类型名称 - /// 返回 Type 对象 - [Obsolete("请直接使用 assembly.GetType(typeName)", false)] - public static Type? GetTypeFromAssembly(Assembly assembly, string typeName) - { - return assembly.GetType(typeName); - } - - /// - /// 创建指定类型的实例,并返回一个 Object 对象 - /// [Obsolete("请直接使用 Activator.CreateInstance(type, parameters)")] - /// - /// 要创建实例的类型 - /// 实例化类型所需要的参数 - /// 返回创建的实例对象 - [Obsolete("请直接使用 Activator.CreateInstance(type, parameters)", false)] - public static object? CreateInstance(Type type, params object[] parameters) - { - return Activator.CreateInstance(type, parameters); - } - - /// - /// 根据类型名称创建实例,并返回一个 Object 对象 - /// - /// 程序集 - /// 类型名称 - /// 实例化类型所需要的参数 - /// 返回创建的实例对象 - public static object? CreateInstanceFromAssembly(Assembly assembly, string typeName, params object[] parameters) - { - Type? type = GetTypeFromAssembly(assembly, typeName); - if (type != null) - { - return CreateInstance(type, parameters); - } - return null; - } - - /// - /// 调用对象的方法,并返回调用结果 - /// - /// 要调用方法的对象 - /// 方法名称 - /// 方法所需要的参数 - /// 返回调用结果 - public static object? InvokeMethod(object instance, string methodName, params object[] parameters) - { - Type type = instance.GetType(); - MethodInfo? methodInfo = type.GetMethod(methodName); - if (methodInfo != null) - { - return methodInfo.Invoke(instance, parameters); - } - else - { - return null; - } - } - - /// - /// 获取程序集中所有的类型信息 - /// [Obsolete("请直接使用 assembly.GetTypes()")] - /// - /// 程序集 - /// 返回 Type[] 数组,数组中每个元素代表程序集中的一个类型 - [Obsolete("请直接使用 assembly.GetTypes()", false)] - public static Type[] GetAllTypesFromAssembly(Assembly assembly) - { - return assembly.GetTypes(); - } - - /// - /// 判断指定类型是否实现了指定的接口 - /// [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)")] - /// - /// 要判断的类型 - /// 要判断的接口类型 - /// 返回布尔值,表示指定类型是否实现了指定的接口 - [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)", false)] - public static bool IsImplementInterface(Type type, Type interfaceType) - { - return interfaceType.IsAssignableFrom(type); - } - - /// - /// 从指定目录中加载所有的 DLL 文件,并返回一个 Assembly[] 数组 - /// - /// 要加载 DLL 文件的目录 - /// 返回一个 Assembly[] 数组,数组中每个元素代表一个 DLL 程序集 - public static Assembly[] LoadAllDllsFromDirectory(string directory) - { - try - { - if (!Directory.Exists(directory)) - { - throw new Exception("LoadAllDllsFromDirectory Error: Directory not exist."); - } - - string[] dllFiles = Directory.GetFiles(directory, "*.dll"); - if (dllFiles.Length == 0) - { - throw new Exception("LoadAllDllsFromDirectory Error: No DLL file found."); - } - - Assembly[] assemblies = new Assembly[dllFiles.Length]; - for (int i = 0; i < dllFiles.Length; i++) - { - assemblies[i] = LoadAssembly(dllFiles[i]); - } - return assemblies; - } - catch (Exception ex) - { - throw new Exception("LoadAllDllsFromDirectory Error: " + ex.Message); - } - } - } -} diff --git a/EasyTool.Core/ToolCategory/EnumExtension.cs b/EasyTool.Core/ToolCategory/EnumExtension.cs index bd117b5..6605335 100644 --- a/EasyTool.Core/ToolCategory/EnumExtension.cs +++ b/EasyTool.Core/ToolCategory/EnumExtension.cs @@ -48,18 +48,6 @@ public static string GetDisplayName(this Enum value) #region 枚举转换 - /// - /// 将枚举值转换为整数 - /// [Obsolete("请直接使用 Convert.ToInt32(value)")] - /// - [Obsolete("请直接使用 Convert.ToInt32(value)", false)] - public static int ToInt(this Enum value) - { - if (value == null) - return 0; - - return Convert.ToInt32(value); - } /// /// 将整数转换为枚举 @@ -88,19 +76,57 @@ public static T ParseEnumOrDefault(this string value, T defaultValue = defaul return defaultValue; } + + #endregion + + #region 枚举字典扩展 + /// - /// 尝试解析字符串为枚举 - /// [Obsolete("请直接使用 Enum.TryParse(value, true, out result)")] + /// 获取枚举类型的所有成员的名称和值的键值对 /// - [Obsolete("请直接使用 Enum.TryParse(value, true, out result)", false)] - public static bool TryParseEnum(this string value, out T result) where T : struct, Enum + public static IDictionary ToNameDictionary() where T : struct, Enum { - return Enum.TryParse(value, true, out result); + var valuesDictionary = new Dictionary(); + foreach (T value in GetValues()) + { + valuesDictionary.Add(Enum.GetName(typeof(T), value)!, value); + } + return valuesDictionary; + } + + /// + /// 获取指定枚举值的描述 + /// + public static string? GetDescriptionByValue(T value) where T : struct, Enum + { + var name = Enum.GetName(typeof(T), value); + if (string.IsNullOrEmpty(name)) + { + return null; + } + var field = typeof(T).GetField(name); + var attr = field?.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute; + return attr?.Description; + } + + /// + /// 获取指定枚举值的显示名称 + /// + public static string? GetDisplayNameByValue(T value) where T : struct, Enum + { + var name = Enum.GetName(typeof(T), value); + if (string.IsNullOrEmpty(name)) + { + return null; + } + var field = typeof(T).GetField(name); + var attr = field?.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false).FirstOrDefault() as System.ComponentModel.DataAnnotations.DisplayAttribute; + return attr?.GetName(); } #endregion - #region 枚举集合 + #region 枚举判断 /// /// 获取枚举类型的所有值 @@ -148,15 +174,6 @@ public static Dictionary ToDisplayNameDictionary() where T : struc #region 枚举判断 - /// - /// 判断是否是定义的枚举值 - /// [Obsolete("请直接使用 Enum.IsDefined(typeof(T), value)")] - /// - [Obsolete("请直接使用 Enum.IsDefined(typeof(T), value)", false)] - public static bool IsDefined(this T value) where T : struct, Enum - { - return Enum.IsDefined(typeof(T), value); - } /// /// 判断字符串是否是有效的枚举名称或值 diff --git a/EasyTool.Core/ToolCategory/EnumUtil.cs b/EasyTool.Core/ToolCategory/EnumUtil.cs deleted file mode 100644 index f03522b..0000000 --- a/EasyTool.Core/ToolCategory/EnumUtil.cs +++ /dev/null @@ -1,253 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace EasyTool -{ - /// - /// 枚举工具类,包含了各种枚举操作的方法 - /// - public class EnumUtil - { - /// - /// 获取指定枚举类型的所有成员名称 - /// [Obsolete("请直接使用 Enum.GetNames(typeof(TEnum))")] - /// - /// 要获取成员名称的枚举类型 - /// 所有成员名称的字符串数组 - [Obsolete("请直接使用 Enum.GetNames(typeof(TEnum))", false)] - public static string[] GetNames() - { - return Enum.GetNames(typeof(TEnum)); - } - - /// - /// 获取指定枚举类型的所有成员的值 - /// [Obsolete("请直接使用 (TEnum[])Enum.GetValues(typeof(TEnum))")] - /// - /// 要获取成员值的枚举类型 - /// 所有成员值的数组 - [Obsolete("请直接使用 (TEnum[])Enum.GetValues(typeof(TEnum))", false)] - public static TEnum[] GetValues() - { - return (TEnum[])Enum.GetValues(typeof(TEnum)); - } - - /// - /// 获取指定枚举值的名称 - /// [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)")] - /// - /// 枚举类型 - /// 枚举值 - /// 枚举值的名称 - [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)", false)] - public static string GetName(TEnum value) - { - return Enum.GetName(typeof(TEnum), value); - } - - /// - /// 检查指定的值是否是枚举类型TEnum的成员 - /// [Obsolete("请直接使用 Enum.IsDefined(typeof(TEnum), value)")] - /// - /// 枚举类型 - /// 要检查的值 - /// 如果指定的值是TEnum的成员,则为true;否则为false - [Obsolete("请直接使用 Enum.IsDefined(typeof(TEnum), value)", false)] - public static bool IsDefined(object value) - { - return Enum.IsDefined(typeof(TEnum), value); - } - - /// - /// 将字符串转换为枚举类型TEnum的值 - /// [Obsolete("请直接使用 (TEnum)Enum.Parse(typeof(TEnum), value)")] - /// - /// 枚举类型 - /// 要转换的字符串 - /// 与字符串对应的枚举值 - [Obsolete("请直接使用 (TEnum)Enum.Parse(typeof(TEnum), value)", false)] - public static TEnum Parse(string value) - { - return (TEnum)Enum.Parse(typeof(TEnum), value); - } - - /// - /// 将字符串转换为枚举类型TEnum的值如果字符串无法转换,则返回默认值 - /// - /// 枚举类型 - /// 要转换的字符串 - /// 默认值 - /// 与字符串对应的枚举值,或默认值(如果字符串无法转换) - public static TEnum Parse(string value, TEnum defaultValue) - { - var result= Enum.Parse(typeof(TEnum), value); - return result!=null ? (TEnum)result : defaultValue; - } - - /// - /// 获取指定枚举类型的Type对象 - /// [Obsolete("请直接使用 typeof(TEnum)")] - /// - /// 枚举类型 - /// 枚举类型的Type对象 - [Obsolete("请直接使用 typeof(TEnum)", false)] - public static Type GetEnumType() - { - return typeof(TEnum); - } - - /// - /// 获取指定枚举类型的所有成员的名称和值的键值对 - /// - /// 枚举类型 - /// 所有成员名称和值的键值对 - public static IDictionary GetValuesDictionary() - { - var valuesDictionary = new Dictionary(); - foreach (var value in GetValues()) - { - valuesDictionary.Add(GetName(value), value); - } - return valuesDictionary; - } - - /// - /// 获取指定枚举类型的所有成员的注释 - /// - /// 枚举类型 - /// 所有成员注释的字典,其中键是成员名称,值是注释内容 - public static IDictionary GetDescriptions() - { - var descriptions = new Dictionary(); - var enumType = GetEnumType(); - foreach (var memberInfo in enumType.GetMembers()) - { - var attribute = memberInfo.GetCustomAttribute(); - if (attribute != null) - { - descriptions.Add(memberInfo.Name, attribute.Description); - } - } - return descriptions; - } - - /// - /// 获取指定枚举类型的指定成员的注释 - /// - /// 枚举类型 - /// 枚举成员 - /// 成员注释的字符串 - public static string GetDescription(TEnum value) - { - var memberInfo = GetEnumType().GetMember(value.ToString()).FirstOrDefault(); - if (memberInfo == null) - { - return string.Empty; - } - - var attribute = memberInfo.GetCustomAttribute(); - return attribute != null ? attribute.Description : string.Empty; - } - - /// - /// 获取指定枚举类型的指定成员的Display名称 - /// - /// 枚举类型 - /// 枚举成员 - /// 成员的Display名称,如果未设置,则返回枚举成员的名称 - public static string GetDisplayName(TEnum value) - { - var memberInfo = GetEnumType().GetMember(value.ToString()).FirstOrDefault(); - if (memberInfo == null) - { - return string.Empty; - } - - var attribute = memberInfo.GetCustomAttribute(); - return attribute != null ? attribute.Name : value.ToString(); - } - - /// - /// 获取指定枚举类型的所有成员的Display名称 - /// - /// 枚举类型 - /// 所有成员的Display名称的字典,其中键是成员名称,值是Display名称 - public static IDictionary GetDisplayNames() - { - var displayNames = new Dictionary(); - var enumType = GetEnumType(); - foreach (var memberInfo in enumType.GetMembers()) - { - var attribute = memberInfo.GetCustomAttribute(); - if (attribute != null) - { - displayNames.Add(memberInfo.Name, attribute.Name); - } - } - return displayNames; - } - - /// - /// 获取指定枚举类型的指定成员的值 - /// - /// 枚举类型 - /// 成员名称 - /// 成员的值,如果成员不存在,则返回默认值 - public static TEnum GetValueByName(string name) - { - return Parse(name, default(TEnum)); - } - - /// - /// 获取指定枚举类型的指定值的名称 - /// [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)")] - /// - /// 枚举类型 - /// 枚举值 - /// 与值对应的名称,如果值不存在,则返回null - [Obsolete("请直接使用 Enum.GetName(typeof(TEnum), value)", false)] - public static string? GetNameByValue(TEnum value) - { - return Enum.GetName(typeof(TEnum), value!); - } - - /// - /// 获取指定枚举类型的指定值的注释 - /// - /// 枚举类型 - /// 枚举值 - /// 与值对应的注释,如果值不存在或未设置注释,则返回null - public static string? GetDescriptionByValue(TEnum value) - { - var name = GetNameByValue(value); - if (string.IsNullOrEmpty(name)) - { - return null; - } - - return GetDescription(GetValueByName(name!)); - } - - /// - /// 获取指定枚举类型的指定值的Display名称 - /// - /// 枚举类型 - /// 枚举值 - /// 与值对应的Display名称,如果值不存在或未设置Display名称,则返回null - public static string? GetDisplayNameByValue(TEnum value) - { - var name = GetNameByValue(value); - if (string.IsNullOrEmpty(name)) - { - return null; - } - - return GetDisplayName(GetValueByName(name!)); - } - } -} diff --git a/EasyTool.Core/ToolCategory/EscapeUtil.cs b/EasyTool.Core/ToolCategory/EscapeUtil.cs deleted file mode 100644 index eea8c2f..0000000 --- a/EasyTool.Core/ToolCategory/EscapeUtil.cs +++ /dev/null @@ -1,211 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace EasyTool -{ - /// - /// 转义和反转义工具类 - /// - public class EscapeUtil - { - /// - /// 将字符串中的特殊字符进行转义 - /// - /// 需要转义的字符串 - /// 转义后的字符串 - public static string Escape(string str) - { - if (string.IsNullOrEmpty(str)) - { - return str; - } - - string escaped = Regex.Replace(str, @"[\a\b\f\n\r\t\v\\""]", m => { - switch (m.Value) - { - case "\a": - return @"\a"; - case "\b": - return @"\b"; - case "\f": - return @"\f"; - case "\n": - return @"\n"; - case "\r": - return @"\r"; - case "\t": - return @"\t"; - case "\v": - return @"\v"; - case "\\": - return @"\\"; - case "\"": - return @"\"""; - default: - return m.Value; - } - }); - - return escaped; - } - - /// - /// 将字符串中的转义字符还原成特殊字符 - /// - /// 需要还原的字符串 - /// 还原后的字符串 - public static string Unescape(string str) - { - if (string.IsNullOrEmpty(str)) - { - return str; - } - - string unescaped = Regex.Replace(str, @"\\[a-z""\\]", m => { - switch (m.Value) - { - case @"\a": - return "\a"; - case @"\b": - return "\b"; - case @"\f": - return "\f"; - case @"\n": - return "\n"; - case @"\r": - return "\r"; - case @"\t": - return "\t"; - case @"\v": - return "\v"; - case @"\\": - return "\\"; - case @"\""": - return "\""; - default: - return m.Value; - } - }); - - return unescaped; - } - - /// - /// 将URL中的特殊字符进行转义 - /// [Obsolete("请直接使用 Uri.EscapeDataString(url)")] - /// - /// 需要转义的URL - /// 转义后的URL - [Obsolete("请直接使用 Uri.EscapeDataString(url)", false)] - public static string UrlEncode(string url) - { - if (string.IsNullOrEmpty(url)) - { - return url; - } - - return Uri.EscapeDataString(url); - } - - /// - /// 将URL中的转义字符还原成特殊字符 - /// [Obsolete("请直接使用 Uri.UnescapeDataString(url)")] - /// - /// 需要还原的URL - /// 还原后的URL - [Obsolete("请直接使用 Uri.UnescapeDataString(url)", false)] - public static string UrlDecode(string url) - { - if (string.IsNullOrEmpty(url)) - { - return url; - } - - return Uri.UnescapeDataString(url); - } - - /// - /// 将HTML字符串进行转义,将特殊字符替换成HTML实体 - /// [Obsolete("请直接使用 System.Net.WebUtility.HtmlEncode(html)")] - /// - /// 需要转义的HTML字符串 - /// 转义后的HTML字符串 - [Obsolete("请直接使用 System.Net.WebUtility.HtmlEncode(html)", false)] - public static string HtmlEncode(string html) - { - if (string.IsNullOrEmpty(html)) - { - return html; - } - - return System.Net.WebUtility.HtmlEncode(html); - } - - /// - /// 将HTML字符串中的HTML实体还原成特殊字符 - /// [Obsolete("请直接使用 System.Net.WebUtility.HtmlDecode(html)")] - /// - /// 需要还原的HTML字符串 - /// 还原后的HTML字符串 - [Obsolete("请直接使用 System.Net.WebUtility.HtmlDecode(html)", false)] - public static string HtmlDecode(string html) - { - if (string.IsNullOrEmpty(html)) - { - return html; - } - - return System.Net.WebUtility.HtmlDecode(html); - } - - /// - /// 将XML字符串进行转义,将特殊字符替换成XML实体 - /// [Obsolete("请直接使用 System.Security.SecurityElement.Escape(xml)")] - /// - /// 需要转义的XML字符串 - /// 转义后的XML字符串 - [Obsolete("请直接使用 System.Security.SecurityElement.Escape(xml)", false)] - public static string XmlEncode(string xml) - { - if (string.IsNullOrEmpty(xml)) - { - return xml; - } - - return System.Security.SecurityElement.Escape(xml); - } - - /// - /// 将XML字符串中的XML实体还原成特殊字符 - /// - /// 需要还原的XML字符串 - /// 还原后的XML字符串 - public static string XmlDecode(string xml) - { - if (string.IsNullOrEmpty(xml)) - { - return xml; - } - - return Regex.Replace(xml, @"&[a-zA-Z]+;", m => { - switch (m.Value) - { - case "&": - return "&"; - case "<": - return "<"; - case ">": - return ">"; - case """: - return "\""; - case "'": - return "'"; - default: - return m.Value; - } - }); - } - } -} diff --git a/EasyTool.Core/ToolCategory/ExceptionExtension.cs b/EasyTool.Core/ToolCategory/ExceptionExtension.cs index 6ebc43e..91824bc 100644 --- a/EasyTool.Core/ToolCategory/ExceptionExtension.cs +++ b/EasyTool.Core/ToolCategory/ExceptionExtension.cs @@ -320,18 +320,6 @@ public static Exception[] GetInnerExceptions(this AggregateException? exception) return exception.InnerExceptions.ToArray(); } - /// - /// 展平聚合异常(递归获取所有内层异常) - /// [Obsolete("请直接使用 exception.Flatten().InnerExceptions.ToArray()")] - /// - [Obsolete("请直接使用 exception.Flatten().InnerExceptions.ToArray()", false)] - public static Exception[] Flatten(this AggregateException? exception) - { - if (exception == null) - return Array.Empty(); - - return exception.Flatten().InnerExceptions.ToArray(); - } #endregion } diff --git a/EasyTool.Core/ToolCategory/GuidExtension.cs b/EasyTool.Core/ToolCategory/GuidExtension.cs index 4f04619..39f97fd 100644 --- a/EasyTool.Core/ToolCategory/GuidExtension.cs +++ b/EasyTool.Core/ToolCategory/GuidExtension.cs @@ -10,15 +10,6 @@ public static class GuidExtension { #region 空值判断 - /// - /// 判断 Guid 是否为空 - /// [Obsolete("请直接使用 guid == Guid.Empty")] - /// - [Obsolete("请直接使用 guid == Guid.Empty", false)] - public static bool IsEmpty(this Guid guid) - { - return guid == Guid.Empty; - } /// /// 判断 Guid 是否为空或默认值 @@ -80,15 +71,6 @@ public static string ToBracedString(this Guid guid) return guid.ToString("B"); } - /// - /// 获取 Guid 的字节数组表示 - /// [Obsolete("请直接使用 guid.ToByteArray()")] - /// - [Obsolete("请直接使用 guid.ToByteArray()", false)] - public static byte[] ToByteArray(this Guid guid) - { - return guid.ToByteArray(); - } /// /// 将 Guid 转换为 Base64 字符串 diff --git a/EasyTool.Core/ToolCategory/IdcardUtil.cs b/EasyTool.Core/ToolCategory/IdcardUtil.cs deleted file mode 100644 index 0f6d9bd..0000000 --- a/EasyTool.Core/ToolCategory/IdcardUtil.cs +++ /dev/null @@ -1,470 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; - -namespace EasyTool -{ - public class IdcardUtil - { - /// - /// 验证身份证号码是否合法 - /// - /// 身份证号码 - /// 验证结果 - public static bool IsValid(string idcard) - { - if (string.IsNullOrEmpty(idcard)) - { - return false; - } - - if (idcard.Length == 15) - { - return IsValid15(idcard); - } - else if (idcard.Length == 18) - { - return IsValid18(idcard); - } - else - { - return false; - } - } - - /// - /// 验证 15 位身份证号码是否合法 - /// - /// 15 位身份证号码 - /// 验证结果 - public static bool IsValid15(string idcard) - { - if (string.IsNullOrEmpty(idcard)) - { - return false; - } - - if (!Regex.IsMatch(idcard, @"^\d{15}$")) - { - return false; - } - - if (!IsValidArea(idcard.Substring(0, 6))) - { - return false; - } - - DateTime birthday; - if (!DateTime.TryParseExact(idcard.Substring(6, 6), "yyMMdd", null, System.Globalization.DateTimeStyles.None, out birthday)) - { - return false; - } - - return true; - } - - /// - /// 验证 18 位身份证号码是否合法 - /// - /// 18 位身份证号码 - /// 验证结果 - public static bool IsValid18(string idcard) - { - if (string.IsNullOrEmpty(idcard)) - { - return false; - } - - if (!Regex.IsMatch(idcard, @"^\d{17}[\dX]$")) - { - return false; - } - - if (!IsValidArea(idcard.Substring(0, 6))) - { - return false; - } - - DateTime birthday; - if (!DateTime.TryParseExact(idcard.Substring(6, 8), "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out birthday)) - { - return false; - } - - if (!IsValidChecksum(idcard)) - { - return false; - } - - return true; - } - - /// - /// 判断给定的区域代码是否合法 - /// - /// 区域代码 - /// 是否合法 - public static bool IsValidArea(string area) - { - if (string.IsNullOrEmpty(area)) - { - return false; - } - - string[] areas = new string[] { - "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", - "33", "34", "35", "36", "37", "41", "42", "43", "44", "45", - "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", - "65" - }; - - return areas.Contains(area); - } - - /// - /// 验证身份证号码的校验位是否正确 - /// - /// 身份证号码 - /// 验证结果 - public static bool IsValidChecksum(string idcard) - { - int[] weights = new int[] { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 }; - string[] checksums = new string[] { "1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2" }; - - int sum = 0; - for (int i = 0; i < 17; i++) - { - sum += int.Parse(idcard[i].ToString()) * weights[i]; - } - - int checksumIndex = sum % 11; - string expectedChecksum = checksums[checksumIndex]; - - return idcard[17].ToString().ToUpper() == expectedChecksum.ToUpper(); - } - - /// - /// 从身份证号码中获取生日 - /// - /// 身份证号码 - /// 生日 - public static DateTime? GetBirthday(string idcard) - { - if (string.IsNullOrEmpty(idcard)) - { - return null; - } - - if (idcard.Length == 15) - { - if (!IsValid15(idcard)) - { - return null; - } - - DateTime birthday; - if (DateTime.TryParseExact(idcard.Substring(6, 6), "yyMMdd", null, System.Globalization.DateTimeStyles.None, out birthday)) - { - return birthday; - } - else - { - return null; - } - } - else if (idcard.Length == 18) - { - if (!IsValid18(idcard)) - { - return null; - } - - DateTime birthday; - if (DateTime.TryParseExact(idcard.Substring(6, 8), "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out birthday)) - { - return birthday; - } - else - { - return null; - } - } - else - { - return null; - } - } - - /// - /// 从身份证号码中获取性别 - /// - /// 身份证号码 - /// 性别 - public static Gender? GetGender(string idcard) - { - if (string.IsNullOrEmpty(idcard)) - { - return null; - } - - if (idcard.Length == 15) - { - if (!IsValid15(idcard)) - { - return null; - } - - int genderCode = int.Parse(idcard.Substring(14, 1)); - return genderCode % 2 == 1 ? Gender.Male : Gender.Female; - } - else if (idcard.Length == 18) - { - if (!IsValid18(idcard)) - { - return null; - } - - int genderCode = int.Parse(idcard.Substring(16, 1)); - return genderCode % 2 == 1 ? Gender.Male : Gender.Female; - } - else - { - return null; - } - } - - /// - /// 从身份证号码中获取 - /// - /// 身份证号码 - /// 省份 - public static string? GetProvince(string? idcard) - { - if (string.IsNullOrEmpty(idcard)) - { - return null; - } - - if (IsValid(idcard)) - { - string[] areas = new string[] { - "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", - "33", "34", "35", "36", "37", "41", "42", "43", "44", "45", - "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", - "65" - }; - - string provinceCode = idcard.Substring(0, 2); - if (areas.Contains(provinceCode)) - { - switch (provinceCode) - { - case "11": - return "北京市"; - case "12": - return "天津市"; - case "13": - return "河北省"; - case "14": - return "山西省"; - case "15": - return "内蒙古自治区"; - case "21": - return "辽宁省"; - case "22": - return "吉林省"; - case "23": - return "黑龙江省"; - case "31": - return "上海市"; - case "32": - return "江苏省"; - case "33": - return "浙江省"; - case "34": - return "安徽省"; - case "35": - return "福建省"; - case "36": - return "江西省"; - case "37": - return "山东省"; - case "41": - return "河南省"; - case "42": - return "湖北省"; - case "43": - return "湖南省"; - case "44": - return "广东省"; - case "45": - return "广西壮族自治区"; - case "46": - return "海南省"; - case "50": - return "重庆市"; - case "51": - return "四川省"; - case "52": - return "贵州省"; - case "53": - return "云南省"; - case "54": - return "西藏自治区"; - case "61": - return "陕西省"; - case "62": - return "甘肃省"; - case "63": - return "青海省"; - case "64": - return "宁夏回族自治区"; - case "65": - return "新疆维吾尔自治区"; - default: - return null; - } - } - else - { - return null; - } - } - else - { - return null; - } - } - - /// - /// 将身份证号码中的生日部分替换成指定的日期,并返回新的身份证号码 - /// - /// 身份证号码 - /// 新的生日日期 - /// 新的身份证号码 - public static string? ReplaceBirthday(string? idcard, DateTime birthday) - { - if (string.IsNullOrEmpty(idcard)) - { - return null; - } - - if (!IsValid(idcard)) - { - return null; - } - - string birthdayStr = birthday.ToString("yyyyMMdd"); - if (idcard.Length == 15) - { - return idcard.Substring(0, 6) + birthdayStr + idcard.Substring(12); - } - else if (idcard.Length == 18) - { - return idcard.Substring(0, 6) + birthdayStr + idcard.Substring(14); - } - else - { - return null; - } - } - - /// - /// 将身份证号码中的性别部分替换成指定的性别,并返回新的身份证号码 - /// - /// 身份证号码 - /// 新的性别 - /// 新的身份证号码 - public static string? ReplaceGender(string idcard, Gender gender) - { - if (string.IsNullOrEmpty(idcard)) - { - return null; - } - - if (!IsValid(idcard)) - { - return null; - } - - int genderCode = gender == Gender.Male ? 1 : 2; - if (idcard.Length == 15) - { - return idcard.Substring(0, 14) + genderCode.ToString(); - } - else if (idcard.Length == 18) - { - return idcard.Substring(0, 16) + genderCode.ToString() + idcard.Substring(17); - } - else - { - return null; - } - } - - /// - /// 生成一个随机的身份证号码 - /// - /// 性别 - /// 最小年龄 - /// 最大年龄 - /// 随机的身份证号码 - public static string GenerateRandomIdcard(Gender gender = Gender.Male, int minAge = 18, int maxAge = 65) - { - DateTime now = DateTime.Now; - DateTime minBirthday = now.AddYears(-maxAge); - DateTime maxBirthday = now.AddYears(-minAge); - - DateTime birthday = RandomUtil.GetRandomDateTime(minBirthday, maxBirthday); - string area = GetRandomArea(); - int sequence = RandomUtil.GetRandomInt(1, 999); - int genderCode = gender == Gender.Male ? 1 : 2; - - string idcard = string.Format("{0}{1:yyyyMMdd}{2:D3}{3:D1}", area, birthday, sequence, genderCode); - if (!IsValid(idcard)) - { - return GenerateRandomIdcard(gender, minAge, maxAge); - } - else - { - return idcard; - } - } - - /// - /// 获取一个随机的身份证号码的区域代码 - /// - /// 区域代码 - private static string GetRandomArea() - { - string[] areas = new string[] { - "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", - "33", "34", "35", "36", "37", "41", "42", "43", "44", "45", - "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", - "65" - }; - - int index = RandomUtil.GetRandomInt(0, areas.Length - 1); - return areas[index]; - } - - - /// - /// 性别枚举 - /// - public enum Gender - { - /// - /// 男性 - /// - Male, - /// - /// 女性 - /// - Female - } - } -} diff --git a/EasyTool.Core/ToolCategory/MEFUtil.cs b/EasyTool.Core/ToolCategory/MEFUtil.cs deleted file mode 100644 index 481bfa5..0000000 --- a/EasyTool.Core/ToolCategory/MEFUtil.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition.Hosting; -using System.Reflection; -using System.Linq; -using System.IO; - -namespace EasyTool -{ - /// - /// MEF加载工具 - /// - public class MEFUtil - { - // 默认扫描程序集的路径 - private static readonly string DefaultDirectory = AppDomain.CurrentDomain.BaseDirectory; - - /// - /// 从指定目录动态加载导出部件 - /// - /// 导出部件的类型 - /// 目录路径 - /// 导出部件的列表 - public static IEnumerable LoadExportParts(string? directory = null) - { - // 如果目录为空,则使用默认目录 - directory ??= DefaultDirectory; - - // 创建目录目录目录目录 - var catalog = new DirectoryCatalog(directory); - // 创建容器并将目录添加到容器中 - var container = new CompositionContainer(catalog); - - // 从容器中获取导出的部件 - var parts = container.GetExportedValues(); - return parts; - } - - /// - /// 从指定程序集动态加载导出部件 - /// - /// 导出部件的类型 - /// 程序集名称 - /// 导出部件的列表 - public static IEnumerable LoadExportPartsFromAssembly(string assemblyName) - { - // 加载指定的程序集 - var assembly = Assembly.Load(assemblyName); - // 创建程序集目录 - var catalog = new AssemblyCatalog(assembly); - // 创建容器并将目录添加到容器中 - var container = new CompositionContainer(catalog); - - // 从容器中获取导出的部件 - var parts = container.GetExportedValues(); - return parts; - } - - /// - /// 从指定文件夹中加载所有导出部件 - /// - /// 导出部件类型 - /// 指定文件夹路径 - /// 搜索选项 - /// 导出部件列表 - public static IEnumerable LoadExportPartsFromFolder(string folderPath, SearchOption searchOption = SearchOption.AllDirectories) - { - var catalog = new DirectoryCatalog(folderPath, "*.dll"); - using var container = new CompositionContainer(catalog); - return container.GetExportedValues(); - } - - /// - /// 从指定类型中加载所有导出部件 - /// - /// 导出部件类型 - /// 指定类型 - /// 导出部件列表 - public static IEnumerable LoadExportPartsFromType(Type type) - { - var catalog = new TypeCatalog(type); - using var container = new CompositionContainer(catalog); - return container.GetExportedValues(); - } - - /// - /// 加载多个目录中的导出部件 - /// - /// 导出部件类型 - /// 多个目录路径 - /// 导出部件列表 - public static IEnumerable LoadExportPartsFromFolders(IEnumerable folderPaths) - { - var catalogs = folderPaths.Select(path => new DirectoryCatalog(path, "*.dll")); - var aggregateCatalog = new AggregateCatalog(catalogs); - using var container = new CompositionContainer(aggregateCatalog); - return container.GetExportedValues(); - } - - /// - /// 从指定容器中获取导入部件 - /// - /// 导入部件的类型 - /// 容器 - /// 导入部件的实例 - public static T GetImportPart(CompositionContainer container) - { - // 获取导入部件的实例 - var part = container.GetExportedValue(); - return part; - } - - /// - /// 从指定容器中获取导入部件的列表 - /// - /// 导入部件的类型 - /// 容器 - /// 导入部件的列表 - public static IEnumerable GetImportParts(CompositionContainer container) - { - // 获取导入部件的列表 - var parts = container.GetExportedValues(); - return parts; - } - } -} diff --git a/EasyTool.Core/ToolCategory/ObjectExtension.cs b/EasyTool.Core/ToolCategory/ObjectExtension.cs index b14b515..8513661 100644 --- a/EasyTool.Core/ToolCategory/ObjectExtension.cs +++ b/EasyTool.Core/ToolCategory/ObjectExtension.cs @@ -1,10 +1,12 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; +using System.Threading.Tasks; using System.Xml.Serialization; namespace EasyTool.Extension @@ -185,6 +187,18 @@ public static T ToOrDefault(this object obj, T defaultValue) return json.FromJson(); } + /// + /// 异步深拷贝对象(使用 JSON 序列化) + /// + public static async Task DeepCloneAsync(this T obj) + { + if (obj == null) + return default; + + var json = await Task.Run(() => obj.ToJson()); + return await Task.Run(() => json.FromJson()); + } + /// /// 浅拷贝对象(使用 MemberwiseClone) /// @@ -347,15 +361,6 @@ public static T Pipe(this T obj, Action action) #region 对象检查 - /// - /// 判断对象是否是指定类型 - /// [Obsolete("请直接使用 obj is T")] - /// - [Obsolete("请直接使用 obj is T", false)] - public static bool Is(this object obj) - { - return obj is T; - } /// /// 判断对象是否实现了指定接口 @@ -405,47 +410,481 @@ public static bool PropertiesEqual(this T obj, T? other) where T : class #region 对象信息 /// - /// 获取对象的类型名称 - /// [Obsolete("请直接使用 obj?.GetType().Name")] + /// 获取指定类型的默认值 + /// + public static object? GetDefault(Type type) + { + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + + /// + /// 判断对象是否是其类型的默认值 + /// + public static bool IsDefaultValue(this object obj) + { + return obj == null || obj.Equals(GetDefault(obj.GetType())); + } + + /// + /// 判断指定类型是否是可空值类型 + /// + public static bool IsNullable(Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + /// + /// 获取可空类型的基础类型 /// - [Obsolete("请直接使用 obj?.GetType().Name", false)] - public static string GetTypeName(this object obj) + public static Type GetNullableType(Type type) { - return obj?.GetType().Name ?? "null"; + return Nullable.GetUnderlyingType(type); } /// - /// 获取对象的完整类型名称 - /// [Obsolete("请直接使用 obj?.GetType().FullName")] + /// 获取可空类型或枚举类型的基础类型 /// - [Obsolete("请直接使用 obj?.GetType().FullName", false)] - public static string GetFullTypeName(this object obj) + public static Type GetUnderlyingType(Type type) { - return obj?.GetType().FullName ?? "null"; + if (IsNullable(type)) + { + return GetNullableType(type); + } + + if (type.IsEnum) + { + return Enum.GetUnderlyingType(type); + } + + return type; + } + + /// + /// 判断指定类型是否是简单类型 + /// + public static bool IsSimpleType(Type type) + { + if (type == typeof(string)) + { + return true; + } + + if (type.IsValueType) + { + return true; + } + + return false; + } + + /// + /// 判断指定类型是否是数字类型 + /// + public static bool IsNumericType(Type type) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.Int16: + case TypeCode.UInt32: + case TypeCode.Int32: + case TypeCode.UInt64: + case TypeCode.Int64: + case TypeCode.Decimal: + case TypeCode.Double: + case TypeCode.Single: + return true; + default: + return false; + } + } + + /// + /// 判断指定类型是否是布尔类型 + /// + public static bool IsBooleanType(Type type) + { + return type == typeof(bool); + } + + /// + /// 判断指定类型是否是日期时间类型 + /// + public static bool IsDateTimeType(Type type) + { + return type == typeof(DateTime); + } + + /// + /// 判断指定类型是否是集合类型 + /// + public static bool IsEnumerableType(Type type) + { + return typeof(IEnumerable).IsAssignableFrom(type); + } + + /// + /// 获取指定类型的所有派生类型 + /// + public static Type[] GetSubclassesOf(Type baseType) + { + return AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(s => s.GetTypes()) + .Where(p => baseType.IsAssignableFrom(p) && p != baseType) + .ToArray(); } #endregion #region 对象转字符串 + #endregion + + #region 对象转换(静态工具方法) + + /// + /// 将对象转换为指定类型(使用 TypeConverter 和 IConvertible) + /// + public static T ConvertTo(object obj) + { + return (T)ConvertTo(obj, typeof(T)); + } + + /// + /// 将对象转换为指定类型(使用 TypeConverter 和 IConvertible) + /// + public static object? ConvertTo(object obj, Type targetType) + { + if (obj == null) + { + return GetDefault(targetType); + } + + Type sourceType = obj.GetType(); + + if (targetType.IsAssignableFrom(sourceType)) + { + return obj; + } + + var converter = System.ComponentModel.TypeDescriptor.GetConverter(targetType); + if (converter != null && converter.CanConvertFrom(sourceType)) + { + return converter.ConvertFrom(obj); + } + + var sourceConverter = System.ComponentModel.TypeDescriptor.GetConverter(sourceType); + if (sourceConverter != null && sourceConverter.CanConvertTo(targetType)) + { + return sourceConverter.ConvertTo(obj, targetType); + } + + if (obj is IConvertible) + { + try + { + return System.Convert.ChangeType(obj, targetType); + } + catch (InvalidCastException) + { + } + } + + try + { + var implicitOp = sourceType.GetMethod("op_Implicit", new[] { sourceType }); + if (implicitOp != null && implicitOp.ReturnType == targetType) + { + return implicitOp.Invoke(null, new[] { obj }); + } + + var explicitOp = sourceType.GetMethod("op_Explicit", new[] { sourceType }); + if (explicitOp != null && explicitOp.ReturnType == targetType) + { + return explicitOp.Invoke(null, new[] { obj }); + } + + var targetImplicitOp = targetType.GetMethod("op_Implicit", new[] { sourceType }); + if (targetImplicitOp != null && targetImplicitOp.ReturnType == targetType) + { + return targetImplicitOp.Invoke(null, new[] { obj }); + } + + var targetExplicitOp = targetType.GetMethod("op_Explicit", new[] { sourceType }); + if (targetExplicitOp != null && targetExplicitOp.ReturnType == targetType) + { + return targetExplicitOp.Invoke(null, new[] { obj }); + } + } + catch (InvalidCastException) + { + } + + throw new InvalidCastException($"无法将类型为 {sourceType.Name} 的对象转换为类型为 {targetType.Name} 的对象"); + } + + #endregion + + #region 对象属性复制 + + /// + /// 将源对象的属性复制到目标对象中 + /// + public static void CopyProperties(object source, object target) + { + Type sourceType = source.GetType(); + Type targetType = target.GetType(); + + foreach (PropertyInfo sourceProperty in sourceType.GetProperties()) + { + if (!sourceProperty.CanRead) + { + continue; + } + + PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name); + + if (targetProperty == null || !targetProperty.CanWrite) + { + continue; + } + + object value = GetPropertyValue(source, sourceProperty.Name); + SetPropertyValue(target, targetProperty.Name, value); + } + } + + /// + /// 对象属性值的加密 + /// + public static void EncryptPropertyValue(object obj, string propertyName, Func encryptFunc) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + if (string.IsNullOrEmpty(propertyName)) + { + throw new ArgumentNullException(nameof(propertyName)); + } + + if (encryptFunc == null) + { + throw new ArgumentNullException(nameof(encryptFunc)); + } + + PropertyInfo property = obj.GetType().GetProperty(propertyName); + + if (property == null) + { + throw new ArgumentException($"对象类型 {obj.GetType().Name} 中没有名为 {propertyName} 的属性", nameof(propertyName)); + } + + object value = property.GetValue(obj); + + if (value != null && value is string) + { + string encryptedValue = encryptFunc((string)value); + property.SetValue(obj, encryptedValue); + } + } + + /// + /// 对象属性值的解密 + /// + public static void DecryptPropertyValue(object obj, string propertyName, Func decryptFunc) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + if (string.IsNullOrEmpty(propertyName)) + { + throw new ArgumentNullException(nameof(propertyName)); + } + + if (decryptFunc == null) + { + throw new ArgumentNullException(nameof(decryptFunc)); + } + + PropertyInfo property = obj.GetType().GetProperty(propertyName); + + if (property == null) + { + throw new ArgumentException($"对象类型 {obj.GetType().Name} 中没有名为 {propertyName} 的属性", nameof(propertyName)); + } + + object value = property.GetValue(obj); + + if (value != null && value is string) + { + string decryptedValue = decryptFunc((string)value); + property.SetValue(obj, decryptedValue); + } + } + + /// + /// 在对象属性上进行特定的处理 + /// + public static void ProcessPropertyValue(object obj, string propertyName, Action processAction) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + if (string.IsNullOrEmpty(propertyName)) + { + throw new ArgumentNullException(nameof(propertyName)); + } + + if (processAction == null) + { + throw new ArgumentNullException(nameof(processAction)); + } + + PropertyInfo property = obj.GetType().GetProperty(propertyName); + + if (property == null) + { + throw new ArgumentException($"对象类型 {obj.GetType().Name} 中没有名为 {propertyName} 的属性", nameof(propertyName)); + } + + object value = property.GetValue(obj); + + if (value != null) + { + processAction(value); + property.SetValue(obj, value); + } + } + + #endregion + + #region 对象比较 + + /// + /// 比较两个对象的差异(属性值或字段值不同) + /// + public static IEnumerable CompareDifferences(object obj1, object obj2) + { + if (obj1 == null && obj2 == null) + { + return Enumerable.Empty(); + } + + if (obj1 == null || obj2 == null) + { + throw new ArgumentNullException("比较对象不能为 null"); + } + + List differences = new List(); + + foreach (PropertyInfo property in obj1.GetType().GetProperties()) + { + object value1 = property.GetValue(obj1); + object value2 = property.GetValue(obj2); + + if (!Equals(value1, value2)) + { + differences.Add($"属性 {property.Name} 的值不同:{value1} -> {value2}"); + } + } + + foreach (FieldInfo field in obj1.GetType().GetFields()) + { + object value1 = field.GetValue(obj1); + object value2 = field.GetValue(obj2); + + if (!Equals(value1, value2)) + { + differences.Add($"字段 {field.Name} 的值不同:{value1} -> {value2}"); + } + } + + return differences; + } + + #endregion + + #region 对象高级转换 + + /// + /// 将对象转换为键值对集合 + /// + public static IEnumerable> ToKeyValuePairs(this object obj) + { + if (obj == null) + { + return Enumerable.Empty>(); + } + + List> pairs = new List>(); + + foreach (PropertyInfo property in obj.GetType().GetProperties()) + { + pairs.Add(new KeyValuePair(property.Name, property.GetValue(obj))); + } + + foreach (FieldInfo field in obj.GetType().GetFields()) + { + pairs.Add(new KeyValuePair(field.Name, field.GetValue(obj))); + } + + return pairs; + } + + /// + /// 将对象转换为动态扩展对象 + /// + public static dynamic? ToDynamic(this object obj) + { + if (obj == null) + { + return null; + } + + IDictionary dictionary = new System.Dynamic.ExpandoObject(); + + foreach (PropertyInfo propertyInfo in obj.GetType().GetProperties()) + { + if (!propertyInfo.CanRead) + { + continue; + } + + object value = GetPropertyValue(obj, propertyInfo.Name); + dictionary.Add(propertyInfo.Name, value); + } + + foreach (FieldInfo fieldInfo in obj.GetType().GetFields()) + { + object value = GetFieldValue(obj, fieldInfo.Name); + dictionary.Add(fieldInfo.Name, value); + } + + return dictionary; + } + /// - /// 将对象转换为字符串(处理 null) - /// [Obsolete("请直接使用 obj?.ToString() ?? string.Empty")] + /// 获取对象的字段值 /// - [Obsolete("请直接使用 obj?.ToString() ?? string.Empty", false)] - public static string ToStringSafe(this object obj) + public static object? GetFieldValue(this object obj, string fieldName) { - return obj?.ToString() ?? string.Empty; + return obj.GetType().GetField(fieldName)?.GetValue(obj); } /// - /// 将对象转换为字符串(null 时返回默认值) - /// [Obsolete("请直接使用 obj?.ToString() ?? defaultValue")] + /// 设置对象的字段值 /// - [Obsolete("请直接使用 obj?.ToString() ?? defaultValue", false)] - public static string ToStringOrDefault(this object obj, string defaultValue = "") + public static void SetFieldValue(this object obj, string fieldName, object? value) { - return obj?.ToString() ?? defaultValue; + obj.GetType().GetField(fieldName)?.SetValue(obj, value); } #endregion diff --git a/EasyTool.Core/ToolCategory/ObjectUtil.cs b/EasyTool.Core/ToolCategory/ObjectUtil.cs deleted file mode 100644 index 81c5921..0000000 --- a/EasyTool.Core/ToolCategory/ObjectUtil.cs +++ /dev/null @@ -1,1193 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Dynamic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; -using System.Runtime.Serialization.Json; -using System.Text; -using System.Xml.Serialization; - -namespace EasyTool -{ - /// - /// 对象工具类 - /// - public class ObjectUtil - { - - /// - /// 检查对象是否为 null - /// [Obsolete("请直接使用 obj == null")] - /// - [Obsolete("请直接使用 obj == null", false)] - public static bool IsNull(object? obj) - { - return obj == null; - } - - /// - /// 检查对象是否不为 null - /// [Obsolete("请直接使用 obj != null")] - /// - [Obsolete("请直接使用 obj != null", false)] - public static bool IsNotNull(object? obj) - { - return obj != null; - } - - /// - /// 检查对象是否为空(null 或者 空字符串或空白字符) - /// - public static bool IsNullOrEmpty(object? obj) - { - if (IsNull(obj)) - { - return true; - } - - if (obj is string str) - { - return string.IsNullOrWhiteSpace(str); - } - - if (obj is ICollection collection) - { - return collection.Count == 0; - } - - return false; - } - - /// - /// 检查对象是否不为空(非 null 且 非空字符串 或者 非空集合) - /// - public static bool IsNotNullOrEmpty(object? obj) - { - return !IsNullOrEmpty(obj); - } - - /// - /// 检查两个对象是否相等 - /// - public static new bool Equals(object obj1, object obj2) - { - if (IsNull(obj1) && IsNull(obj2)) - { - return true; - } - - if (IsNull(obj1) || IsNull(obj2)) - { - return false; - } - - return obj1.Equals(obj2); - } - - /// - /// 获取对象的类型名称 - /// [Obsolete("请直接使用 obj.GetType().Name")] - /// - [Obsolete("请直接使用 obj.GetType().Name", false)] - public static string GetTypeName(object obj) - { - return obj.GetType().Name; - } - - /// - /// 将对象转换为指定类型 - /// - public static T Convert(object obj) - { - return (T)Convert(obj, typeof(T)); - } - - /// - /// 将对象转换为指定类型 - /// - public static object? Convert(object obj, Type targetType) - { - if (IsNull(obj)) - { - // 处理可空值类型的默认值 - return GetDefault(targetType); - } - - Type sourceType = obj.GetType(); - - // 如果目标类型可以从源类型赋值,直接返回 - if (targetType.IsAssignableFrom(sourceType)) - { - return obj; - } - - // 使用 TypeConverter 进行转换 - var converter = System.ComponentModel.TypeDescriptor.GetConverter(targetType); - if (converter != null && converter.CanConvertFrom(sourceType)) - { - return converter.ConvertFrom(obj); - } - - // 尝试从源类型的 TypeConverter 转换 - var sourceConverter = System.ComponentModel.TypeDescriptor.GetConverter(sourceType); - if (sourceConverter != null && sourceConverter.CanConvertTo(targetType)) - { - return sourceConverter.ConvertTo(obj, targetType); - } - - // 使用 IConvertible 接口转换 - if (obj is IConvertible) - { - try - { - return System.Convert.ChangeType(obj, targetType); - } - catch (InvalidCastException) - { - // 继续尝试其他转换方式 - } - } - - // 尝试使用隐式或显式转换操作符 - try - { - // 查找源类型的隐式转换操作符 - var implicitOp = sourceType.GetMethod("op_Implicit", new[] { sourceType }); - if (implicitOp != null && implicitOp.ReturnType == targetType) - { - return implicitOp.Invoke(null, new[] { obj }); - } - - // 查找源类型的显式转换操作符 - var explicitOp = sourceType.GetMethod("op_Explicit", new[] { sourceType }); - if (explicitOp != null && explicitOp.ReturnType == targetType) - { - return explicitOp.Invoke(null, new[] { obj }); - } - - // 查找目标类型的隐式转换操作符 - var targetImplicitOp = targetType.GetMethod("op_Implicit", new[] { sourceType }); - if (targetImplicitOp != null && targetImplicitOp.ReturnType == targetType) - { - return targetImplicitOp.Invoke(null, new[] { obj }); - } - - // 查找目标类型的显式转换操作符 - var targetExplicitOp = targetType.GetMethod("op_Explicit", new[] { sourceType }); - if (targetExplicitOp != null && targetExplicitOp.ReturnType == targetType) - { - return targetExplicitOp.Invoke(null, new[] { obj }); - } - } - catch (InvalidCastException) - { - // 转换操作符失败,继续抛出异常 - } - - throw new InvalidCastException($"无法将类型为 {sourceType.Name} 的对象转换为类型为 {targetType.Name} 的对象"); - } - - /// - /// 获取对象的属性列表 - /// [Obsolete("请直接使用 obj.GetType().GetProperties()")] - /// - [Obsolete("请直接使用 obj.GetType().GetProperties()", false)] - public static IEnumerable GetProperties(object obj) - { - return obj.GetType().GetProperties(); - } - - /// - /// 获取对象的属性值 - /// - public static object? GetPropertyValue(object obj, string propertyName) - { - return obj.GetType().GetProperty(propertyName)?.GetValue(obj); - } - - /// - /// 设置对象的属性值 - /// - public static void SetPropertyValue(object obj, string propertyName, object? value) - { - obj.GetType().GetProperty(propertyName)?.SetValue(obj, value); - } - - /// - /// 获取对象的字段列表 - /// [Obsolete("请直接使用 obj.GetType().GetFields()")] - /// - [Obsolete("请直接使用 obj.GetType().GetFields()", false)] - public static IEnumerable GetFields(object obj) - { - return obj.GetType().GetFields(); - } - - /// - /// 获取对象的字段值 - /// - public static object? GetFieldValue(object obj, string fieldName) - { - return obj.GetType().GetField(fieldName)?.GetValue(obj); - } - - /// - /// 设置对象的字段值 - /// - public static void SetFieldValue(object obj, string fieldName, object? value) - { - obj.GetType().GetField(fieldName)?.SetValue(obj, value); - } - - /// - /// 获取对象的方法列表 - /// [Obsolete("请直接使用 obj.GetType().GetMethods()")] - /// - [Obsolete("请直接使用 obj.GetType().GetMethods()", false)] - public static IEnumerable GetMethods(object obj) - { - return obj.GetType().GetMethods(); - } - - /// - /// 判断对象是否实现了指定接口 - /// [Obsolete("请直接使用 interfaceType.IsAssignableFrom(obj.GetType())")] - /// - [Obsolete("请直接使用 interfaceType.IsAssignableFrom(obj.GetType())", false)] - public static bool ImplementsInterface(object obj, Type interfaceType) - { - return interfaceType.IsAssignableFrom(obj.GetType()); - } - - /// - /// 判断对象是否为指定类型的实例 - /// [Obsolete("请直接使用 targetType.IsInstanceOfType(obj)")] - /// - [Obsolete("请直接使用 targetType.IsInstanceOfType(obj)", false)] - public static bool IsInstanceOfType(object obj, Type targetType) - { - return targetType.IsInstanceOfType(obj); - } - - /// - /// 对象属性或字段值的加密 - /// - public static void EncryptPropertyValue(object obj, string propertyName, Func encryptFunc) - { - if (IsNull(obj)) - { - throw new ArgumentNullException(nameof(obj)); - } - - if (string.IsNullOrEmpty(propertyName)) - { - throw new ArgumentNullException(nameof(propertyName)); - } - - if (encryptFunc == null) - { - throw new ArgumentNullException(nameof(encryptFunc)); - } - - PropertyInfo property = obj.GetType().GetProperty(propertyName); - - if (property == null) - { - throw new ArgumentException($"对象类型 {obj.GetType().Name} 中没有名为 {propertyName} 的属性", nameof(propertyName)); - } - - object value = property.GetValue(obj); - - if (value != null && value is string) - { - string encryptedValue = encryptFunc((string)value); - property.SetValue(obj, encryptedValue); - } - } - - /// - /// 对象属性或字段值的解密 - /// - public static void DecryptPropertyValue(object obj, string propertyName, Func decryptFunc) - { - if (IsNull(obj)) - { - throw new ArgumentNullException(nameof(obj)); - } - - if (string.IsNullOrEmpty(propertyName)) - { - throw new ArgumentNullException(nameof(propertyName)); - } - - if (decryptFunc == null) - { - throw new ArgumentNullException(nameof(decryptFunc)); - } - - PropertyInfo property = obj.GetType().GetProperty(propertyName); - - if (property == null) - { - throw new ArgumentException($"对象类型 {obj.GetType().Name} 中没有名为 {propertyName} 的属性", nameof(propertyName)); - } - - object value = property.GetValue(obj); - - if (value != null && value is string) - { - string decryptedValue = decryptFunc((string)value); - property.SetValue(obj, decryptedValue); - } - } - - /// - /// 在对象属性或字段上进行特定的处理 - /// - public static void ProcessPropertyValue(object obj, string propertyName, Action processAction) - { - if (IsNull(obj)) - { - throw new ArgumentNullException(nameof(obj)); - } - - if (string.IsNullOrEmpty(propertyName)) - { - throw new ArgumentNullException(nameof(propertyName)); - } - - if (processAction == null) - { - throw new ArgumentNullException(nameof(processAction)); - } - PropertyInfo property = obj.GetType().GetProperty(propertyName); - - if (property == null) - { - throw new ArgumentException($"对象类型 {obj.GetType().Name} 中没有名为 {propertyName} 的属性", nameof(propertyName)); - } - - object value = property.GetValue(obj); - - if (value != null) - { - processAction(value); - property.SetValue(obj, value); - } - } - - /// - /// 将对象序列化为 JSON 字符串 - /// - public static string? ToJson(object obj) - { - if (IsNull(obj)) - { - return null; - } - - using (MemoryStream stream = new MemoryStream()) - { - DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType()); - serializer.WriteObject(stream, obj); - return Encoding.UTF8.GetString(stream.ToArray()); - } - } - - /// - /// 将 JSON 字符串反序列化为对象 - /// - public static T FromJson(string json) - { - if (string.IsNullOrEmpty(json)) - { - return default(T); - } - - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) - { - DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); - return (T)serializer.ReadObject(stream); - } - } - - /// - /// 将对象序列化为 XML 字符串 - /// - public static string? ToXml(object obj) - { - if (IsNull(obj)) - { - return null; - } - - XmlSerializer serializer = new XmlSerializer(obj.GetType()); - using (MemoryStream stream = new MemoryStream()) - { - serializer.Serialize(stream, obj); - return Encoding.UTF8.GetString(stream.ToArray()); - } - } - - /// - /// 将 XML 字符串反序列化为对象 - /// - public static T FromXml(string xml) - { - if (string.IsNullOrEmpty(xml)) - { - return default(T); - } - - XmlSerializer serializer = new XmlSerializer(typeof(T)); - using (StringReader reader = new StringReader(xml)) - { - return (T)serializer.Deserialize(reader); - } - } - - /// - /// 将对象转换为字典 - /// - public static Dictionary? ToDictionary(object obj) - { - if (IsNull(obj)) - { - return null; - } - - Dictionary dictionary = new Dictionary(); - - foreach (PropertyInfo property in GetProperties(obj)) - { - dictionary[property.Name] = property.GetValue(obj); - } - - foreach (FieldInfo field in GetFields(obj)) - { - dictionary[field.Name] = field.GetValue(obj); - } - - return dictionary; - } - - /// - /// 将字典转换为对象 - /// - public static T FromDictionary(Dictionary dictionary) where T : new() - { - if (dictionary == null) - { - return default(T); - } - - T obj = new T(); - - foreach (PropertyInfo property in GetProperties(obj)) - { - if (dictionary.TryGetValue(property.Name, out object value)) - { - property.SetValue(obj, Convert(value, property.PropertyType)); - } - } - - foreach (FieldInfo field in GetFields(obj)) - { - if (dictionary.TryGetValue(field.Name, out object value)) - { - field.SetValue(obj, Convert(value, field.FieldType)); - } - } - - return obj; - } - - /// - /// 比较两个对象的差异(属性值或字段值不同) - /// - public static IEnumerable Compare(object obj1, object obj2) - { - if (IsNull(obj1) && IsNull(obj2)) - { - return Enumerable.Empty(); - } - - if (IsNull(obj1) || IsNull(obj2)) - { - throw new ArgumentNullException("比较对象不能为 null"); - } - - List differences = new List(); - - foreach (PropertyInfo property in GetProperties(obj1)) - { - object value1 = property.GetValue(obj1); - object value2 = property.GetValue(obj2); - - if (!Equals(value1, value2)) - { - differences.Add($"属性 {property.Name} 的值不同:{value1} -> {value2}"); - } - } - - foreach (FieldInfo field in GetFields(obj1)) - { - object value1 = field.GetValue(obj1); - object value2 = field.GetValue(obj2); - - if (!Equals(value1, value2)) - { - differences.Add($"字段 {field.Name} 的值不同:{value1} -> {value2}"); - } - } - - return differences; - } - - /// - /// 获取对象的哈希码 - /// [Obsolete("请直接使用 obj?.GetHashCode() ?? 0")] - /// - [Obsolete("请直接使用 obj?.GetHashCode() ?? 0", false)] - public static int GetHashCode(object obj) - { - if (IsNull(obj)) - { - return 0; - } - - return obj.GetHashCode(); - } - - /// - /// 深拷贝对象 - /// - public static T? DeepClone(T obj) - { - if (IsNull(obj)) - { - return default(T); - } - - DataContractSerializer serializer = new DataContractSerializer(obj.GetType()); - - using (MemoryStream stream = new MemoryStream()) - { - serializer.WriteObject(stream, obj); - stream.Position = 0; - return (T)serializer.ReadObject(stream); - } - } - - /// - /// 判断对象是否为值类型 - /// [Obsolete("请直接使用 obj?.GetType().IsValueType ?? false")] - /// - [Obsolete("请直接使用 obj?.GetType().IsValueType ?? false", false)] - public static bool IsValueType(object obj) - { - if (IsNull(obj)) - { - return false; - } - - return obj.GetType().IsValueType; - } - - /// - /// 将对象转换为键值对集合 - /// - public static IEnumerable> ToKeyValuePairs(object obj) - { - if (IsNull(obj)) - { - return Enumerable.Empty>(); - } - - List> pairs = new List>(); - - foreach (PropertyInfo property in GetProperties(obj)) - { - pairs.Add(new KeyValuePair(property.Name, property.GetValue(obj))); - } - - foreach (FieldInfo field in GetFields(obj)) - { - pairs.Add(new KeyValuePair(field.Name, field.GetValue(obj))); - } - - return pairs; - } - - /// - /// 深度复制对象 - /// - public static object? DeepCopy(object obj) - { - if (obj == null) - { - return null; - } - - Type type = obj.GetType(); - - if (IsSimpleType(type)) - { - return obj; - } - - if (IsEnumerableType(type)) - { - Type elementType = type.GetElementType() ?? type.GetGenericArguments().FirstOrDefault(); - - if (elementType == null || IsSimpleType(elementType)) - { - return obj; - } - - IList list = (IList)Activator.CreateInstance(type); - - foreach (object item in (IEnumerable)obj) - { - list.Add(DeepCopy(item)); - } - - return list; - } - - object clone = Activator.CreateInstance(type); - - foreach (PropertyInfo propertyInfo in GetProperties(type)) - { - if (!propertyInfo.CanRead || !propertyInfo.CanWrite) - { - continue; - } - - object value = GetPropertyValue(obj, propertyInfo.Name); - - if (value == null) - { - continue; - } - - if (IsSimpleType(propertyInfo.PropertyType)) - { - SetPropertyValue(clone, propertyInfo.Name, value); - } - else if (IsEnumerableType(propertyInfo.PropertyType)) - { - object enumerable = DeepCopy(value); - SetPropertyValue(clone, propertyInfo.Name, enumerable); - } - else - { - object childClone = DeepCopy(value); - SetPropertyValue(clone, propertyInfo.Name, childClone); - } - } - - foreach (FieldInfo fieldInfo in GetFields(type)) - { - object value = GetFieldValue(obj, fieldInfo.Name); - - if (value == null) - { - continue; - } - - if (IsSimpleType(fieldInfo.FieldType)) - { - SetFieldValue(clone, fieldInfo.Name, value); - } - else if (IsEnumerableType(fieldInfo.FieldType)) - { - object enumerable = DeepCopy(value); - SetFieldValue(clone, fieldInfo.Name, enumerable); - } - else - { - object childClone = DeepCopy(value); - SetFieldValue(clone, fieldInfo.Name, childClone); - } - } - - return clone; - } - - /// - /// 将源对象的属性复制到目标对象中 - /// - public static void CopyProperties(object source, object target) - { - Type sourceType = source.GetType(); - Type targetType = target.GetType(); - - foreach (PropertyInfo sourceProperty in GetProperties(sourceType)) - { - if (!sourceProperty.CanRead) - { - continue; - } - - PropertyInfo targetProperty = GetProperty(targetType, sourceProperty.Name); - - if (targetProperty == null || !targetProperty.CanWrite) - { - continue; - } - - object value = GetPropertyValue(source, sourceProperty.Name); - SetPropertyValue(target, targetProperty.Name, value); - } - } - - /// - /// 获取指定类型的 Type 对象 - /// [Obsolete("请直接使用 Type.GetType(typeName)")] - /// - [Obsolete("请直接使用 Type.GetType(typeName)", false)] - public static Type GetType(string typeName) - { - return Type.GetType(typeName); - } - - /// - /// 获取对象的 Type 对象 - /// [Obsolete("请直接使用 obj.GetType()")] - /// - [Obsolete("请直接使用 obj.GetType()", false)] - public static Type GetType(object obj) - { - return obj.GetType(); - } - - /// - /// 获取类型的所有成员信息,包括字段、属性、方法和事件等 - /// [Obsolete("请直接使用 type.GetMembers()")] - /// - [Obsolete("请直接使用 type.GetMembers()", false)] - public static MemberInfo[] GetMembers(Type type) - { - return type.GetMembers(); - } - - /// - /// 获取类型的所有属性信息 - /// [Obsolete("请直接使用 type.GetProperties()")] - /// - [Obsolete("请直接使用 type.GetProperties()", false)] - public static PropertyInfo[] GetProperties(Type type) - { - return type.GetProperties(); - } - - /// - /// 获取类型的所有字段信息 - /// [Obsolete("请直接使用 type.GetFields()")] - /// - [Obsolete("请直接使用 type.GetFields()", false)] - public static FieldInfo[] GetFields(Type type) - { - return type.GetFields(); - } - - /// - /// 获取指定名称的属性信息 - /// [Obsolete("请直接使用 type.GetProperty(propertyName)")] - /// - [Obsolete("请直接使用 type.GetProperty(propertyName)", false)] - public static PropertyInfo GetProperty(Type type, string propertyName) - { - return type.GetProperty(propertyName); - } - - /// - /// 获取指定名称的属性信息 - /// [Obsolete("请直接使用 obj.GetType().GetProperty(propertyName)")] - /// - [Obsolete("请直接使用 obj.GetType().GetProperty(propertyName)", false)] - public static PropertyInfo GetProperty(object obj, string propertyName) - { - return obj.GetType().GetProperty(propertyName); - } - - /// - /// 获取指定名称的字段信息 - /// [Obsolete("请直接使用 type.GetField(fieldName)")] - /// - [Obsolete("请直接使用 type.GetField(fieldName)", false)] - public static FieldInfo GetField(Type type, string fieldName) - { - return type.GetField(fieldName); - } - - /// - /// 获取指定名称的字段信息 - /// [Obsolete("请直接使用 obj.GetType().GetField(fieldName)")] - /// - [Obsolete("请直接使用 obj.GetType().GetField(fieldName)", false)] - public static FieldInfo GetField(object obj, string fieldName) - { - return obj.GetType().GetField(fieldName); - } - - /// - /// 获取指定名称的方法信息 - /// [Obsolete("请直接使用 type.GetMethod(methodName)")] - /// - [Obsolete("请直接使用 type.GetMethod(methodName)", false)] - public static MethodInfo GetMethod(Type type, string methodName) - { - return type.GetMethod(methodName); - } - - /// - /// 获取指定名称的方法信息 - /// [Obsolete("请直接使用 obj.GetType().GetMethod(methodName)")] - /// - [Obsolete("请直接使用 obj.GetType().GetMethod(methodName)", false)] - public static MethodInfo GetMethod(object obj, string methodName) - { - return obj.GetType().GetMethod(methodName); - } - - /// - /// 获取指定名称和参数类型的方法信息 - /// [Obsolete("请直接使用 type.GetMethod(methodName, parameterTypes)")] - /// - [Obsolete("请直接使用 type.GetMethod(methodName, parameterTypes)", false)] - public static MethodInfo GetMethod(Type type, string methodName, Type[] parameterTypes) - { - return type.GetMethod(methodName, parameterTypes); - } - - /// - /// 获取指定名称和参数类型的方法信息 - /// [Obsolete("请直接使用 obj.GetType().GetMethod(methodName, parameterTypes)")] - /// - [Obsolete("请直接使用 obj.GetType().GetMethod(methodName, parameterTypes)", false)] - public static MethodInfo GetMethod(object obj, string methodName, Type[] parameterTypes) - { - return obj.GetType().GetMethod(methodName, parameterTypes); - } - - /// - /// 调用对象的指定方法 - /// - public static object InvokeMethod(object obj, string methodName, object[] parameters) - { - Type type = obj.GetType(); - MethodInfo methodInfo = GetMethod(type, methodName); - return methodInfo.Invoke(obj, parameters); - } - - /// - /// 调用对象的指定方法 - /// - public static object InvokeMethod(object obj, string methodName, Type[] parameterTypes, object[] parameters) - { - Type type = obj.GetType(); - MethodInfo methodInfo = GetMethod(type, methodName, parameterTypes); - return methodInfo.Invoke(obj, parameters); - } - - /// - /// 创建指定类型的实例 - /// [Obsolete("请直接使用 Activator.CreateInstance(type, constructorParameters)")] - /// - [Obsolete("请直接使用 Activator.CreateInstance(type, constructorParameters)", false)] - public static object CreateInstance(Type type, object[] constructorParameters) - { - return Activator.CreateInstance(type, constructorParameters); - } - - - /// - /// 判断指定类型是否派生自指定的基类或接口 - /// [Obsolete("请直接使用 type.IsSubclassOf(baseType)")] - /// - [Obsolete("请直接使用 type.IsSubclassOf(baseType)", false)] - public static bool IsSubclassOf(Type type, Type baseType) - { - return type.IsSubclassOf(baseType); - } - - /// - /// 获取指定类型的所有派生类型 - /// - public static Type[] GetSubclassesOf(Type baseType) - { - return AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(s => s.GetTypes()) - .Where(p => baseType.IsAssignableFrom(p) && p != baseType) - .ToArray(); - } - - /// - /// 获取指定类型实现的所有接口类型 - /// [Obsolete("请直接使用 type.GetInterfaces()")] - /// - [Obsolete("请直接使用 type.GetInterfaces()", false)] - public static Type[] GetInterfaces(Type type) - { - return type.GetInterfaces(); - } - - /// - /// 获取指定类型的程序集限定名 - /// [Obsolete("请直接使用 type.AssemblyQualifiedName")] - /// - [Obsolete("请直接使用 type.AssemblyQualifiedName", false)] - public static string GetAssemblyQualifiedName(Type type) - { - return type.AssemblyQualifiedName; - } - - /// - /// 获取指定类型的默认值 - /// - public static object? GetDefault(Type type) - { - return type.IsValueType ? Activator.CreateInstance(type) : null; - } - - /// - /// 判断对象是否是其类型的默认值 - /// - public static bool IsDefaultValue(object obj) - { - return obj == null || obj.Equals(GetDefault(obj.GetType())); - } - - /// - /// 判断指定类型是否是可空类型 - /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) != null")] - /// - [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) != null", false)] - public static bool IsNullable(Type type) - { - return Nullable.GetUnderlyingType(type) != null; - } - - /// - /// 获取可空类型的基础类型 - /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type")] - /// - [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type", false)] - public static Type GetNullableType(Type type) - { - return Nullable.GetUnderlyingType(type) ?? type; - } - - /// - /// 获取可空类型或枚举类型的基础类型 - /// - public static Type GetUnderlyingType(Type type) - { - if (IsNullable(type)) - { - return GetNullableType(type); - } - - if (IsEnumType(type)) - { - return Enum.GetUnderlyingType(type); - } - - return type; - } - - /// - /// 判断指定类型是否是简单类型 - /// - public static bool IsSimpleType(Type type) - { - if (type == typeof(string)) - { - return true; - } - - if (type.IsValueType) - { - return true; - } - - return false; - } - - /// - /// 判断指定类型是否是数字类型 - /// - public static bool IsNumericType(Type type) - { - switch (Type.GetTypeCode(type)) - { - case TypeCode.Byte: - case TypeCode.SByte: - case TypeCode.UInt16: - case TypeCode.Int16: - case TypeCode.UInt32: - case TypeCode.Int32: - case TypeCode.UInt64: - case TypeCode.Int64: - case TypeCode.Decimal: - case TypeCode.Double: - case TypeCode.Single: - return true; - default: - return false; - } - } - - /// - /// 判断指定类型是否是布尔类型 - /// - public static bool IsBooleanType(Type type) - { - return type == typeof(bool); - } - - /// - /// 判断指定类型是否是日期时间类型 - /// - public static bool IsDateTimeType(Type type) - { - return type == typeof(DateTime); - } - - /// - /// 判断指定类型是否是枚举类型 - /// [Obsolete("请直接使用 type.IsEnum")] - /// - [Obsolete("请直接使用 type.IsEnum", false)] - public static bool IsEnumType(Type type) - { - return type.IsEnum; - } - - /// - /// 判断指定类型是否是集合类型 - /// - public static bool IsEnumerableType(Type type) - { - return typeof(IEnumerable).IsAssignableFrom(type); - } - - /// - /// 将对象转换为动态扩展对象 - /// - public static dynamic? ToDynamic(object obj) - { - if (obj == null) - { - return null; - } - - IDictionary dictionary = new ExpandoObject(); - - foreach (PropertyInfo propertyInfo in GetProperties(obj.GetType())) - { - if (!propertyInfo.CanRead) - { - continue; - } - - object value = GetPropertyValue(obj, propertyInfo.Name); - dictionary.Add(propertyInfo.Name, value); - } - - foreach (FieldInfo fieldInfo in GetFields(obj.GetType())) - { - object value = GetFieldValue(obj, fieldInfo.Name); - dictionary.Add(fieldInfo.Name, value); - } - - return dictionary; - } - -#if NET6_0_OR_GREATER - - /// - /// 将对象序列化为 JSON 字符串 - /// - public static string SerializeToJson(object obj) - { - return System.Text.Json.JsonSerializer.Serialize(obj); - } - - /// - /// 将 JSON 字符串反序列化为指定类型的对象 - /// - public static T? DeserializeFromJson(string json) - { - return System.Text.Json.JsonSerializer.Deserialize(json); - } - -#endif - - /// - /// 将对象序列化为 XML 字符串 - /// - public static string SerializeToXml(object obj) - { - XmlSerializer serializer = new XmlSerializer(obj.GetType()); - - using (StringWriter writer = new StringWriter()) - { - serializer.Serialize(writer, obj); - return writer.ToString(); - } - } - - /// - /// 将 XML 字符串反序列化为指定类型的对象 - /// - public static object? DeserializeFromXml(string xml, Type type) - { - XmlSerializer serializer = new XmlSerializer(type); - - using (StringReader reader = new StringReader(xml)) - { - return serializer.Deserialize(reader); - } - } - - /// - /// 将对象序列化为二进制数据 - /// - [Obsolete("BinaryFormatter is obsolete and unsafe. Use SerializeToJson or SerializeToXml instead.")] - public static byte[] SerializeToBinary(object obj) - { -#pragma warning disable SYSLIB0011 // 类型或成员已过时 - BinaryFormatter formatter = new BinaryFormatter(); - - using (MemoryStream stream = new MemoryStream()) - { - formatter.Serialize(stream, obj); - return stream.ToArray(); - } -#pragma warning restore SYSLIB0011 // 类型或成员已过时 - } - - /// - /// 将二进制数据反序列化为指定类型的对象 - /// - [Obsolete("BinaryFormatter is obsolete and unsafe. Use DeserializeFromJson or DeserializeFromXml instead.")] - public static object DeserializeFromBinary(byte[] data, Type type) - { -#pragma warning disable SYSLIB0011 // 类型或成员已过时 - BinaryFormatter formatter = new BinaryFormatter(); - - using (MemoryStream stream = new MemoryStream(data)) - { - return formatter.Deserialize(stream); - } -#pragma warning restore SYSLIB0011 // 类型或成员已过时 - } - } -} diff --git a/EasyTool.Core/ToolCategory/PageUtil.cs b/EasyTool.Core/ToolCategory/PageUtil.cs index e44b300..d1334eb 100644 --- a/EasyTool.Core/ToolCategory/PageUtil.cs +++ b/EasyTool.Core/ToolCategory/PageUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -193,41 +193,56 @@ public void SetOrderField(Func orderField, bool isAsc) /// 分页HTML代码 public string GetPaginationHtml(string urlFormat, string currentPageClass = "current", int range = 5) { - string html = ""; + var sb = new StringBuilder(); if (totalPage <= 1) { - return html; + return sb.ToString(); } + int startPage = Math.Max(1, currentPage - range); int endPage = Math.Min(totalPage, currentPage + range); if (startPage > 1) { - html += "1"; + sb.Append("1"); if (startPage > 2) { - html += "..."; + sb.Append("..."); } } for (int i = startPage; i <= endPage; i++) { if (i == currentPage) { - html += "" + i.ToString() + ""; + sb.Append(""); + sb.Append(i.ToString()); + sb.Append(""); } else { - html += "" + i.ToString() + ""; + sb.Append(""); + sb.Append(i.ToString()); + sb.Append(""); } } if (endPage < totalPage) { if (endPage < totalPage - 1) { - html += "..."; + sb.Append("..."); } - html += "" + totalPage.ToString() + ""; + sb.Append(""); + sb.Append(totalPage.ToString()); + sb.Append(""); } - return html; + return sb.ToString(); } } } diff --git a/EasyTool.Core/ToolCategory/ProcessUtil.cs b/EasyTool.Core/ToolCategory/ProcessUtil.cs deleted file mode 100644 index 63a9d8b..0000000 --- a/EasyTool.Core/ToolCategory/ProcessUtil.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Text; - -namespace EasyTool -{ - /// - /// 进程工具类 - /// - public class ProcessUtil - { - /// - /// 通过进程名称获取进程 - /// - /// 进程名称 - /// 进程 - public static Process GetProcessByName(string processName) - { - // 获取当前运行的所有进程 - var processes = Process.GetProcessesByName(processName); - if (processes.Length > 0) - { - return processes[0]; - } - return null; - } - - /// - /// 获取进程的所有线程 - /// [Obsolete("请直接使用 process.Threads")] - /// - /// 进程 - /// 线程集合 - [Obsolete("请直接使用 process.Threads", false)] - public static ProcessThreadCollection GetProcessThreads(Process process) - { - return process.Threads; - } - - /// - /// 获取进程的主窗口句柄 - /// [Obsolete("请直接使用 process.MainWindowHandle")] - /// - /// 进程 - /// 窗口句柄 - [Obsolete("请直接使用 process.MainWindowHandle", false)] - public static IntPtr GetMainWindowHandle(Process process) - { - return process.MainWindowHandle; - } - - /// - /// 获取进程的主窗口标题 - /// [Obsolete("请直接使用 process.MainWindowTitle")] - /// - /// 进程 - /// 窗口标题 - [Obsolete("请直接使用 process.MainWindowTitle", false)] - public static string GetMainWindowTitle(Process process) - { - return process.MainWindowTitle; - } - - /// - /// 获取进程的所有模块 - /// [Obsolete("请直接使用 process.Modules")] - /// - /// 进程 - /// 模块集合 - [Obsolete("请直接使用 process.Modules", false)] - public static ProcessModuleCollection GetProcessModules(Process process) - { - return process.Modules; - } - - /// - /// 关闭进程 - /// [Obsolete("请直接使用 process.Kill()")] - /// - /// 进程 - [Obsolete("请直接使用 process.Kill()", false)] - public static void KillProcess(Process process) - { - process.Kill(); - } - - /// - /// 关闭进程并等待结束 - /// - /// 进程 - public static void KillProcessAndWait(Process process) - { - process.Kill(); - process.WaitForExit(); - } - - /// - /// 启动新进程 - /// [Obsolete("请直接使用 Process.Start(fileName)")] - /// - /// 文件名 - /// 新进程 - [Obsolete("请直接使用 Process.Start(fileName)", false)] - public static Process StartProcess(string fileName) - { - return Process.Start(fileName); - } - - /// - /// 启动新进程并等待结束 - /// - /// 文件名 - public static void StartProcessAndWait(string fileName) - { - var process = Process.Start(fileName); - process.WaitForExit(); - } - - /// - /// 判断进程是否存在 - /// - /// 进程名称 - /// 是否存在 - public static bool IsProcessExists(string processName) - { - return Process.GetProcessesByName(processName).Length > 0; - } - - /// - /// 获取进程使用的内存大小 - /// [Obsolete("请直接使用 process.WorkingSet64")] - /// - /// 进程 - /// 内存大小(字节) - [Obsolete("请直接使用 process.WorkingSet64", false)] - public static long GetProcessMemorySize(Process process) - { - return process.WorkingSet64; - } - - /// - /// 暂停进程 - /// - /// 进程 - public static void SuspendProcess(Process process) - { - foreach (ProcessThread thread in process.Threads) - { - IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); - if (pOpenThread == IntPtr.Zero) - { - break; - } - SuspendThread(pOpenThread); - CloseHandle(pOpenThread); - } - } - - /// - /// 恢复进程 - /// - /// 进程 - public static void ResumeProcess(Process process) - { - foreach (ProcessThread thread in process.Threads) - { - IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); - if (pOpenThread == IntPtr.Zero) - { - break; - } - ResumeThread(pOpenThread); - CloseHandle(pOpenThread); - } - } - - [DllImport("kernel32.dll")] - static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); - [DllImport("kernel32.dll")] - static extern uint SuspendThread(IntPtr hThread); - [DllImport("kernel32.dll")] - static extern int ResumeThread(IntPtr hThread); - [DllImport("kernel32.dll")] - static extern IntPtr CloseHandle(IntPtr hObject); - [Flags] - public enum ThreadAccess : int - { - TERMINATE = (0x0001), - SUSPEND_RESUME = (0x0002), - GET_CONTEXT = (0x0008), - SET_CONTEXT = (0x0010), - SET_INFORMATION = (0x0020), - QUERY_INFORMATION = (0x0040), - SET_THREAD_TOKEN = (0x0080), - IMPERSONATE = (0x0100), - DIRECT_IMPERSONATION = (0x0200) - } - - } -} diff --git a/EasyTool.Core/ToolCategory/ReflectUtil.cs b/EasyTool.Core/ToolCategory/ReflectUtil.cs index 2226924..f22b1dd 100644 --- a/EasyTool.Core/ToolCategory/ReflectUtil.cs +++ b/EasyTool.Core/ToolCategory/ReflectUtil.cs @@ -1,122 +1,16 @@ -using System; +using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; namespace EasyTool { /// - /// 反射工具类 + /// 反射工具类,提供类型、属性、字段、方法的反射操作 /// - public class ReflectUtil + public static class ReflectUtil { - /// - /// 根据类型名称获取Type对象 - /// [Obsolete("请直接使用 Type.GetType(typeName)")] - /// - /// 类型名称 - /// Type对象 - [Obsolete("请直接使用 Type.GetType(typeName)", false)] - public static Type GetType(string typeName) - { - return Type.GetType(typeName); - } - - /// - /// 获取指定程序集中的所有类型 - /// [Obsolete("请直接使用 assembly.GetTypes()")] - /// - /// 程序集 - /// 类型数组 - [Obsolete("请直接使用 assembly.GetTypes()", false)] - public static Type[] GetTypes(Assembly assembly) - { - return assembly.GetTypes(); - } - - /// - /// 获取指定类型所在的程序集 - /// [Obsolete("请直接使用 type.Assembly")] - /// - /// 类型 - /// 程序集 - [Obsolete("请直接使用 type.Assembly", false)] - public static Assembly GetAssembly(Type type) - { - return type.Assembly; - } - - /// - /// 获取指定类型的指定类型的特性 - /// [Obsolete("请直接使用 type.GetCustomAttribute()")] - /// - /// 特性类型 - /// 类型 - /// 特性对象 - [Obsolete("请直接使用 type.GetCustomAttribute()", false)] - public static T GetAttribute(Type type) where T : Attribute - { - return type.GetCustomAttribute(); - } - - /// - /// 获取指定类型的指定类型的特性数组 - /// - /// 特性类型 - /// 类型 - /// 特性数组 - public static T[] GetAttributes(Type type) where T : Attribute - { - return type.GetCustomAttributes().ToArray(); - } - - /// - /// 获取指定类型的默认值 - /// [Obsolete("请直接使用 type.IsValueType ? Activator.CreateInstance(type) : null")] - /// - /// 类型 - /// 默认值 - [Obsolete("请直接使用 type.IsValueType ? Activator.CreateInstance(type) : null", false)] - public static object GetDefaultValue(Type type) - { - return type.IsValueType ? Activator.CreateInstance(type) : null; - } - - /// - /// 获取类型的基类 - /// [Obsolete("请直接使用 type.BaseType")] - /// - /// 类型 - /// 基类 - [Obsolete("请直接使用 type.BaseType", false)] - public static Type GetBaseType(Type type) - { - return type.BaseType; - } - - /// - /// 判断类型是否实现了某个接口 - /// [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)")] - /// - /// 类型 - /// 接口类型 - /// 是否实现 - [Obsolete("请直接使用 interfaceType.IsAssignableFrom(type)", false)] - public static bool HasInterface(Type type, Type interfaceType) - { - return interfaceType.IsAssignableFrom(type); - } - - /// - /// 获取方法的参数信息 - /// [Obsolete("请直接使用 method.GetParameters()")] - /// - /// 方法 - /// 参数信息数组 - [Obsolete("请直接使用 method.GetParameters()", false)] - public static ParameterInfo[] GetParameters(MethodInfo method) - { - return method.GetParameters(); - } + #region 类型成员获取 /// /// 获取类型的所有构造函数 @@ -168,18 +62,6 @@ public static EventInfo[] GetEvents(Type type) return type.GetEvents(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } - /// - /// 获取类型的所有接口 - /// [Obsolete("请直接使用 type.GetInterfaces()")] - /// - /// 类型 - /// 接口数组 - [Obsolete("请直接使用 type.GetInterfaces()", false)] - public static Type[] GetInterfaces(Type type) - { - return type.GetInterfaces(); - } - /// /// 获取类型的所有属性名 /// @@ -227,9 +109,72 @@ public static string[] GetEventNames(Type type) /// 接口名数组 public static string[] GetInterfaceNames(Type type) { - return GetInterfaces(type).Select(i => i.Name).ToArray(); + return type.GetInterfaces().Select(i => i.Name).ToArray(); } + #endregion + + #region 类型特性 + + /// + /// 获取指定类型的指定类型的特性数组 + /// + /// 特性类型 + /// 类型 + /// 特性数组 + public static T[] GetAttributes(Type type) where T : Attribute + { + return type.GetCustomAttributes().ToArray(); + } + + /// + /// 判断类型是否实现了指定的接口 + /// + /// 要判断的类型 + /// 要判断的接口类型 + /// 是否实现了指定的接口 + public static bool ImplementsInterface() + { + return typeof(T).GetInterfaces().Any(i => i == typeof(TInterface)); + } + + /// + /// 获取类的继承层次结构 + /// + /// 要获取继承层次结构的类 + /// 类的继承层次结构 + public static Type[] GetClassHierarchy(Type type) + { + Type[] hierarchy = new Type[0]; + Type currentType = type; + while (currentType != null) + { + Array.Resize(ref hierarchy, hierarchy.Length + 1); + hierarchy[hierarchy.Length - 1] = currentType; + currentType = currentType.BaseType; + } + return hierarchy; + } + + /// + /// 获取枚举类型的所有值 + /// + /// 枚举类型 + /// 枚举类型的所有值 + public static IEnumerable GetEnumValues() + { + if (!typeof(T).IsEnum) + { + throw new ArgumentException("Type is not an enum type"); + } + + return Enum.GetValues(typeof(T)).Cast(); + } + + #endregion + + #region 实例创建 + /// /// 创建类型的实例 /// @@ -238,8 +183,9 @@ public static string[] GetInterfaceNames(Type type) /// 实例 public static object CreateInstance(Type type, params object[] args) { + Type[] parameterTypes = GetParameterTypes(args); ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, - null, args.Select(a => a.GetType()).ToArray(), null); + null, parameterTypes, null); if (constructor == null) { throw new ArgumentException($"Type {type} does not have a constructor with specified arguments"); @@ -247,6 +193,36 @@ public static object CreateInstance(Type type, params object[] args) return constructor.Invoke(args); } + /// + /// 获取构造函数参数类型的数组 + /// + /// 要获取参数类型的参数数组 + /// 参数类型的数组 + private static Type[] GetParameterTypes(object[] parameters) + { + if (parameters == null) + { + return Type.EmptyTypes; + } + Type[] parameterTypes = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + if (parameters[i] == null) + { + parameterTypes[i] = typeof(object); + } + else + { + parameterTypes[i] = parameters[i].GetType(); + } + } + return parameterTypes; + } + + #endregion + + #region 方法调用 + /// /// 调用泛型方法 /// @@ -261,5 +237,86 @@ public static object InvokeGenericMethod(object obj, string methodName, Type gen MethodInfo genericMethod = method.MakeGenericMethod(genericType); return genericMethod.Invoke(obj, args); } + + /// + /// 动态调用类的实例方法 + /// + /// 要调用实例方法的类实例 + /// 要调用的实例方法的名称 + /// 要传递给实例方法的参数 + /// 实例方法的返回值 + public static object InvokeMethod(object instance, string methodName, params object[] arguments) + { + Type type = instance.GetType(); + MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); + return method.Invoke(instance, arguments); + } + + /// + /// 动态调用类的静态方法 + /// + /// 要调用静态方法的类 + /// 要调用的静态方法的名称 + /// 要传递给静态方法的参数 + /// 静态方法的返回值 + public static object InvokeStaticMethod(Type type, string methodName, params object[] arguments) + { + MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public); + return method.Invoke(null, arguments); + } + + #endregion + + #region 静态成员操作 + + /// + /// 获取类的静态属性的值 + /// + /// 要获取静态属性的类 + /// 要获取的静态属性的名称 + /// 静态属性的值 + public static object GetStaticPropertyValue(Type type, string propertyName) + { + PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public); + return property.GetValue(null); + } + + /// + /// 设置类的静态属性的值 + /// + /// 要设置静态属性的类 + /// 要设置的静态属性的名称 + /// 要设置的静态属性的值 + public static void SetStaticPropertyValue(Type type, string propertyName, object value) + { + PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public); + property.SetValue(null, value); + } + + /// + /// 获取类的静态字段的值 + /// + /// 要获取静态字段的类 + /// 要获取的静态字段的名称 + /// 静态字段的值 + public static object GetStaticFieldValue(Type type, string fieldName) + { + FieldInfo field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Public); + return field.GetValue(null); + } + + /// + /// 设置类的静态字段的值 + /// + /// 要设置静态字段的类 + /// 要设置的静态字段的名称 + /// 要设置的静态字段的值 + public static void SetStaticFieldValue(Type type, string fieldName, object value) + { + FieldInfo field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Public); + field.SetValue(null, value); + } + + #endregion } } diff --git a/EasyTool.Core/ToolCategory/RuntimeUtil.cs b/EasyTool.Core/ToolCategory/RuntimeUtil.cs deleted file mode 100644 index eea6036..0000000 --- a/EasyTool.Core/ToolCategory/RuntimeUtil.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -#if NET5_0_OR_GREATER -using System.Runtime.Versioning; -#endif -using System.Text; - -namespace EasyTool -{ - /// - /// 运行时工具 - /// - public class RuntimeUtil - { - /// - /// 获取当前运行的 .NET 版本 - /// [Obsolete("请直接使用 Environment.Version.ToString()")] - /// - /// .NET 版本 - [Obsolete("请直接使用 Environment.Version.ToString()", false)] - public static string GetDotNetVersion() - { - return Environment.Version.ToString(); - } - - /// - /// 获取当前操作系统版本 - /// [Obsolete("请直接使用 Environment.OSVersion.ToString()")] - /// - /// 操作系统版本 - [Obsolete("请直接使用 Environment.OSVersion.ToString()", false)] - public static string GetOSVersion() - { - return Environment.OSVersion.ToString(); - } - - /// - /// 获取当前运行环境的处理器架构 - /// - /// 处理器架构 - public static string GetProcessArchitecture() - { - return Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit"; - } - - /// - /// 获取当前应用程序内存使用量 - /// - /// 内存使用量(字节) - public static long GetCurrentMemoryUsage() - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - return GC.GetTotalMemory(true); - } - - /// - /// 获取当前运行时间 - /// - /// 运行时间(秒) - public static int GetCurrentRunningTime() - { - return (int)Stopwatch.StartNew().Elapsed.TotalSeconds; - } - - /// - /// 关闭当前应用程序 - /// - public static void ExitApplication() - { - Environment.Exit(0); - } - - /// - /// 获取当前系统的物理内存总量 - /// - /// 物理内存总量(字节) -#if NET5_0_OR_GREATER - [SupportedOSPlatform("windows")] -#endif - public static long GetTotalPhysicalMemory() - { - PerformanceCounter pc = new PerformanceCounter("Memory", "Available Bytes"); - return pc.RawValue; - } - - /// - /// 获取当前系统的可用物理内存量 - /// - /// 可用物理内存量(字节) -#if NET5_0_OR_GREATER - [SupportedOSPlatform("windows")] -#endif - public static float GetAvailablePhysicalMemory() - { - PerformanceCounter pc = new PerformanceCounter("Memory", "Available Bytes"); - return pc.NextValue(); - } - - /// - /// 获取当前系统的虚拟内存总量 - /// - /// 虚拟内存总量(字节) -#if NET5_0_OR_GREATER - [SupportedOSPlatform("windows")] -#endif - public static long GetTotalVirtualMemory() - { - PerformanceCounter pc = new PerformanceCounter("Memory", "Committed Bytes"); - return pc.RawValue; - } - - /// - /// 获取当前系统的可用虚拟内存量 - /// - /// 可用虚拟内存量(字节) -#if NET5_0_OR_GREATER - [SupportedOSPlatform("windows")] -#endif - public static float GetAvailableVirtualMemory() - { - PerformanceCounter pc = new PerformanceCounter("Memory", "Committed Bytes"); - return pc.NextValue(); - } - - [DllImport("kernel32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool GetPhysicallyInstalledSystemMemory(out long TotalMemoryInKilobytes); - - /// - /// 获取当前系统的实际物理内存总量 - /// - /// 实际物理内存总量(字节) -#if NET5_0_OR_GREATER - [SupportedOSPlatform("windows")] -#endif - public static long GetRealTotalPhysicalMemory() - { - GetPhysicallyInstalledSystemMemory(out long memoryInBytes); - return memoryInBytes * 1024; - } - } -} diff --git a/EasyTool.Core/ToolCategory/StrExtension.cs b/EasyTool.Core/ToolCategory/StrExtension.cs index 20933be..a5fd201 100644 --- a/EasyTool.Core/ToolCategory/StrExtension.cs +++ b/EasyTool.Core/ToolCategory/StrExtension.cs @@ -11,27 +11,6 @@ namespace EasyTool.Extension public static class StrExtension { #region 文本可为空判断 - - /// - /// 判断字符串是否为 null 或空 - /// [Obsolete("请直接使用 string.IsNullOrEmpty(value)")] - /// - [Obsolete("请直接使用 string.IsNullOrEmpty(value)", false)] - public static bool IsNullOrEmpty(this string value) - { - return string.IsNullOrEmpty(value); - } - - /// - /// 判断字符串是否为 null 或空白字符 - /// [Obsolete("请直接使用 string.IsNullOrWhiteSpace(value)")] - /// - [Obsolete("请直接使用 string.IsNullOrWhiteSpace(value)", false)] - public static bool IsNullOrWhiteSpace(this string value) - { - return string.IsNullOrWhiteSpace(value); - } - #endregion #region 字符串验证 @@ -230,33 +209,6 @@ public static string GenerateSlug(this string value) return slug; } - /// - /// 反转字符串 - /// [Obsolete("请直接使用 new string(value.Reverse().ToArray()) 或通过 LINQ")] - /// - [Obsolete("请直接使用 new string(value.Reverse().ToArray())", false)] - public static string Reverse(this string value) - { - if (string.IsNullOrEmpty(value)) - return value; - - var charArray = value.ToCharArray(); - Array.Reverse(charArray); - return new string(charArray); - } - - /// - /// 获取字符串的字节数(UTF-8编码) - /// [Obsolete("请直接使用 Encoding.UTF8.GetByteCount(value)")] - /// - [Obsolete("请直接使用 Encoding.UTF8.GetByteCount(value)", false)] - public static int GetByteCount(this string value) - { - if (string.IsNullOrEmpty(value)) - return 0; - - return Encoding.UTF8.GetByteCount(value); - } /// /// 隐藏字符串的中间部分(例如:手机号、身份证号) diff --git a/EasyTool.Core/ToolCategory/StrUtil.cs b/EasyTool.Core/ToolCategory/StrUtil.cs index aaaf9be..3e835f7 100644 --- a/EasyTool.Core/ToolCategory/StrUtil.cs +++ b/EasyTool.Core/ToolCategory/StrUtil.cs @@ -20,19 +20,6 @@ public static string RemoveAllSpaces(string str) return Regex.Replace(str, @"\s+", ""); } - /// - /// 将字符串中的指定字符替换成新的字符 - /// [Obsolete("请直接使用 str.Replace(oldChar, newChar)")] - /// - /// 要处理的字符串 - /// 要替换的字符 - /// 新的字符 - /// 处理后的字符串 - [Obsolete("请直接使用 str.Replace(oldChar, newChar)", false)] - public static string ReplaceChar(string str, char oldChar, char newChar) - { - return str.Replace(oldChar, newChar); - } /// /// 检查字符串是否为数字 @@ -67,91 +54,12 @@ public static bool IsDate(string str) return DateTime.TryParse(str, out result); } - /// - /// 获取字符串的字节数组 - /// [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)")] - /// - /// 要处理的字符串 - /// 字符串的字节数组 - [Obsolete("请直接使用 Encoding.UTF8.GetBytes(str)", false)] - public static byte[] GetBytes(string str) - { - return System.Text.Encoding.UTF8.GetBytes(str); - } - /// - /// 将字节数组转换为字符串 - /// [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)")] - /// - /// 要处理的字节数组 - /// 字节数组转换后的字符串 - [Obsolete("请直接使用 Encoding.UTF8.GetString(bytes)", false)] - public static string GetString(byte[] bytes) - { - return System.Text.Encoding.UTF8.GetString(bytes); - } - /// - /// 将字符串转换为大写 - /// [Obsolete("请直接使用 str.ToUpper()")] - /// - /// 要处理的字符串 - /// 处理后的字符串 - [Obsolete("请直接使用 str.ToUpper()", false)] - public static string ToUpperCase(string str) - { - return str.ToUpper(); - } - /// - /// 将字符串转换为小写 - /// [Obsolete("请直接使用 str.ToLower()")] - /// - /// 要处理的字符串 - /// 处理后的字符串 - [Obsolete("请直接使用 str.ToLower()", false)] - public static string ToLowerCase(string str) - { - return str.ToLower(); - } - /// - /// 检查字符串是否为空或null - /// [Obsolete("请直接使用 string.IsNullOrEmpty(str)")] - /// - /// 要检查的字符串 - /// 如果是空或null,则返回true,否则返回false - [Obsolete("请直接使用 string.IsNullOrEmpty(str)", false)] - public static bool IsNullOrEmpty(string str) - { - return string.IsNullOrEmpty(str); - } - /// - /// 检查字符串是否为空或仅由空格组成 - /// [Obsolete("请直接使用 string.IsNullOrWhiteSpace(str)")] - /// - /// 要检查的字符串 - /// 如果是空或仅由空格组成,则返回true,否则返回false - [Obsolete("请直接使用 string.IsNullOrWhiteSpace(str)", false)] - public static bool IsNullOrWhiteSpace(string str) - { - return string.IsNullOrWhiteSpace(str); - } - /// - /// 截取字符串的指定部分 - /// [Obsolete("请直接使用 str.Substring(startIndex, length)")] - /// - /// 要处理的字符串 - /// 起始位置(从0开始) - /// 要截取的长度 - /// 截取后的字符串 - [Obsolete("请直接使用 str.Substring(startIndex, length)", false)] - public static string Substring(string str, int startIndex, int length) - { - return str.Substring(startIndex, length); - } /// /// 将字符串转换为驼峰命名法 @@ -161,19 +69,20 @@ public static string Substring(string str, int startIndex, int length) public static string ToCamelCase(string str) { string[] words = str.Split(new char[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries); - string result = ""; + var sb = new StringBuilder(); for (int i = 0; i < words.Length; i++) { if (i == 0) { - result += words[i].ToLower(); + sb.Append(words[i].ToLower()); } else { - result += words[i].Substring(0, 1).ToUpper() + words[i].Substring(1).ToLower(); + sb.Append(words[i].Substring(0, 1).ToUpper()); + sb.Append(words[i].Substring(1).ToLower()); } } - return result; + return sb.ToString(); } /// @@ -184,12 +93,13 @@ public static string ToCamelCase(string str) public static string ToPascalCase(string str) { string[] words = str.Split(new char[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries); - string result = ""; + var sb = new StringBuilder(); for (int i = 0; i < words.Length; i++) { - result += words[i].Substring(0, 1).ToUpper() + words[i].Substring(1).ToLower(); + sb.Append(words[i].Substring(0, 1).ToUpper()); + sb.Append(words[i].Substring(1).ToLower()); } - return result; + return sb.ToString(); } /// @@ -200,19 +110,20 @@ public static string ToPascalCase(string str) public static string ToSnakeCase(string str) { string[] words = str.Split(new char[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries); - string result = ""; + var sb = new StringBuilder(); for (int i = 0; i < words.Length; i++) { if (i == 0) { - result += words[i].ToLower(); + sb.Append(words[i].ToLower()); } else { - result += "_" + words[i].ToLower(); + sb.Append('_'); + sb.Append(words[i].ToLower()); } } - return result; + return sb.ToString(); } /// @@ -223,19 +134,20 @@ public static string ToSnakeCase(string str) public static string ToKebabCase(string str) { string[] words = str.Split(new char[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries); - string result = ""; + var sb = new StringBuilder(); for (int i = 0; i < words.Length; i++) { if (i == 0) { - result += words[i].ToLower(); + sb.Append(words[i].ToLower()); } else { - result += "-" + words[i].ToLower(); + sb.Append('-'); + sb.Append(words[i].ToLower()); } } - return result; + return sb.ToString(); } /// @@ -259,33 +171,7 @@ public static bool EqualsIgnoreCaseAndWhiteSpace(string str1, string str2) return string.Equals(RemoveAllSpaces(str1), RemoveAllSpaces(str2), StringComparison.OrdinalIgnoreCase); } - /// - /// 在字符串的左侧填充指定字符,使字符串达到指定长度 - /// [Obsolete("请直接使用 str.PadLeft(length, paddingChar)")] - /// - /// 要处理的字符串 - /// 指定长度 - /// 填充字符 - /// 处理后的字符串 - [Obsolete("请直接使用 str.PadLeft(length, paddingChar)", false)] - public static string PadLeft(string str, int length, char paddingChar) - { - return str.PadLeft(length, paddingChar); - } - /// - /// 在字符串的右侧填充指定字符,使字符串达到指定长度 - /// [Obsolete("请直接使用 str.PadRight(length, paddingChar)")] - /// - /// 要处理的字符串 - /// 指定长度 - /// 填充字符 - /// 处理后的字符串 - [Obsolete("请直接使用 str.PadRight(length, paddingChar)", false)] - public static string PadRight(string str, int length, char paddingChar) - { - return str.PadRight(length, paddingChar); - } /// /// 将字符串中的某些字符替换成指定的字符 diff --git a/EasyTool.Core/ToolCategory/StringBuilderExtension.cs b/EasyTool.Core/ToolCategory/StringBuilderExtension.cs index c5c3294..bbeeadc 100644 --- a/EasyTool.Core/ToolCategory/StringBuilderExtension.cs +++ b/EasyTool.Core/ToolCategory/StringBuilderExtension.cs @@ -313,16 +313,6 @@ public static StringBuilder Trim(this StringBuilder sb) #region 转换操作 - /// - /// 转换为只读字符串 - /// [Obsolete("请直接使用 sb?.ToString() ?? string.Empty")] - /// - [Obsolete("请直接使用 sb?.ToString() ?? string.Empty", false)] - public static string ToReadOnly(this StringBuilder sb) - { - return sb?.ToString() ?? string.Empty; - } - /// /// 转换为 MemoryStream /// diff --git a/EasyTool.Core/ToolCategory/StringComparisonExtension.cs b/EasyTool.Core/ToolCategory/StringComparisonExtension.cs index 1304e6b..b81c809 100644 --- a/EasyTool.Core/ToolCategory/StringComparisonExtension.cs +++ b/EasyTool.Core/ToolCategory/StringComparisonExtension.cs @@ -352,31 +352,6 @@ public static string ToTitleCase(this string str, CultureInfo? culture = null) #region 大小写转换 - /// - /// 转换为大写(不变则为返回原字符串) - /// [Obsolete("请直接使用 value.ToUpper()")] - /// - [Obsolete("请直接使用 value.ToUpper()", false)] - public static string ToUpperSafe(this string str) - { - if (string.IsNullOrEmpty(str)) - return str; - - return str.ToUpper(); - } - - /// - /// 转换为小写(不变则为返回原字符串) - /// [Obsolete("请直接使用 value.ToLower()")] - /// - [Obsolete("请直接使用 value.ToLower()", false)] - public static string ToLowerSafe(this string str) - { - if (string.IsNullOrEmpty(str)) - return str; - - return str.ToLower(); - } /// /// 转换为单词首字母大写(如:helloWorld -> HelloWorld) diff --git a/EasyTool.Core/ToolCategory/SystemUtil.cs b/EasyTool.Core/ToolCategory/SystemUtil.cs new file mode 100644 index 0000000..154b48a --- /dev/null +++ b/EasyTool.Core/ToolCategory/SystemUtil.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace EasyTool +{ + /// + /// 系统工具类,提供系统、进程、内存、网络等相关功能 + /// + public static class SystemUtil + { + #region DLL 工具 + + /// + /// 根据类型名称创建实例,并返回一个 Object 对象 + /// + /// 程序集 + /// 类型名称 + /// 实例化类型所需要的参数 + /// 返回创建的实例对象 + public static object? CreateInstanceFromAssembly(Assembly assembly, string typeName, params object[] parameters) + { + Type? type = assembly.GetType(typeName); + if (type != null) + { + return Activator.CreateInstance(type, parameters); + } + return null; + } + + /// + /// 调用对象的方法,并返回调用结果 + /// + /// 要调用方法的对象 + /// 方法名称 + /// 方法所需要的参数 + /// 返回调用结果 + public static object? InvokeMethod(object instance, string methodName, params object[] parameters) + { + Type type = instance.GetType(); + MethodInfo? methodInfo = type.GetMethod(methodName); + if (methodInfo != null) + { + return methodInfo.Invoke(instance, parameters); + } + return null; + } + + /// + /// 从指定目录中加载所有的 DLL 文件,并返回一个 Assembly[] 数组 + /// + /// 要加载 DLL 文件的目录 + /// 返回一个 Assembly[] 数组,数组中每个元素代表一个 DLL 程序集 + public static Assembly[] LoadAllDllsFromDirectory(string directory) + { + try + { + if (!Directory.Exists(directory)) + { + throw new Exception("LoadAllDllsFromDirectory Error: Directory not exist."); + } + + string[] dllFiles = Directory.GetFiles(directory, "*.dll"); + if (dllFiles.Length == 0) + { + throw new Exception("LoadAllDllsFromDirectory Error: No DLL file found."); + } + + Assembly[] assemblies = new Assembly[dllFiles.Length]; + for (int i = 0; i < dllFiles.Length; i++) + { + assemblies[i] = Assembly.LoadFile(dllFiles[i]); + } + return assemblies; + } + catch (Exception ex) + { + throw new Exception("LoadAllDllsFromDirectory Error: " + ex.Message); + } + } + + #endregion + + #region 进程工具 + + /// + /// 通过进程名称获取进程 + /// + /// 进程名称 + /// 进程 + public static Process? GetProcessByName(string processName) + { + var processes = Process.GetProcessesByName(processName); + if (processes.Length > 0) + { + return processes[0]; + } + return null; + } + + /// + /// 判断进程是否存在 + /// + /// 进程名称 + /// 是否存在 + public static bool IsProcessExists(string processName) + { + return Process.GetProcessesByName(processName).Length > 0; + } + + /// + /// 暂停进程 + /// + /// 进程 + public static void SuspendProcess(Process process) + { + foreach (ProcessThread thread in process.Threads) + { + IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); + if (pOpenThread == IntPtr.Zero) + { + break; + } + SuspendThread(pOpenThread); + CloseHandle(pOpenThread); + } + } + + /// + /// 恢复进程 + /// + /// 进程 + public static void ResumeProcess(Process process) + { + foreach (ProcessThread thread in process.Threads) + { + IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); + if (pOpenThread == IntPtr.Zero) + { + break; + } + ResumeThread(pOpenThread); + CloseHandle(pOpenThread); + } + } + + [DllImport("kernel32.dll")] + static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); + + [DllImport("kernel32.dll")] + static extern uint SuspendThread(IntPtr hThread); + + [DllImport("kernel32.dll")] + static extern IntPtr CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll")] + static extern uint ResumeThread(IntPtr hThread); + + [Flags] + public enum ThreadAccess : int + { + TERMINATE = (0x0001), + SUSPEND_RESUME = (0x0002), + GET_CONTEXT = (0x0008), + SET_CONTEXT = (0x0010), + SET_INFORMATION = (0x0020), + QUERY_INFORMATION = (0x0040), + SET_THREAD_TOKEN = (0x0080), + IMPERSONATE = (0x0100), + DIRECT_IMPERSONATION = (0x0200) + } + + #endregion + + #region 运行时工具 + + /// + /// 获取当前运行的处理器架构 + /// + /// 处理器架构 + public static string GetProcessArchitecture() + { + return Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit"; + } + + /// + /// 获取当前应用程序内存使用量 + /// + /// 内存使用量(字节) + public static long GetCurrentMemoryUsage() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + return GC.GetTotalMemory(true); + } + + /// + /// 关闭当前应用程序 + /// + public static void ExitApplication() + { + Environment.Exit(0); + } + + #endregion + + #region 网络工具 + + /// + /// 对指定主机进行 Ping 测试,返回是否成功 + /// + /// 主机名或IP地址 + /// 是否成功 + public static bool Ping(string host) + { + try + { + Ping pingSender = new Ping(); + PingReply reply = pingSender.Send(host); + if (reply.Status == IPStatus.Success) + { + return true; + } + else + { + return false; + } + } + catch + { + return false; + } + } + + /// + /// 获取指定主机的IP地址 + /// + /// 主机名 + /// IP地址 + public static IPAddress? GetIpAddress(string host) + { + try + { + IPHostEntry hostEntry = Dns.GetHostEntry(host); + foreach (IPAddress address in hostEntry.AddressList) + { + if (address.AddressFamily == AddressFamily.InterNetwork) + { + return address; + } + } + return null; + } + catch + { + return null; + } + } + + /// + /// 检查给定IP地址上的端口是否开放 + /// + /// IP地址 + /// 端口号 + /// 端口是否开放 + public static bool IsPortOpen(string host, int port) + { + try + { + IPAddress ipAddress = GetIpAddress(host); + if (ipAddress == null) + { + return false; + } + + IPEndPoint endpoint = new IPEndPoint(ipAddress, port); + using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + socket.Connect(endpoint); + return true; + } + } + catch + { + return false; + } + } + + /// + /// 发送HTTP GET请求并返回响应 + /// + /// URL地址 + /// 响应内容 + public static async Task HttpGetAsync(string url) + { + using (HttpClient client = new HttpClient()) + { + return await client.GetStringAsync(url); + } + } + + /// + /// 发送HTTP POST请求并返回响应 + /// + /// URL地址 + /// 要发送的数据 + /// 响应内容 + public static async Task HttpPostAsync(string url, string data) + { + using (HttpClient client = new HttpClient()) + { + StringContent content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded"); + HttpResponseMessage response = await client.PostAsync(url, content); + return await response.Content.ReadAsStringAsync(); + } + } + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/TypeExtension.cs b/EasyTool.Core/ToolCategory/TypeExtension.cs index 5e35baa..547511b 100644 --- a/EasyTool.Core/ToolCategory/TypeExtension.cs +++ b/EasyTool.Core/ToolCategory/TypeExtension.cs @@ -237,18 +237,6 @@ public static string GetDescription(this Type? type) #region 泛型处理 - /// - /// 获取可空类型的实际类型 - /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type")] - /// - [Obsolete("请直接使用 Nullable.GetUnderlyingType(type) ?? type", false)] - public static Type? GetNullableType(this Type? type) - { - if (type == null) - return null; - - return Nullable.GetUnderlyingType(type) ?? type; - } /// /// 获取集合的元素类型 diff --git a/EasyTool.Core/ToolCategory/TypeUtil.cs b/EasyTool.Core/ToolCategory/TypeUtil.cs deleted file mode 100644 index 6fdd218..0000000 --- a/EasyTool.Core/ToolCategory/TypeUtil.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace EasyTool -{ - /// - /// 泛型类型工具 - /// - public class TypeUtil - { - /// - /// 判断类型是否是可空类型 - /// [Obsolete("请直接使用 Nullable.GetUnderlyingType(typeof(T)) != null")] - /// - /// 要判断的类型 - /// 是否是可空类型 - [Obsolete("请直接使用 Nullable.GetUnderlyingType(typeof(T)) != null", false)] - public static bool IsNullable() where T : struct - { - return Nullable.GetUnderlyingType(typeof(T)) != null; - } - - /// - /// 判断类型是否是枚举类型 - /// [Obsolete("请直接使用 typeof(T).IsEnum")] - /// - /// 要判断的类型 - /// 是否是枚举类型 - [Obsolete("请直接使用 typeof(T).IsEnum", false)] - public static bool IsEnum() - { - return typeof(T).IsEnum; - } - - /// - /// 获取泛型类型的参数类型 - /// [Obsolete("请直接使用 typeof(T).GetGenericArguments()")] - /// - /// 要获取参数类型的泛型类型 - /// 泛型类型的参数类型数组 - [Obsolete("请直接使用 typeof(T).GetGenericArguments()", false)] - public static Type[] GetGenericArguments() - { - return typeof(T).GetGenericArguments(); - } - - /// - /// 获取类型的所有属性 - /// [Obsolete("请直接使用 typeof(T).GetProperties()")] - /// - /// 要获取属性的类型 - /// 属性数组 - [Obsolete("请直接使用 typeof(T).GetProperties()", false)] - public static PropertyInfo[] GetProperties() - { - return typeof(T).GetProperties(); - } - - /// - /// 获取类型的所有字段 - /// [Obsolete("请直接使用 typeof(T).GetFields()")] - /// - /// 要获取字段的类型 - /// 字段数组 - [Obsolete("请直接使用 typeof(T).GetFields()", false)] - public static FieldInfo[] GetFields() - { - return typeof(T).GetFields(); - } - - /// - /// 获取类型的所有方法 - /// [Obsolete("请直接使用 typeof(T).GetMethods()")] - /// - /// 要获取方法的类型 - /// 方法数组 - [Obsolete("请直接使用 typeof(T).GetMethods()", false)] - public static MethodInfo[] GetMethods() - { - return typeof(T).GetMethods(); - } - - /// - /// 获取类型的所有事件 - /// [Obsolete("请直接使用 typeof(T).GetEvents()")] - /// - /// 要获取事件的类型 - /// 事件数组 - [Obsolete("请直接使用 typeof(T).GetEvents()", false)] - public static EventInfo[] GetEvents() - { - return typeof(T).GetEvents(); - } - - /// - /// 获取类型的所有属性、字段、方法和事件 - /// [Obsolete("请直接使用 typeof(T).GetMembers()")] - /// - /// 要获取成员的类型 - /// 成员数组 - [Obsolete("请直接使用 typeof(T).GetMembers()", false)] - public static MemberInfo[] GetMembers() - { - return typeof(T).GetMembers(); - } - - /// - /// 获取类型的所有构造函数 - /// [Obsolete("请直接使用 typeof(T).GetConstructors()")] - /// - /// 要获取构造函数的类型 - /// 构造函数数组 - [Obsolete("请直接使用 typeof(T).GetConstructors()", false)] - public static ConstructorInfo[] GetConstructors() - { - return typeof(T).GetConstructors(); - } - - /// - /// 判断类型是否实现了指定的接口 - /// - /// 要判断的类型 - /// 要判断的接口类型 - /// 是否实现了指定的接口 - public static bool ImplementsInterface() - { - return typeof(T).GetInterfaces().Any(i => i == typeof(TInterface)); - } - - /// - /// 判断类型是否继承了指定的基类 - /// [Obsolete("请直接使用 typeof(T).IsSubclassOf(typeof(TBase))")] - /// - /// 要判断的类型 - /// 要判断的基类类型 - /// 是否继承了指定的基类 - [Obsolete("请直接使用 typeof(T).IsSubclassOf(typeof(TBase))", false)] - public static bool InheritsFrom() - { - return typeof(T).IsSubclassOf(typeof(TBase)); - } - - /// - /// 创建指定类型的实例 - /// [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T))")] - /// - /// 要创建实例的类型 - /// 类型的实例 - [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T))", false)] - public static T CreateInstance() - { - return (T)Activator.CreateInstance(typeof(T)); - } - - /// - /// 创建指定类型的实例 - /// [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T), args)")] - /// - /// 要创建实例的类型 - /// 构造函数的参数 - /// 类型的实例 - [Obsolete("请直接使用 (T)Activator.CreateInstance(typeof(T), args)", false)] - public static T CreateInstance(params object[] args) - { - return (T)Activator.CreateInstance(typeof(T), args); - } - - /// - /// 获取枚举类型的所有值 - /// - /// 枚举类型 - /// 枚举类型的所有值 - public static IEnumerable GetEnumValues() - { - if (!IsEnum()) - { - throw new ArgumentException("Type is not an enum type"); - } - - return Enum.GetValues(typeof(T)).Cast(); - } - - /// - /// 将字符串转换为指定类型的值 - /// [Obsolete("请直接使用 (T)Convert.ChangeType(value, typeof(T))")] - /// - /// 要转换的类型 - /// 要转换的字符串 - /// 转换后的值 - [Obsolete("请直接使用 (T)Convert.ChangeType(value, typeof(T))", false)] - public static T ConvertFromString(string value) - { - return (T)Convert.ChangeType(value, typeof(T)); - } - - /// - /// 将值转换为指定类型的字符串 - /// [Obsolete("请直接使用 Convert.ToString(value)")] - /// - /// 要转换的类型 - /// 要转换的值 - /// 转换后的字符串 - [Obsolete("请直接使用 Convert.ToString(value)", false)] - public static string ConvertToString(T value) - { - return Convert.ToString(value); - } - } -} diff --git a/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs b/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs index f5fdbaa..df44f68 100644 --- a/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs +++ b/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs @@ -7,7 +7,7 @@ namespace EasyTool.Tests public class CloneExtensionTests { [TestMethod()] - public void CloneTest() + public void DeepCloneTest() { var obj1 = new First() { @@ -24,7 +24,7 @@ public void CloneTest() MyProperty2 = "C", } }; - var obj2 = obj1.Clone(); + var obj2 = obj1.DeepClone(); Assert.AreEqual(obj1.MyProperty1, obj2.MyProperty1); Assert.AreEqual(obj1.Second1.MyProperty1, obj2.Second1.MyProperty1); From 353e1eb092e83c4d6d1945c7a1f5af8e6e849e74 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 16:42:20 +0800 Subject: [PATCH 04/14] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0AES?= =?UTF-8?q?=E5=92=8CDES=E5=8A=A0=E5=AF=86=E5=B7=A5=E5=85=B7=E7=B1=BB?= =?UTF-8?q?=E5=B9=B6=E9=87=8D=E6=9E=84=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在AesUtil中新增字节数组版本的加密解密方法 - 在DesUtil中修复IV设置错误并添加字节数组版本方法 - 将多个工具类从Extension命名空间迁移到对应的功能分类命名空间 - 重命名CreditCodeUtil和DesensitizedUtil为静态类 - 添加新的SystemCategory.EnvUtil工具类 - 修复EncodingUtil中BASE32编码的位移计算错误 - 更新RandomUtil引用路径解决依赖问题 - 标记ConvertExtension中的ToHex方法为过时并提供替代方案 --- EasyTool.Core/CodeCategory/AesUtil.cs | 125 +++++++ EasyTool.Core/CodeCategory/DesUtil.cs | 70 +++- EasyTool.Core/CodeCategory/EncodingUtil.cs | 39 ++- EasyTool.Core/CodeCategory/HashUtil.cs | 114 +++++-- EasyTool.Core/CodeCategory/HexUtil.cs | 67 +++- .../CollectionsCategory/ArrayExtension.cs | 2 +- .../DictionaryExtension.cs | 2 +- .../CollectionsCategory/LinkedListUtil.cs | 4 +- .../CollectionsCategory/ListExtension.cs | 2 +- .../CollectionsCategory/QueueUtil.cs | 4 +- .../CollectionsCategory/StackUtil.cs | 100 +----- .../ConvertCategory/ByteExtension.cs | 2 +- .../ColorExtension.cs | 2 +- .../ConvertCategory/ConvertExtension.cs | 47 +-- .../ConvertCategory/NumberExtension.cs | 2 +- .../PageUtil.cs | 2 +- .../DateTimeCategory/DateTimeExtension.cs | 2 +- .../DateTimeCategory/DateTimeUtil.cs | 2 +- .../DateTimeCategory/LunarCalendarUtil.cs | 4 +- EasyTool.Core/DateTimeCategory/TimerUtil.cs | 4 +- EasyTool.Core/EmojiCategory/EmojiUtil.cs | 4 +- .../IOCategory/FileSystemExtension.cs | 3 +- EasyTool.Core/IOCategory/FileTypeExtension.cs | 2 +- EasyTool.Core/IOCategory/FileUtil.cs | 41 +-- EasyTool.Core/IOCategory/StreamExtension.cs | 2 +- EasyTool.Core/IOCategory/Tailer.cs | 2 +- EasyTool.Core/IOCategory/WatchMonitor.cs | 2 +- .../{ToolCategory => IOCategory}/ZipUtil.cs | 4 +- EasyTool.Core/MathCategory/MathUtil.cs | 2 +- EasyTool.Core/MathCategory/PredictUtil.cs | 4 +- EasyTool.Core/MathCategory/RandomUtil.cs | 4 +- .../NetCategory/HttpClientExtension.cs | 2 +- .../{ToolCategory => NetCategory}/IpUtil.cs | 4 +- EasyTool.Core/NetCategory/URLUtil.cs | 4 +- .../ReflectUtil.cs | 2 +- .../DesensitizedUtil.cs | 4 +- EasyTool.Core/Standardization/Option.cs | 2 +- EasyTool.Core/Standardization/QueryPage.cs | 2 +- EasyTool.Core/SystemCategory/EnvUtil.cs | 97 ++++++ .../SystemUtil.cs | 2 +- EasyTool.Core/TextCategory/RegexUtil.cs | 4 +- EasyTool.Core/TextCategory/StrSplitter.cs | 4 +- .../{ToolCategory => TextCategory}/StrUtil.cs | 4 +- .../{ToolCategory => TextCategory}/XmlUtil.cs | 4 +- EasyTool.Core/ToolCategory/CreditCodeUtil.cs | 6 +- .../ToolCategory/DelegateExtension.cs | 2 +- EasyTool.Core/ToolCategory/EnumExtension.cs | 2 +- EasyTool.Core/ToolCategory/EnvUtil.cs | 309 ------------------ .../ToolCategory/ExceptionExtension.cs | 2 +- EasyTool.Core/ToolCategory/GuidExtension.cs | 2 +- EasyTool.Core/ToolCategory/IdUtil.cs | 4 +- EasyTool.Core/ToolCategory/ObjectExtension.cs | 2 +- .../ToolCategory/PropertyInfoExtension.cs | 2 +- .../ToolCategory/SimpleMapExtension.cs | 12 +- EasyTool.Core/ToolCategory/StrExtension.cs | 2 +- .../ToolCategory/StringBuilderExtension.cs | 2 +- .../ToolCategory/StringComparisonExtension.cs | 2 +- EasyTool.Core/ToolCategory/TaskExtension.cs | 2 +- EasyTool.Core/ToolCategory/TypeExtension.cs | 2 +- .../CloneCategory/CloneExtensionTests.cs | 2 +- .../MathCategory/MathUtilTests.cs | 2 +- .../Standardization/OptionTests.cs | 2 +- .../ToolCategory/IDUtilTests.cs | 2 +- .../ToolCategory/IpUtilTests.cs | 2 +- .../ToolCategory/SimpleMapExtensionTests.cs | 2 +- 65 files changed, 583 insertions(+), 583 deletions(-) rename EasyTool.Core/{ToolCategory => ConvertCategory}/ColorExtension.cs (99%) rename EasyTool.Core/{ToolCategory => DataCategory}/PageUtil.cs (99%) rename EasyTool.Core/{ToolCategory => IOCategory}/ZipUtil.cs (98%) rename EasyTool.Core/{ToolCategory => NetCategory}/IpUtil.cs (99%) rename EasyTool.Core/{ToolCategory => ReflectCategory}/ReflectUtil.cs (99%) rename EasyTool.Core/{ToolCategory => SecurityCategory}/DesensitizedUtil.cs (98%) create mode 100644 EasyTool.Core/SystemCategory/EnvUtil.cs rename EasyTool.Core/{ToolCategory => SystemCategory}/SystemUtil.cs (99%) rename EasyTool.Core/{ToolCategory => TextCategory}/StrUtil.cs (99%) rename EasyTool.Core/{ToolCategory => TextCategory}/XmlUtil.cs (99%) delete mode 100644 EasyTool.Core/ToolCategory/EnvUtil.cs diff --git a/EasyTool.Core/CodeCategory/AesUtil.cs b/EasyTool.Core/CodeCategory/AesUtil.cs index e2d2bb2..c7c8142 100644 --- a/EasyTool.Core/CodeCategory/AesUtil.cs +++ b/EasyTool.Core/CodeCategory/AesUtil.cs @@ -168,5 +168,130 @@ private static bool KeyIsLegalSize(string sk) return keyLength == 16 || keyLength == 24 || keyLength == 32; } private static bool IvIsLegalSize(string iv) => iv.Length == 16; + + /// + /// AES 加密(字节数组版本) + /// + /// 需要加密的数据 + /// 加密key + /// 向量iv + /// 默认CBC + /// 默认PKCS7 + /// 加密后的数据 + /// + public static byte[] Encrypt(byte[] data, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) + { + if (data == null || data.Length == 0) + return Array.Empty(); + if (key == null || key.Length == 0) + throw new ArgumentException("Key cannot be null or empty", nameof(key)); + if (!KeyIsLegalSizeBytes(key)) + throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位"); + if (iv != null && iv.Length != 16) + throw new ArgumentException("不合规的iv,请确认iv为16位"); + + using var symmetricKey = Aes.Create(); + symmetricKey.Mode = cipher; + symmetricKey.Padding = padding; + using var encryptor = symmetricKey.CreateEncryptor(key, iv); + using var memoryStream = new MemoryStream(); + using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write); + + cryptoStream.Write(data, 0, data.Length); + cryptoStream.FlushFinalBlock(); + return memoryStream.ToArray(); + } + + /// + /// AES 解密(字节数组版本) + /// + /// 需要解密的数据 + /// 解密key + /// 向量iv + /// 默认CBC + /// 默认PKCS7 + /// 解密后的数据 + /// + public static byte[] Decrypt(byte[] data, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) + { + if (data == null || data.Length == 0) + return Array.Empty(); + if (key == null || key.Length == 0) + throw new ArgumentException("Key cannot be null or empty", nameof(key)); + if (!KeyIsLegalSizeBytes(key)) + throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位"); + if (iv != null && iv.Length != 16) + throw new ArgumentException("不合规的iv,请确认iv为16位"); + + using var symmetricKey = Aes.Create(); + symmetricKey.Mode = cipher; + symmetricKey.Padding = padding; + using var decryptor = symmetricKey.CreateDecryptor(key, iv); + using var memoryStream = new MemoryStream(data); + using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); + using var resultStream = new MemoryStream(); + cryptoStream.CopyTo(resultStream); + return resultStream.ToArray(); + } + + private static bool KeyIsLegalSizeBytes(byte[] key) + { + int keyLength = key.Length; + return keyLength == 16 || keyLength == 24 || keyLength == 32; + } + + /// + /// 创建加密流 + /// + /// 输出流 + /// 加密key + /// 向量iv + /// 默认CBC + /// 默认PKCS7 + /// 加密流 + public static CryptoStream CreateEncryptingStream(Stream outputStream, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) + { + if (outputStream == null) + throw new ArgumentNullException(nameof(outputStream)); + if (key == null || key.Length == 0) + throw new ArgumentException("Key cannot be null or empty", nameof(key)); + if (!KeyIsLegalSizeBytes(key)) + throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位"); + if (iv != null && iv.Length != 16) + throw new ArgumentException("不合规的iv,请确认iv为16位"); + + var aes = Aes.Create(); + aes.Mode = cipher; + aes.Padding = padding; + var encryptor = aes.CreateEncryptor(key, iv); + return new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write); + } + + /// + /// 创建解密流 + /// + /// 输入流 + /// 解密key + /// 向量iv + /// 默认CBC + /// 默认PKCS7 + /// 解密流 + public static CryptoStream CreateDecryptingStream(Stream inputStream, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) + { + if (inputStream == null) + throw new ArgumentNullException(nameof(inputStream)); + if (key == null || key.Length == 0) + throw new ArgumentException("Key cannot be null or empty", nameof(key)); + if (!KeyIsLegalSizeBytes(key)) + throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位"); + if (iv != null && iv.Length != 16) + throw new ArgumentException("不合规的iv,请确认iv为16位"); + + var aes = Aes.Create(); + aes.Mode = cipher; + aes.Padding = padding; + var decryptor = aes.CreateDecryptor(key, iv); + return new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read); + } } } diff --git a/EasyTool.Core/CodeCategory/DesUtil.cs b/EasyTool.Core/CodeCategory/DesUtil.cs index 94e350f..1fad310 100644 --- a/EasyTool.Core/CodeCategory/DesUtil.cs +++ b/EasyTool.Core/CodeCategory/DesUtil.cs @@ -85,12 +85,13 @@ public static string Encrypt(string str, string sk,string iv, CipherMode cipher if (!IsLegalSize(iv)) throw new ArgumentException("不合规的IV,请确认IV为8位的字符"); encoding ??= Encoding.UTF8; byte[] keyBytes = encoding.GetBytes(sk).ToArray(); + byte[] ivBytes = encoding.GetBytes(iv).ToArray(); byte[] toEncrypt = encoding.GetBytes(str); var des = DES.Create(); des.Mode = cipher; des.Padding = padding; des.Key = keyBytes; - des.IV = keyBytes; + des.IV = ivBytes; ICryptoTransform cTransform = des.CreateEncryptor(); var resultArray = cTransform.TransformFinalBlock(toEncrypt, 0, toEncrypt.Length); @@ -115,12 +116,13 @@ public static string Decrypt(string str, string sk, string iv, CipherMode cipher if (!IsLegalSize(iv)) throw new ArgumentException("不合规的IV,请确认IV为8位的字符"); encoding ??= Encoding.UTF8; byte[] keyBytes = encoding.GetBytes(sk).ToArray(); + byte[] ivBytes = encoding.GetBytes(iv).ToArray(); byte[] toDecrypt = Convert.FromBase64String(str); var des = DES.Create(); des.Mode = cipher; des.Padding = padding; des.Key = keyBytes; - des.IV = keyBytes; + des.IV = ivBytes; ICryptoTransform cTransform = des.CreateDecryptor(); var resultArray = cTransform.TransformFinalBlock(toDecrypt, 0, toDecrypt.Length); return encoding.GetString(resultArray); @@ -134,5 +136,69 @@ private static bool IsLegalSize(string sk) return false; } + /// + /// DES 加密(字节数组版本) + /// + /// 待加密数据 + /// 秘钥 + /// 向量Iv + /// 默认ECB + /// 默认PKCS7 + /// + /// + public static byte[] Encrypt(byte[] data, byte[] keyBytes, byte[]? ivBytes = null, CipherMode cipher = CipherMode.ECB, PaddingMode padding = PaddingMode.PKCS7) + { + if (data == null || data.Length == 0) + return Array.Empty(); + if (keyBytes == null || keyBytes.Length != 8) + throw new ArgumentException("不合规的秘钥,请确认秘钥为8位"); + if (ivBytes != null && ivBytes.Length != 8) + throw new ArgumentException("不合规的IV,请确认IV为8位"); + + var des = DES.Create(); + des.Mode = cipher; + des.Padding = padding; + des.Key = keyBytes; + if (ivBytes != null) + des.IV = ivBytes; + else + des.IV = keyBytes; + + ICryptoTransform cTransform = des.CreateEncryptor(); + return cTransform.TransformFinalBlock(data, 0, data.Length); + } + + /// + /// DES 解密(字节数组版本) + /// + /// 待解密数据 + /// 秘钥 + /// 向量Iv + /// 默认ECB + /// 默认PKCS7 + /// + /// + public static byte[] Decrypt(byte[] data, byte[] keyBytes, byte[]? ivBytes = null, CipherMode cipher = CipherMode.ECB, PaddingMode padding = PaddingMode.PKCS7) + { + if (data == null || data.Length == 0) + return Array.Empty(); + if (keyBytes == null || keyBytes.Length != 8) + throw new ArgumentException("不合规的秘钥,请确认秘钥为8位"); + if (ivBytes != null && ivBytes.Length != 8) + throw new ArgumentException("不合规的IV,请确认IV为8位"); + + var des = DES.Create(); + des.Mode = cipher; + des.Padding = padding; + des.Key = keyBytes; + if (ivBytes != null) + des.IV = ivBytes; + else + des.IV = keyBytes; + + ICryptoTransform cTransform = des.CreateDecryptor(); + return cTransform.TransformFinalBlock(data, 0, data.Length); + } + } } diff --git a/EasyTool.Core/CodeCategory/EncodingUtil.cs b/EasyTool.Core/CodeCategory/EncodingUtil.cs index 67a3042..58d7a78 100644 --- a/EasyTool.Core/CodeCategory/EncodingUtil.cs +++ b/EasyTool.Core/CodeCategory/EncodingUtil.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace EasyTool +namespace EasyTool.CodeCategory { /// /// 编码工具类,提供各种编码格式的转换功能 @@ -40,8 +40,9 @@ public static string Base32Encode(byte[] bytes) int index = 0; for (int i = 0; i < length; i += 5) { - int val = (bytes[i] << 24) + ((i + 1 < length ? bytes[i + 1] : 0) << 16) + - ((i + 2 < length ? bytes[i + 2] : 0) << 8) + ((i + 3 < length ? bytes[i + 3] : 0) << 0); + int val = (bytes[i] << 32) + ((i + 1 < length ? bytes[i + 1] : 0) << 24) + + ((i + 2 < length ? bytes[i + 2] : 0) << 16) + ((i + 3 < length ? bytes[i + 3] : 0) << 8) + + ((i + 4 < length ? bytes[i + 4] : 0) << 0); chars[index++] = BASE32_CHARS[(val >> 35) & 0x1F]; chars[index++] = BASE32_CHARS[(val >> 30) & 0x1F]; chars[index++] = BASE32_CHARS[(val >> 25) & 0x1F]; @@ -158,10 +159,15 @@ public static byte[] Base32Decode(string str) // 解码 Base32 字符 private static int DecodeBase32Char(char c) { + // 支持大小写 if (c >= 'A' && c <= 'Z') { return c - 'A'; } + if (c >= 'a' && c <= 'z') + { + return c - 'a'; + } if (c >= '2' && c <= '7') { return c - '2' + 26; @@ -321,6 +327,21 @@ public static string RotDecrypt(string text, int n) {' ', " "} }; + // Morse 反向电码表,用于解码优化性能 + private static readonly Dictionary MORSE_REVERSE_TABLE; + + static EncodingUtil() + { + MORSE_REVERSE_TABLE = new Dictionary(); + foreach (var kvp in MORSE_TABLE) + { + if (!string.IsNullOrEmpty(kvp.Value)) + { + MORSE_REVERSE_TABLE[kvp.Value] = kvp.Key; + } + } + } + /// /// 将给定的字符串转换为 Morse 电码字符串 /// @@ -357,19 +378,15 @@ public static string MorseDecode(string morseCode) } string[] codes = morseCode.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - List chars = new List(); + StringBuilder result = new StringBuilder(codes.Length); foreach (string code in codes) { - foreach (KeyValuePair kvp in MORSE_TABLE) + if (MORSE_REVERSE_TABLE.TryGetValue(code, out char c)) { - if (kvp.Value == code) - { - chars.Add(kvp.Key); - break; - } + result.Append(c); } } - return new string(chars.ToArray()); + return result.ToString(); } #endregion diff --git a/EasyTool.Core/CodeCategory/HashUtil.cs b/EasyTool.Core/CodeCategory/HashUtil.cs index 5ac7eb3..7202d3a 100644 --- a/EasyTool.Core/CodeCategory/HashUtil.cs +++ b/EasyTool.Core/CodeCategory/HashUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; @@ -14,8 +14,11 @@ public class HashUtil /// /// 要进行hash的字符串 /// 返回hash值 - public static uint AdditiveHash(string str) + public static uint AdditiveHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 0; foreach (char c in str) { @@ -30,8 +33,11 @@ public static uint AdditiveHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint RotatingHash(string str) + public static uint RotatingHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = (uint)str.Length; foreach (char c in str) { @@ -46,8 +52,11 @@ public static uint RotatingHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint OneByOneHash(string str) + public static uint OneByOneHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 0; foreach (char c in str) { @@ -67,8 +76,11 @@ public static uint OneByOneHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint Bernstein(string str) + public static uint Bernstein(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 5381; foreach (char c in str) { @@ -86,8 +98,15 @@ public static uint Bernstein(string str) /// 哈希桶的数量 /// a的取值范围为[1, prime - 1] /// b的取值范围 - public static uint Universal(string str, uint prime, uint num_buckets, uint a, uint b) + public static uint Universal(string? str, uint prime, uint num_buckets, uint a, uint b) { + if (string.IsNullOrEmpty(str)) + return 0; + if (prime == 0) + throw new ArgumentException("Prime must be greater than 0", nameof(prime)); + if (num_buckets == 0) + throw new ArgumentException("Number of buckets must be greater than 0", nameof(num_buckets)); + uint hash = a; foreach (char c in str) { @@ -104,12 +123,18 @@ public static uint Universal(string str, uint prime, uint num_buckets, uint a, u /// 要进行hash的字符串 /// 随机数表 /// 返回hash值 - public static uint Zobrist(string str, uint[] table) + public static uint Zobrist(string? str, uint[]? table) { + if (string.IsNullOrEmpty(str)) + return 0; + if (table == null || table.Length == 0) + throw new ArgumentException("Table cannot be null or empty", nameof(table)); + uint hash = 0; for (int i = 0; i < str.Length; i++) { - hash ^= table[str[i]]; + int index = Math.Min(str[i], table.Length - 1); + hash ^= table[index]; } return hash; @@ -120,8 +145,11 @@ public static uint Zobrist(string str, uint[] table) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint FnvHash(string str) + public static uint FnvHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + const uint fnv_prime = 0x811C9DC5; uint hash = 0; foreach (char c in str) @@ -156,8 +184,13 @@ public static uint IntHash(uint key) /// b的取值范围为[1, 255] /// a的取值范围为[1, b-1] /// 返回hash值 - public static uint RsHash(string str, uint b, uint a) + public static uint RsHash(string? str, uint b, uint a) { + if (string.IsNullOrEmpty(str)) + return 0; + if (b == 0) + throw new ArgumentException("b must be greater than 0", nameof(b)); + uint hash = 0; foreach (char c in str) { @@ -173,8 +206,11 @@ public static uint RsHash(string str, uint b, uint a) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint JsHash(string str) + public static uint JsHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 1315423911; foreach (char c in str) { @@ -189,8 +225,11 @@ public static uint JsHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint PjwHash(string str) + public static uint PjwHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 0; const uint BitsInUnsignedInt = (uint)(sizeof(uint) * 8); const uint ThreeQuarters = (uint)((BitsInUnsignedInt * 3) / 4); @@ -214,8 +253,11 @@ public static uint PjwHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint ElfHash(string str) + public static uint ElfHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 0; uint x = 0; foreach (char c in str) @@ -239,8 +281,13 @@ public static uint ElfHash(string str) /// 要进行hash的字符串 /// 种子值 /// 返回hash值 - public static uint BkdrHash(string str, uint seed) + public static uint BkdrHash(string? str, uint seed) { + if (string.IsNullOrEmpty(str)) + return 0; + if (seed == 0) + throw new ArgumentException("Seed must be greater than 0", nameof(seed)); + uint hash = 0; foreach (char c in str) { @@ -255,8 +302,11 @@ public static uint BkdrHash(string str, uint seed) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint SdbmHash(string str) + public static uint SdbmHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 0; foreach (char c in str) { @@ -271,8 +321,11 @@ public static uint SdbmHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint DjbHash(string str) + public static uint DjbHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 5381; foreach (char c in str) { @@ -287,8 +340,11 @@ public static uint DjbHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint DekHash(string str) + public static uint DekHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = (uint)str.Length; foreach (char c in str) { @@ -303,8 +359,11 @@ public static uint DekHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint ApHash(string str) + public static uint ApHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 0; int i; for (i = 0; i < str.Length; i++) @@ -328,14 +387,17 @@ public static uint ApHash(string str) /// 要进行hash的字符串 /// hash表的长度 /// 返回hash值 - public static uint TianlHash(string str, uint len) + public static uint TianlHash(string? str, uint len) { + if (string.IsNullOrEmpty(str)) + return 0; + if (len == 0) + throw new ArgumentException("Length must be greater than 0", nameof(len)); + uint hash = 0; uint[] w = new uint[64]; uint[] v = new uint[8]; - if (str.Length == 0) return 0; - if (str.Length <= 64) { for (int i = 0; i < str.Length; i++) @@ -403,8 +465,11 @@ public static uint TianlHash(string str, uint len) /// /// 要进行hash的字符串 /// 返回hash值 - public static uint JavaDefaultHash(string str) + public static uint JavaDefaultHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint hash = 0; uint h = hash; foreach (char c in str) @@ -421,8 +486,11 @@ public static uint JavaDefaultHash(string str) /// /// 要进行hash的字符串 /// 返回hash值 - public static ulong MixHash(string str) + public static ulong MixHash(string? str) { + if (string.IsNullOrEmpty(str)) + return 0; + uint seed = 131; // 31 131 1313 13131 131313 etc.. ulong hash1 = 0; ulong hash2 = 0; diff --git a/EasyTool.Core/CodeCategory/HexUtil.cs b/EasyTool.Core/CodeCategory/HexUtil.cs index 102a626..e3f9870 100644 --- a/EasyTool.Core/CodeCategory/HexUtil.cs +++ b/EasyTool.Core/CodeCategory/HexUtil.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Text; -namespace EasyTool +namespace EasyTool.CodeCategory { /// /// 16进制工具类 /// - public class HexUtil + public static class HexUtil { /// /// 将16进制字符串转换为字节数组 @@ -32,16 +32,53 @@ public static byte[] HexToBytes(string hex) /// 将字节数组转换为16进制字符串 /// /// 字节数组 + /// 是否使用小写(默认大写) /// 16进制字符串 - public static string BytesToHex(byte[] bytes) + public static string BytesToHex(byte[] bytes, bool lowercase = false) { - StringBuilder sb = new StringBuilder(); + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + StringBuilder sb = new StringBuilder(bytes.Length * 2); + string format = lowercase ? "x2" : "X2"; foreach (byte b in bytes) - sb.Append(b.ToString("X2")); + sb.Append(b.ToString(format)); return sb.ToString(); } + /// + /// 将16进制字符串转换为字节数组(安全版本,不抛出异常) + /// + /// 16进制字符串 + /// 转换后的字节数组 + /// 是否转换成功 + public static bool TryHexToBytes(string hex, out byte[]? bytes) + { + bytes = null; + if (string.IsNullOrWhiteSpace(hex)) + return false; + + hex = hex.Replace(" ", ""); + if (hex.Length % 2 != 0) + return false; + + try + { + bytes = new byte[hex.Length / 2]; + for (int i = 0; i < hex.Length; i += 2) + { + if (!byte.TryParse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber, null, out bytes[i / 2])) + return false; + } + return true; + } + catch + { + return false; + } + } + /// /// 比较两个16进制字符串是否相等 /// @@ -72,7 +109,25 @@ public static bool HexEquals(string hex1, string hex2) /// 十进制数值 public static int HexToInt(string hex) { - return Convert.ToInt32(hex, 16); + try + { + return Convert.ToInt32(hex, 16); + } + catch (FormatException ex) + { + throw new ArgumentException("Invalid hex string: " + hex, nameof(hex), ex); + } + } + + /// + /// 将16进制字符串转换为十进制数值(安全版本) + /// + /// 16进制字符串 + /// 转换后的数值 + /// 是否转换成功 + public static bool TryHexToInt(string hex, out int result) + { + return int.TryParse(hex, System.Globalization.NumberStyles.HexNumber, null, out result); } /// diff --git a/EasyTool.Core/CollectionsCategory/ArrayExtension.cs b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs index c2ce442..ac3f4a4 100644 --- a/EasyTool.Core/CollectionsCategory/ArrayExtension.cs +++ b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace EasyTool.Extension +namespace EasyTool.CollectionsCategory { /// /// 数组扩展方法 diff --git a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs index 22ba15b..b8620c0 100644 --- a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs +++ b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace EasyTool.Extension +namespace EasyTool.CollectionsCategory { public static class DictionaryExtension { diff --git a/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs b/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs index a27bd80..5729fea 100644 --- a/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs +++ b/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Text; -namespace EasyTool +namespace EasyTool.CollectionsCategory { /// /// 双向链表工具类 /// - public class LinkedListUtil + public static class LinkedListUtil { /// /// 将指定元素添加到双向链表的结尾处。 diff --git a/EasyTool.Core/CollectionsCategory/ListExtension.cs b/EasyTool.Core/CollectionsCategory/ListExtension.cs index 15cd2b4..fa39ee2 100644 --- a/EasyTool.Core/CollectionsCategory/ListExtension.cs +++ b/EasyTool.Core/CollectionsCategory/ListExtension.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace EasyTool.Extension +namespace EasyTool.CollectionsCategory { /// /// List 集合扩展方法 diff --git a/EasyTool.Core/CollectionsCategory/QueueUtil.cs b/EasyTool.Core/CollectionsCategory/QueueUtil.cs index d1f45e9..68e72bf 100644 --- a/EasyTool.Core/CollectionsCategory/QueueUtil.cs +++ b/EasyTool.Core/CollectionsCategory/QueueUtil.cs @@ -3,12 +3,12 @@ using System.Linq; using System.Text; -namespace EasyTool +namespace EasyTool.CollectionsCategory { /// /// 队列工具类 /// - public class QueueUtil + public static class QueueUtil { /// /// 将指定元素添加到队列的末尾。 diff --git a/EasyTool.Core/CollectionsCategory/StackUtil.cs b/EasyTool.Core/CollectionsCategory/StackUtil.cs index 5a0cd9c..c7955bf 100644 --- a/EasyTool.Core/CollectionsCategory/StackUtil.cs +++ b/EasyTool.Core/CollectionsCategory/StackUtil.cs @@ -3,68 +3,13 @@ using System.Linq; using System.Text; -namespace EasyTool +namespace EasyTool.CollectionsCategory { /// - /// 堆栈工具类 + /// 栈工具类 /// - public class StackUtil + public static class StackUtil { - /// - /// 将指定元素推入堆栈的顶部。 - /// [Obsolete("请直接使用 stack.Push(item)")] - /// - /// 堆栈元素类型 - /// 堆栈 - /// 要添加的元素 - [Obsolete("请直接使用 stack.Push(item)", false)] - public static void Push(Stack stack, T item) - { - stack.Push(item); - } - - /// - /// 从堆栈的顶部移除并返回对象。 - /// [Obsolete("请直接使用 stack.Pop()")] - /// - /// 堆栈元素类型 - /// 堆栈 - /// 堆栈顶部的元素 - /// 堆栈为空时引发异常 - [Obsolete("请直接使用 stack.Pop()", false)] - public static T Pop(Stack stack) - { - return stack.Pop(); - } - - /// - /// 返回位于堆栈顶部的对象但不将其移除。 - /// [Obsolete("请直接使用 stack.Peek()")] - /// - /// 堆栈元素类型 - /// 堆栈 - /// 堆栈顶部的元素 - /// 堆栈为空时引发异常 - [Obsolete("请直接使用 stack.Peek()", false)] - public static T Peek(Stack stack) - { - return stack.Peek(); - } - - /// - /// 确定堆栈是否包含指定元素。 - /// [Obsolete("请直接使用 stack.Contains(item)")] - /// - /// 堆栈元素类型 - /// 堆栈 - /// 要查找的元素 - /// 如果堆栈包含指定元素,则为 true;否则为 false。 - [Obsolete("请直接使用 stack.Contains(item)", false)] - public static bool Contains(Stack stack, T item) - { - return stack.Contains(item); - } - /// /// 从堆栈中移除指定元素的第一个匹配项。 /// @@ -86,44 +31,5 @@ public static bool Remove(Stack stack, T item) } return false; } - - /// - /// 将堆栈中的所有元素复制到新数组中。 - /// [Obsolete("请直接使用 stack.ToArray()")] - /// - /// 堆栈元素类型 - /// 堆栈 - /// 包含堆栈中所有元素的新数组 - [Obsolete("请直接使用 stack.ToArray()", false)] - public static T[] ToArray(Stack stack) - { - return stack.ToArray(); - } - - /// - /// 将堆栈中的所有元素复制到新数组中,从指定的索引开始。 - /// [Obsolete("请直接使用 stack.CopyTo(array, arrayIndex)")] - /// - /// 堆栈元素类型 - /// 堆栈 - /// 要复制到的目标数组 - /// 目标数组的起始索引 - [Obsolete("请直接使用 stack.CopyTo(array, arrayIndex)", false)] - public static void CopyTo(Stack stack, T[] array, int arrayIndex) - { - stack.CopyTo(array, arrayIndex); - } - - /// - /// 从堆栈中移除所有元素。 - /// [Obsolete("请直接使用 stack.Clear()")] - /// - /// 堆栈元素类型 - /// 堆栈 - [Obsolete("请直接使用 stack.Clear()", false)] - public static void Clear(Stack stack) - { - stack.Clear(); - } } } diff --git a/EasyTool.Core/ConvertCategory/ByteExtension.cs b/EasyTool.Core/ConvertCategory/ByteExtension.cs index 9a2be43..13d4829 100644 --- a/EasyTool.Core/ConvertCategory/ByteExtension.cs +++ b/EasyTool.Core/ConvertCategory/ByteExtension.cs @@ -3,7 +3,7 @@ using System.Text; using System.Linq; -namespace EasyTool.Extension +namespace EasyTool.ConvertCategory { /// /// Byte 字节扩展方法 diff --git a/EasyTool.Core/ToolCategory/ColorExtension.cs b/EasyTool.Core/ConvertCategory/ColorExtension.cs similarity index 99% rename from EasyTool.Core/ToolCategory/ColorExtension.cs rename to EasyTool.Core/ConvertCategory/ColorExtension.cs index 41f7913..b11f7d7 100644 --- a/EasyTool.Core/ToolCategory/ColorExtension.cs +++ b/EasyTool.Core/ConvertCategory/ColorExtension.cs @@ -1,7 +1,7 @@ using System; using System.Drawing; -namespace EasyTool.Extension +namespace EasyTool.ConvertCategory { /// /// Color 颜色扩展方法 diff --git a/EasyTool.Core/ConvertCategory/ConvertExtension.cs b/EasyTool.Core/ConvertCategory/ConvertExtension.cs index 1402df8..e02a64a 100644 --- a/EasyTool.Core/ConvertCategory/ConvertExtension.cs +++ b/EasyTool.Core/ConvertCategory/ConvertExtension.cs @@ -217,27 +217,6 @@ public static string ToZhCn(this bool b) #region ==字节转换== - /// - /// 转换为16进制 - /// - /// - /// 是否小写 - /// - public static string ToHex(this byte[] bytes, bool lowerCase = true) - { - if (bytes == null) - return string.Empty; - - var result = new StringBuilder(); - var format = lowerCase ? "x2" : "X2"; - for (var i = 0; i < bytes.Length; i++) - { - result.Append(bytes[i].ToString(format)); - } - - return result.ToString(); - } - /// /// 16进制转字节数组 /// @@ -258,6 +237,32 @@ public static string ToHex(this byte[] bytes, bool lowerCase = true) return bytes; } + /// + /// 转换为16进制 + /// + /// + /// 已过时:请使用 替代 + /// 此方法与 ByteExtension.ToHex 存在命名冲突,已标记为过时 + /// + /// + /// 是否小写 + /// + [Obsolete("请使用 ByteExtension.ToHex(byte[], bool) 替代")] + public static string ToHexLegacy(this byte[] bytes, bool lowerCase = true) + { + if (bytes == null) + return string.Empty; + + var result = new StringBuilder(); + var format = lowerCase ? "x2" : "X2"; + for (var i = 0; i < bytes.Length; i++) + { + result.Append(bytes[i].ToString(format)); + } + + return result.ToString(); + } + /// diff --git a/EasyTool.Core/ConvertCategory/NumberExtension.cs b/EasyTool.Core/ConvertCategory/NumberExtension.cs index a9cae99..dcc047a 100644 --- a/EasyTool.Core/ConvertCategory/NumberExtension.cs +++ b/EasyTool.Core/ConvertCategory/NumberExtension.cs @@ -1,6 +1,6 @@ using System; -namespace EasyTool.Extension +namespace EasyTool.ConvertCategory { /// /// 数字类型扩展方法 diff --git a/EasyTool.Core/ToolCategory/PageUtil.cs b/EasyTool.Core/DataCategory/PageUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/PageUtil.cs rename to EasyTool.Core/DataCategory/PageUtil.cs index d1334eb..5a6b25e 100644 --- a/EasyTool.Core/ToolCategory/PageUtil.cs +++ b/EasyTool.Core/DataCategory/PageUtil.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace EasyTool +namespace EasyTool.DataCategory { /// /// 分页工具类,支持多种数据源和多种排序方式的分页 diff --git a/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs b/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs index bad196b..dd88327 100644 --- a/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs +++ b/EasyTool.Core/DateTimeCategory/DateTimeExtension.cs @@ -3,7 +3,7 @@ using System.Globalization; using System.Text; -namespace EasyTool.Extension +namespace EasyTool.DateTimeCategory { /// /// 提供各种日期操作和计算的工具类。 diff --git a/EasyTool.Core/DateTimeCategory/DateTimeUtil.cs b/EasyTool.Core/DateTimeCategory/DateTimeUtil.cs index f31092d..5ad6dcb 100644 --- a/EasyTool.Core/DateTimeCategory/DateTimeUtil.cs +++ b/EasyTool.Core/DateTimeCategory/DateTimeUtil.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Globalization; -namespace EasyTool +namespace EasyTool.DateTimeCategory { /// /// 提供各种日期操作和计算的工具类。 diff --git a/EasyTool.Core/DateTimeCategory/LunarCalendarUtil.cs b/EasyTool.Core/DateTimeCategory/LunarCalendarUtil.cs index 8dc4d14..8dc0c5d 100644 --- a/EasyTool.Core/DateTimeCategory/LunarCalendarUtil.cs +++ b/EasyTool.Core/DateTimeCategory/LunarCalendarUtil.cs @@ -1,11 +1,11 @@ using System; -namespace EasyTool +namespace EasyTool.DateTimeCategory { /// /// 农历日期工具类 /// - public class LunarCalendarUtil + public static class LunarCalendarUtil { #region 基础数据 diff --git a/EasyTool.Core/DateTimeCategory/TimerUtil.cs b/EasyTool.Core/DateTimeCategory/TimerUtil.cs index afd9082..10b945a 100644 --- a/EasyTool.Core/DateTimeCategory/TimerUtil.cs +++ b/EasyTool.Core/DateTimeCategory/TimerUtil.cs @@ -1,12 +1,12 @@ using System; using System.Diagnostics; -namespace EasyTool +namespace EasyTool.DateTimeCategory { /// /// 计时器工具类,提供各种计时和时间间隔计算的方法。 /// - public class TimerUtil + public static class TimerUtil { /// /// 记录程序启动时间。 diff --git a/EasyTool.Core/EmojiCategory/EmojiUtil.cs b/EasyTool.Core/EmojiCategory/EmojiUtil.cs index 85500eb..615ba06 100644 --- a/EasyTool.Core/EmojiCategory/EmojiUtil.cs +++ b/EasyTool.Core/EmojiCategory/EmojiUtil.cs @@ -3,9 +3,9 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool +namespace EasyTool.EmojiCategory { - public class EmojiUtil + public static class EmojiUtil { // Unicode 区间:Emoji 表情符号的 Unicode 区间 private const string EmojiRanges = "[\u1F600-\u1F64F\u1F910-\u1F96B\u1F980-\u1F9E0]"; diff --git a/EasyTool.Core/IOCategory/FileSystemExtension.cs b/EasyTool.Core/IOCategory/FileSystemExtension.cs index 5a5c5ca..4fe44fa 100644 --- a/EasyTool.Core/IOCategory/FileSystemExtension.cs +++ b/EasyTool.Core/IOCategory/FileSystemExtension.cs @@ -1,8 +1,9 @@ using System; using System.IO; using System.Linq; +using EasyTool.ConvertCategory; -namespace EasyTool.Extension +namespace EasyTool.IOCategory { /// /// 文件系统扩展方法 diff --git a/EasyTool.Core/IOCategory/FileTypeExtension.cs b/EasyTool.Core/IOCategory/FileTypeExtension.cs index f8a5d0b..4220f7a 100644 --- a/EasyTool.Core/IOCategory/FileTypeExtension.cs +++ b/EasyTool.Core/IOCategory/FileTypeExtension.cs @@ -3,7 +3,7 @@ using System.IO; using System.Text; -namespace EasyTool.Extension +namespace EasyTool.IOCategory { /// /// 文件类型扩展方法 diff --git a/EasyTool.Core/IOCategory/FileUtil.cs b/EasyTool.Core/IOCategory/FileUtil.cs index 9104d26..eb8133e 100644 --- a/EasyTool.Core/IOCategory/FileUtil.cs +++ b/EasyTool.Core/IOCategory/FileUtil.cs @@ -6,54 +6,19 @@ using System.Net.Http; using System.Text; using System.Web; -using EasyTool.Extension; +using EasyTool.IOCategory; -namespace EasyTool +namespace EasyTool.IOCategory { /// /// 文件操作类 /// - public class FileUtil + public static class FileUtil { /// /// 判断当前操作系统是否为 Windows /// - public static bool IsWindows() - { - // 判断当前操作系统的 PlatformID 是否为 Win32S、Win32Windows、Win32NT 或 WinCE - return Environment.OSVersion.Platform == PlatformID.Win32S - || Environment.OSVersion.Platform == PlatformID.Win32Windows - || Environment.OSVersion.Platform == PlatformID.Win32NT - || Environment.OSVersion.Platform == PlatformID.WinCE; - } - - /// - /// 判断当前操作系统是否为 Unix - /// - public static bool IsUnix() - { - // 判断当前操作系统的 PlatformID 是否为 Unix - return Environment.OSVersion.Platform == PlatformID.Unix; - } - - /// - /// 判断当前操作系统是否为 Xbox - /// - public static bool IsXbox() - { - // 判断当前操作系统的 PlatformID 是否为 Xbox - return Environment.OSVersion.Platform == PlatformID.Xbox; - } - - /// - /// 判断当前操作系统是否为 macOS - /// - public static bool IsMacOSX() - { - // 判断当前操作系统的 PlatformID 是否为 macOSX - return Environment.OSVersion.Platform == PlatformID.MacOSX; - } /// /// 判断文件或目录是否为空 diff --git a/EasyTool.Core/IOCategory/StreamExtension.cs b/EasyTool.Core/IOCategory/StreamExtension.cs index d3c93bd..bad7a17 100644 --- a/EasyTool.Core/IOCategory/StreamExtension.cs +++ b/EasyTool.Core/IOCategory/StreamExtension.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; -namespace EasyTool.Extension +namespace EasyTool.IOCategory { /// /// Stream 扩展方法 diff --git a/EasyTool.Core/IOCategory/Tailer.cs b/EasyTool.Core/IOCategory/Tailer.cs index 1a6d6f8..2fd0380 100644 --- a/EasyTool.Core/IOCategory/Tailer.cs +++ b/EasyTool.Core/IOCategory/Tailer.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading; -namespace EasyTool +namespace EasyTool.IOCategory { /// /// 文件跟随工具类 diff --git a/EasyTool.Core/IOCategory/WatchMonitor.cs b/EasyTool.Core/IOCategory/WatchMonitor.cs index 226e44c..6be74b7 100644 --- a/EasyTool.Core/IOCategory/WatchMonitor.cs +++ b/EasyTool.Core/IOCategory/WatchMonitor.cs @@ -3,7 +3,7 @@ using System.IO; using System.Text; -namespace EasyTool +namespace EasyTool.IOCategory { /// /// 文件监听工具类 diff --git a/EasyTool.Core/ToolCategory/ZipUtil.cs b/EasyTool.Core/IOCategory/ZipUtil.cs similarity index 98% rename from EasyTool.Core/ToolCategory/ZipUtil.cs rename to EasyTool.Core/IOCategory/ZipUtil.cs index 4d3de35..ceb894d 100644 --- a/EasyTool.Core/ToolCategory/ZipUtil.cs +++ b/EasyTool.Core/IOCategory/ZipUtil.cs @@ -4,12 +4,12 @@ using System.IO; using System.Text; -namespace EasyTool +namespace EasyTool.IOCategory { /// /// 压缩工具 /// - public class ZipUtil + public static class ZipUtil { /// /// 压缩文件或目录 diff --git a/EasyTool.Core/MathCategory/MathUtil.cs b/EasyTool.Core/MathCategory/MathUtil.cs index ff4f064..6887204 100644 --- a/EasyTool.Core/MathCategory/MathUtil.cs +++ b/EasyTool.Core/MathCategory/MathUtil.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace EasyTool +namespace EasyTool.MathCategory { /// /// 数学工具类,提供数字计算和数学运算方法 diff --git a/EasyTool.Core/MathCategory/PredictUtil.cs b/EasyTool.Core/MathCategory/PredictUtil.cs index ebff051..564a42f 100644 --- a/EasyTool.Core/MathCategory/PredictUtil.cs +++ b/EasyTool.Core/MathCategory/PredictUtil.cs @@ -3,12 +3,12 @@ using System.Linq; using System.Text; -namespace EasyTool +namespace EasyTool.MathCategory { /// /// 预测数据类 /// - public class PredictUtil + public static class PredictUtil { /// /// 线性回归预测 diff --git a/EasyTool.Core/MathCategory/RandomUtil.cs b/EasyTool.Core/MathCategory/RandomUtil.cs index c8ea967..5bc77d7 100644 --- a/EasyTool.Core/MathCategory/RandomUtil.cs +++ b/EasyTool.Core/MathCategory/RandomUtil.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Text; -namespace EasyTool +namespace EasyTool.MathCategory { - public class RandomUtil + public static class RandomUtil { private static readonly Random random = new Random(); diff --git a/EasyTool.Core/NetCategory/HttpClientExtension.cs b/EasyTool.Core/NetCategory/HttpClientExtension.cs index 2c4654e..59ca88b 100644 --- a/EasyTool.Core/NetCategory/HttpClientExtension.cs +++ b/EasyTool.Core/NetCategory/HttpClientExtension.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; -namespace EasyTool.Extension +namespace EasyTool.NetCategory { /// /// 扩展HttpClient中一些缺少的请求方式 diff --git a/EasyTool.Core/ToolCategory/IpUtil.cs b/EasyTool.Core/NetCategory/IpUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/IpUtil.cs rename to EasyTool.Core/NetCategory/IpUtil.cs index 577185e..34adbe8 100644 --- a/EasyTool.Core/ToolCategory/IpUtil.cs +++ b/EasyTool.Core/NetCategory/IpUtil.cs @@ -2,12 +2,12 @@ using System.Net; using System.Text.RegularExpressions; -namespace EasyTool +namespace EasyTool.NetCategory { /// /// IP地址工具类 /// - public class IpUtil + public static class IpUtil { /// /// 判断是否是ipv4格式 diff --git a/EasyTool.Core/NetCategory/URLUtil.cs b/EasyTool.Core/NetCategory/URLUtil.cs index d281985..3227671 100644 --- a/EasyTool.Core/NetCategory/URLUtil.cs +++ b/EasyTool.Core/NetCategory/URLUtil.cs @@ -4,12 +4,12 @@ using System.Text; using System.Web; -namespace EasyTool +namespace EasyTool.NetCategory { /// /// URL工具类 /// - public class URLUtil + public static class URLUtil { /// /// 解析URL并返回其组成部分。 diff --git a/EasyTool.Core/ToolCategory/ReflectUtil.cs b/EasyTool.Core/ReflectCategory/ReflectUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/ReflectUtil.cs rename to EasyTool.Core/ReflectCategory/ReflectUtil.cs index f22b1dd..9848cbe 100644 --- a/EasyTool.Core/ToolCategory/ReflectUtil.cs +++ b/EasyTool.Core/ReflectCategory/ReflectUtil.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Reflection; -namespace EasyTool +namespace EasyTool.ReflectCategory { /// /// 反射工具类,提供类型、属性、字段、方法的反射操作 diff --git a/EasyTool.Core/ToolCategory/DesensitizedUtil.cs b/EasyTool.Core/SecurityCategory/DesensitizedUtil.cs similarity index 98% rename from EasyTool.Core/ToolCategory/DesensitizedUtil.cs rename to EasyTool.Core/SecurityCategory/DesensitizedUtil.cs index 62d07b3..3873756 100644 --- a/EasyTool.Core/ToolCategory/DesensitizedUtil.cs +++ b/EasyTool.Core/SecurityCategory/DesensitizedUtil.cs @@ -3,12 +3,12 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool +namespace EasyTool.SecurityCategory { /// /// 信息脱敏工具类 /// - public class DesensitizedUtil + public static class DesensitizedUtil { private static readonly Regex IdcardRegex = new Regex(@"^\d{15}(\d{2}[0-9xX])?$"); private static readonly Regex MobileRegex = new Regex(@"^(13\d|14[5-9]|15[^4\D]|16\d|17[0-8]|18\d|19[0-3,5-9])\d{8}$"); diff --git a/EasyTool.Core/Standardization/Option.cs b/EasyTool.Core/Standardization/Option.cs index fc8f89d..f15b2cc 100644 --- a/EasyTool.Core/Standardization/Option.cs +++ b/EasyTool.Core/Standardization/Option.cs @@ -6,7 +6,7 @@ using System.Reflection; -namespace EasyTool +namespace EasyTool.Standardization { #if NET6_0_OR_GREATER diff --git a/EasyTool.Core/Standardization/QueryPage.cs b/EasyTool.Core/Standardization/QueryPage.cs index 192b954..26e7566 100644 --- a/EasyTool.Core/Standardization/QueryPage.cs +++ b/EasyTool.Core/Standardization/QueryPage.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace EasyTool +namespace EasyTool.Standardization { /* * 标准化查询参数,减少前后端对接工作 diff --git a/EasyTool.Core/SystemCategory/EnvUtil.cs b/EasyTool.Core/SystemCategory/EnvUtil.cs new file mode 100644 index 0000000..502ff3c --- /dev/null +++ b/EasyTool.Core/SystemCategory/EnvUtil.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Net; +using System.Runtime.InteropServices; +using System.Text; + +namespace EasyTool.SystemCategory +{ + /// + /// 环境工具 + /// + public static class EnvUtil + { + #region 系统信息 + + /// + /// 获取系统信息 + /// + /// 系统信息字符串 + public static string GetSystemInfo() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("操作系统版本:" + Environment.OSVersion.ToString()); + sb.AppendLine("系统位数:" + (Environment.Is64BitOperatingSystem ? "64 位" : "32 位")); + sb.AppendLine("系统目录:" + Environment.SystemDirectory); + sb.AppendLine("处理器数量:" + Environment.ProcessorCount); + sb.AppendLine("计算机名:" + Environment.MachineName); + sb.AppendLine("用户名:" + Environment.UserName); + sb.AppendLine("用户域名:" + Environment.UserDomainName); + sb.AppendLine("当前目录:" + Environment.CurrentDirectory); + sb.AppendLine("CLR版本:" + Environment.Version.ToString()); + return sb.ToString(); + } + + /// + /// 判断当前系统是否为Windows操作系统 + /// + /// 当前系统是否为Windows操作系统 + public static bool IsWindows() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } + + /// + /// 判断当前系统是否为Linux操作系统 + /// + /// 当前系统是否为Linux操作系统 + public static bool IsLinux() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + } + + /// + /// 判断当前系统是否为macOS操作系统 + /// + /// 当前系统是否为macOS操作系统 + public static bool IsMacOS() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + } + + #endregion + + #region 网络时间 + + /* + /// + /// 获取网络时间 + /// + /// 网络时间 + public static DateTime GetNetworkTime() + { + const string ntpServer = "time.windows.com"; + byte[] ntpData = new byte[48]; + ntpData[0] = 0x1B; + IPAddress[] addresses = Dns.GetHostEntry(ntpServer).AddressList; + IPEndPoint ipEndPoint = new IPEndPoint(addresses[0], 123); + using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) + { + socket.Connect(ipEndPoint); + socket.Send(ntpData); + socket.Receive(ntpData); + } + const byte offsetTransmitTime = 40; + uint intPart = BitConverter.ToUInt32(ntpData, offsetTransmitTime); + uint fractPart = BitConverter.ToUInt32(ntpData, offsetTransmitTime + 4); + ulong milliseconds = (ulong)(intPart * 1000) + ((ulong)fractPart * 1000) / 0x100000000L); + return new DateTime(1900, 1, 1, 0, 0, DateTimeKind.Utc).AddMilliseconds((long)milliseconds).ToLocalTime(); + } + */ + + #endregion + } +} diff --git a/EasyTool.Core/ToolCategory/SystemUtil.cs b/EasyTool.Core/SystemCategory/SystemUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/SystemUtil.cs rename to EasyTool.Core/SystemCategory/SystemUtil.cs index 154b48a..e81649e 100644 --- a/EasyTool.Core/ToolCategory/SystemUtil.cs +++ b/EasyTool.Core/SystemCategory/SystemUtil.cs @@ -12,7 +12,7 @@ using System.Text; using System.Threading.Tasks; -namespace EasyTool +namespace EasyTool.SystemCategory { /// /// 系统工具类,提供系统、进程、内存、网络等相关功能 diff --git a/EasyTool.Core/TextCategory/RegexUtil.cs b/EasyTool.Core/TextCategory/RegexUtil.cs index 2ce88d2..c75e228 100644 --- a/EasyTool.Core/TextCategory/RegexUtil.cs +++ b/EasyTool.Core/TextCategory/RegexUtil.cs @@ -4,12 +4,12 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool +namespace EasyTool.TextCategory { /// /// 正则工具 /// - public class RegexUtil + public static class RegexUtil { /// /// 验证字符串是否与指定的正则表达式匹配,并返回匹配结果 diff --git a/EasyTool.Core/TextCategory/StrSplitter.cs b/EasyTool.Core/TextCategory/StrSplitter.cs index cc2c6e2..fb85ebd 100644 --- a/EasyTool.Core/TextCategory/StrSplitter.cs +++ b/EasyTool.Core/TextCategory/StrSplitter.cs @@ -3,9 +3,9 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool +namespace EasyTool.TextCategory { - public class StrSplitter + public static class StrSplitter { /// /// 使用指定的分隔符将输入字符串分割成字符串数组。 diff --git a/EasyTool.Core/ToolCategory/StrUtil.cs b/EasyTool.Core/TextCategory/StrUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/StrUtil.cs rename to EasyTool.Core/TextCategory/StrUtil.cs index 3e835f7..776f920 100644 --- a/EasyTool.Core/ToolCategory/StrUtil.cs +++ b/EasyTool.Core/TextCategory/StrUtil.cs @@ -3,12 +3,12 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool +namespace EasyTool.TextCategory { /// /// 字符串处理工具类 /// - public class StrUtil + public static class StrUtil { /// /// 移除字符串中的所有空格 diff --git a/EasyTool.Core/ToolCategory/XmlUtil.cs b/EasyTool.Core/TextCategory/XmlUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/XmlUtil.cs rename to EasyTool.Core/TextCategory/XmlUtil.cs index 92a081d..c50ed04 100644 --- a/EasyTool.Core/ToolCategory/XmlUtil.cs +++ b/EasyTool.Core/TextCategory/XmlUtil.cs @@ -3,12 +3,12 @@ using System.Text; using System.Xml; -namespace EasyTool +namespace EasyTool.TextCategory { /// /// XML工具类 /// - public class XmlUtil + public static class XmlUtil { /// /// 解析XML字符串。 diff --git a/EasyTool.Core/ToolCategory/CreditCodeUtil.cs b/EasyTool.Core/ToolCategory/CreditCodeUtil.cs index 25b9e44..21d8167 100644 --- a/EasyTool.Core/ToolCategory/CreditCodeUtil.cs +++ b/EasyTool.Core/ToolCategory/CreditCodeUtil.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Text; -namespace EasyTool +namespace EasyTool.ToolCategory { /// /// 社会信用代码工具 /// - public class CreditCodeUtil + public static class CreditCodeUtil { private const string BaseCode = "0123456789ABCDEFGHJKLMNPQRTUWXY"; // 社会信用代码中的基础字符集 private const int Modulo = 31; // 校验码计算中的模数 @@ -68,7 +68,7 @@ public static string GenerateRandomCreditCode() { string orgCode = "911101"; // 默认的组织机构代码 string entType = "00"; // 默认的企业类型 - string regNum = RandomUtil.RandomNumberString(10); // 生成随机的注册号 + string regNum = EasyTool.MathCategory.RandomUtil.RandomNumberString(10); // 生成随机的注册号 string code = orgCode + entType + regNum; // 计算出校验码并添加到社会信用代码中 diff --git a/EasyTool.Core/ToolCategory/DelegateExtension.cs b/EasyTool.Core/ToolCategory/DelegateExtension.cs index d90708b..5585100 100644 --- a/EasyTool.Core/ToolCategory/DelegateExtension.cs +++ b/EasyTool.Core/ToolCategory/DelegateExtension.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// Delegate 委托扩展方法 diff --git a/EasyTool.Core/ToolCategory/EnumExtension.cs b/EasyTool.Core/ToolCategory/EnumExtension.cs index 6605335..5a1a3a0 100644 --- a/EasyTool.Core/ToolCategory/EnumExtension.cs +++ b/EasyTool.Core/ToolCategory/EnumExtension.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Linq; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// Enum 枚举扩展方法 diff --git a/EasyTool.Core/ToolCategory/EnvUtil.cs b/EasyTool.Core/ToolCategory/EnvUtil.cs deleted file mode 100644 index bb67821..0000000 --- a/EasyTool.Core/ToolCategory/EnvUtil.cs +++ /dev/null @@ -1,309 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net.Sockets; -using System.Net; -using System.Runtime.InteropServices; -using System.Text; - -namespace EasyTool -{ - /// - /// 环境工具 - /// - public class EnvUtil - { - /// - /// 获取系统信息 - /// - /// 系统信息字符串 - public static string GetSystemInfo() - { - StringBuilder sb = new StringBuilder(); - sb.AppendLine("操作系统版本:" + Environment.OSVersion.ToString()); - sb.AppendLine("系统位数:" + (Environment.Is64BitOperatingSystem ? "64 位" : "32 位")); - sb.AppendLine("系统目录:" + Environment.SystemDirectory); - sb.AppendLine("处理器数量:" + Environment.ProcessorCount); - sb.AppendLine("计算机名:" + Environment.MachineName); - sb.AppendLine("用户名:" + Environment.UserName); - sb.AppendLine("用户域名:" + Environment.UserDomainName); - sb.AppendLine("当前目录:" + Environment.CurrentDirectory); - sb.AppendLine("CLR版本:" + Environment.Version.ToString()); - return sb.ToString(); - } - - /// - /// 获取环境变量值 - /// [Obsolete("请直接使用 Environment.GetEnvironmentVariable(name)")] - /// - /// 环境变量名称 - /// 环境变量值 - [Obsolete("请直接使用 Environment.GetEnvironmentVariable(name)", false)] - public static string GetEnvironmentVariable(string name) - { - return Environment.GetEnvironmentVariable(name); - } - - /// - /// 设置环境变量值 - /// [Obsolete("请直接使用 Environment.SetEnvironmentVariable(name, value)")] - /// - /// 环境变量名称 - /// 环境变量值 - [Obsolete("请直接使用 Environment.SetEnvironmentVariable(name, value)", false)] - public static void SetEnvironmentVariable(string name, string value) - { - Environment.SetEnvironmentVariable(name, value); - } - - /// - /// 获取环境变量列表 - /// - /// 环境变量列表 - public static IDictionary GetEnvironmentVariables() - { - IDictionary variables = new Dictionary(); - foreach (DictionaryEntry de in Environment.GetEnvironmentVariables()) - { - variables.Add(de.Key.ToString(), de.Value.ToString()); - } - return variables; - } - - /// - /// 获取当前目录下的文件列表 - /// - /// 当前目录下的文件列表 - public static List GetFilesInCurrentDirectory() - { - List files = new List(); - foreach (string file in Directory.GetFiles(Environment.CurrentDirectory)) - { - files.Add(file); - } - return files; - } - - /// - /// 获取指定目录下的文件列表 - /// - /// 指定目录路径 - /// 指定目录下的文件列表 - public static List GetFilesInDirectory(string path) - { - List files = new List(); - foreach (string file in Directory.GetFiles(path)) - { - files.Add(file); - } - return files; - } - - /// - /// 获取当前目录下的目录列表 - /// - /// 当前目录下的目录列表 - public static List GetDirectoriesInCurrentDirectory() - { - List directories = new List(); - foreach (string directory in Directory.GetDirectories(Environment.CurrentDirectory)) - { - directories.Add(directory); - } - return directories; - } - - /// - /// 获取指定目录下的目录列表 - /// - /// 指定目录路径 - /// 指定目录下的目录列表 - public static List GetDirectoriesInDirectory(string path) - { - List directories = new List(); - foreach (string directory in Directory.GetDirectories(path)) - { - directories.Add(directory); - } - return directories; - } - - /// - /// 创建文件 - /// [Obsolete("请直接使用 File.Create(path)")] - /// - /// 文件路径 - [Obsolete("请直接使用 File.Create(path)", false)] - public static void CreateFile(string path) - { - File.Create(path); - } - - /// - /// 删除文件 - /// [Obsolete("请直接使用 File.Delete(path)")] - /// - /// 文件路径 - [Obsolete("请直接使用 File.Delete(path)", false)] - public static void DeleteFile(string path) - { - File.Delete(path); - } - - /// - /// 创建目录 - /// [Obsolete("请直接使用 Directory.CreateDirectory(path)")] - /// - /// 目录路径 - [Obsolete("请直接使用 Directory.CreateDirectory(path)", false)] - public static void CreateDirectory(string path) - { - Directory.CreateDirectory(path); - } - - /// - /// 删除目录 - /// [Obsolete("请直接使用 Directory.Delete(path, true)")] - /// - /// 目录路径 - [Obsolete("请直接使用 Directory.Delete(path, true)", false)] - public static void DeleteDirectory(string path) - { - Directory.Delete(path, true); - } - - /// - /// 检查目录是否存在 - /// [Obsolete("请直接使用 Directory.Exists(path)")] - /// - /// 目录路径 - /// 目录是否存在 - [Obsolete("请直接使用 Directory.Exists(path)", false)] - public static bool DirectoryExists(string path) - { - return Directory.Exists(path); - } - - /// - /// 检查文件是否存在 - /// [Obsolete("请直接使用 File.Exists(path)")] - /// - /// 文件路径 - /// 文件是否存在 - [Obsolete("请直接使用 File.Exists(path)", false)] - public static bool FileExists(string path) - { - return File.Exists(path); - } - - /// - /// 获取文件大小 - /// - /// 文件路径 - /// 文件大小(字节) - public static long GetFileSize(string path) - { - FileInfo fileInfo = new FileInfo(path); - return fileInfo.Length; - } - - /// - /// 获取文件的创建时间 - /// - /// 文件路径 - /// 文件的创建时间 - public static DateTime GetFileCreationTime(string path) - { - FileInfo fileInfo = new FileInfo(path); - return fileInfo.CreationTime; - } - - /// - /// 获取文件的修改时间 - /// - /// 文件路径 - /// 文件的修改时间 - public static DateTime GetFileLastWriteTime(string path) - { - FileInfo fileInfo = new FileInfo(path); - return fileInfo.LastWriteTime; - } - - /// - /// 复制文件 - /// [Obsolete("请直接使用 File.Copy(sourcePath, destinationPath, overwrite)")] - /// - /// 源文件路径 - /// 目标文件路径 - /// 是否覆盖已有文件 - [Obsolete("请直接使用 File.Copy(sourcePath, destinationPath, overwrite)", false)] - public static void CopyFile(string sourcePath, string destinationPath, bool overwrite) - { - File.Copy(sourcePath, destinationPath, overwrite); - } - - /// - /// 移动文件 - /// [Obsolete("请直接使用 File.Move(sourcePath, destinationPath)")] - /// - /// 源文件路径 - /// 目标文件路径 - [Obsolete("请直接使用 File.Move(sourcePath, destinationPath)", false)] - public static void MoveFile(string sourcePath, string destinationPath) - { - File.Move(sourcePath, destinationPath); - } - - /// - /// 获取网络时间 - /// - /// 网络时间 - public static DateTime GetNetworkTime() - { - const string ntpServer = "time.windows.com"; - byte[] ntpData = new byte[48]; - ntpData[0] = 0x1B; - IPAddress[] addresses = Dns.GetHostEntry(ntpServer).AddressList; - IPEndPoint ipEndPoint = new IPEndPoint(addresses[0], 123); - using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) - { - socket.Connect(ipEndPoint); - socket.Send(ntpData); - socket.Receive(ntpData); - } - const byte offsetTransmitTime = 40; - ulong intPart = BitConverter.ToUInt32(ntpData, offsetTransmitTime); - ulong fractPart = BitConverter.ToUInt32(ntpData, offsetTransmitTime + 4); - ulong milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L); - return new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milliseconds).ToLocalTime(); - } - - /// - /// 判断当前系统是否为Windows操作系统 - /// - /// 当前系统是否为Windows操作系统 - public static bool IsWindows() - { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - } - - /// - /// 判断当前系统是否为Linux操作系统 - /// - /// 当前系统是否为Linux操作系统 - public static bool IsLinux() - { - return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - } - - /// - /// 判断当前系统是否为macOS操作系统 - /// - /// 当前系统是否为macOS操作系统 - public static bool IsMacOS() - { - return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); - } - } -} diff --git a/EasyTool.Core/ToolCategory/ExceptionExtension.cs b/EasyTool.Core/ToolCategory/ExceptionExtension.cs index 91824bc..325b319 100644 --- a/EasyTool.Core/ToolCategory/ExceptionExtension.cs +++ b/EasyTool.Core/ToolCategory/ExceptionExtension.cs @@ -3,7 +3,7 @@ using System.Text; using System.Linq; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// Exception 异常扩展方法 diff --git a/EasyTool.Core/ToolCategory/GuidExtension.cs b/EasyTool.Core/ToolCategory/GuidExtension.cs index 39f97fd..656fd54 100644 --- a/EasyTool.Core/ToolCategory/GuidExtension.cs +++ b/EasyTool.Core/ToolCategory/GuidExtension.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// Guid 扩展方法 diff --git a/EasyTool.Core/ToolCategory/IdUtil.cs b/EasyTool.Core/ToolCategory/IdUtil.cs index 64edf2a..6470694 100644 --- a/EasyTool.Core/ToolCategory/IdUtil.cs +++ b/EasyTool.Core/ToolCategory/IdUtil.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading; -namespace EasyTool +namespace EasyTool.ToolCategory { /// /// uuid生成风格 @@ -27,7 +27,7 @@ public enum UUIDStyle /// /// 唯一ID工具 /// - public class IdUtil + public static class IdUtil { private static readonly DateTime epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static int objectIdCounter = 0; diff --git a/EasyTool.Core/ToolCategory/ObjectExtension.cs b/EasyTool.Core/ToolCategory/ObjectExtension.cs index 8513661..a22e623 100644 --- a/EasyTool.Core/ToolCategory/ObjectExtension.cs +++ b/EasyTool.Core/ToolCategory/ObjectExtension.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using System.Xml.Serialization; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// Object 对象扩展方法 diff --git a/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs b/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs index 5bf41fb..0e78d29 100644 --- a/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs +++ b/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Reflection; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// PropertyInfo 扩展方法 diff --git a/EasyTool.Core/ToolCategory/SimpleMapExtension.cs b/EasyTool.Core/ToolCategory/SimpleMapExtension.cs index 2c5572d..1c6796d 100644 --- a/EasyTool.Core/ToolCategory/SimpleMapExtension.cs +++ b/EasyTool.Core/ToolCategory/SimpleMapExtension.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; @@ -7,7 +7,8 @@ using System.Text; using System.Text.Json; -namespace EasyTool; +namespace EasyTool.ToolCategory +{ /// /// 简单实体转化拓展类 /// @@ -92,7 +93,6 @@ private static Delegate BuildSimpleMapDelegate(Type srcType, Type destType) } } } - if (expr == null) continue; assignments.Add(Expression.Bind(destinationProp, expr)); } @@ -105,6 +105,7 @@ private static Delegate BuildSimpleMapDelegate(Type srcType, Type destType) return Expression.Lambda(bodyExpr, srcExpr).Compile(); } + /// /// 简单实体转化 /// 目标泛型需要默认构造函数 @@ -120,8 +121,9 @@ private static Delegate BuildSimpleMapDelegate(Type srcType, Type destType) return mapper(source); } + /// - /// 集合实体转化,需要目标泛型具有默认构造函数 + /// 融合实体转化,需要目标泛型具有默认构造函数 /// /// 源泛型 /// 目标泛型 @@ -131,6 +133,7 @@ private static Delegate BuildSimpleMapDelegate(Type srcType, Type destType) { if (!sources.Any()) return Enumerable.Empty(); + var mapper = (Func)mapDelegateCache.GetOrAdd(new(typeof(TSource), typeof(TDestination)), static key => BuildSimpleMapDelegate(key.SrcType, key.DestType)); List result = new(); @@ -141,3 +144,4 @@ private static Delegate BuildSimpleMapDelegate(Type srcType, Type destType) return result; } } +} diff --git a/EasyTool.Core/ToolCategory/StrExtension.cs b/EasyTool.Core/ToolCategory/StrExtension.cs index a5fd201..06dd0b1 100644 --- a/EasyTool.Core/ToolCategory/StrExtension.cs +++ b/EasyTool.Core/ToolCategory/StrExtension.cs @@ -3,7 +3,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// 字符串扩展方法 diff --git a/EasyTool.Core/ToolCategory/StringBuilderExtension.cs b/EasyTool.Core/ToolCategory/StringBuilderExtension.cs index bbeeadc..8de02ad 100644 --- a/EasyTool.Core/ToolCategory/StringBuilderExtension.cs +++ b/EasyTool.Core/ToolCategory/StringBuilderExtension.cs @@ -2,7 +2,7 @@ using System.IO; using System.Text; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// StringBuilder 扩展方法 diff --git a/EasyTool.Core/ToolCategory/StringComparisonExtension.cs b/EasyTool.Core/ToolCategory/StringComparisonExtension.cs index b81c809..8274e77 100644 --- a/EasyTool.Core/ToolCategory/StringComparisonExtension.cs +++ b/EasyTool.Core/ToolCategory/StringComparisonExtension.cs @@ -1,7 +1,7 @@ using System; using System.Globalization; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// String 字符串比较扩展方法 diff --git a/EasyTool.Core/ToolCategory/TaskExtension.cs b/EasyTool.Core/ToolCategory/TaskExtension.cs index c3e0312..1515de6 100644 --- a/EasyTool.Core/ToolCategory/TaskExtension.cs +++ b/EasyTool.Core/ToolCategory/TaskExtension.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// Task 异步任务扩展方法 diff --git a/EasyTool.Core/ToolCategory/TypeExtension.cs b/EasyTool.Core/ToolCategory/TypeExtension.cs index 547511b..5e1edff 100644 --- a/EasyTool.Core/ToolCategory/TypeExtension.cs +++ b/EasyTool.Core/ToolCategory/TypeExtension.cs @@ -5,7 +5,7 @@ using System.Reflection; using System.Runtime.CompilerServices; -namespace EasyTool.Extension +namespace EasyTool.ToolCategory { /// /// Type 类型扩展方法 diff --git a/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs b/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs index df44f68..688b659 100644 --- a/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs +++ b/EasyTool.CoreTests/CloneCategory/CloneExtensionTests.cs @@ -1,4 +1,4 @@ -using EasyTool.Extension; +using EasyTool.ToolCategory; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace EasyTool.Tests diff --git a/EasyTool.CoreTests/MathCategory/MathUtilTests.cs b/EasyTool.CoreTests/MathCategory/MathUtilTests.cs index 9a5598b..f56a81d 100644 --- a/EasyTool.CoreTests/MathCategory/MathUtilTests.cs +++ b/EasyTool.CoreTests/MathCategory/MathUtilTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool; +using EasyTool.MathCategory; using System; using System.Collections.Generic; using System.Text; diff --git a/EasyTool.CoreTests/Standardization/OptionTests.cs b/EasyTool.CoreTests/Standardization/OptionTests.cs index 9b79407..44237da 100644 --- a/EasyTool.CoreTests/Standardization/OptionTests.cs +++ b/EasyTool.CoreTests/Standardization/OptionTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool; +using EasyTool.Standardization; using System; using System.Collections.Generic; using System.Linq; diff --git a/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs b/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs index 7e796cf..ca2635a 100644 --- a/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs +++ b/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool; +using EasyTool.ToolCategory; using System; using System.Collections.Generic; using System.Linq; diff --git a/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs b/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs index f54d0a5..5714715 100644 --- a/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs +++ b/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs @@ -1,6 +1,6 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool; +using EasyTool.NetCategory; namespace EasyTool.Tests { diff --git a/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs b/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs index 6349bed..175b94d 100644 --- a/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs +++ b/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool; +using EasyTool.ToolCategory; using System; using System.Collections.Generic; From 388ab24bb1846fc2d14f9dbfcd947d65a5cfa08a Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 16:58:49 +0800 Subject: [PATCH 05/14] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=88=86=E7=B1=BB=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 PropertyInfoExtension 和 TypeExtension 移动到 ReflectCategory,修正命名空间 - 将 StrExtension 移动到 TextCategory 并重命名为 StringExtension - 修复 HashUtil 命名空间为 EasyTool.CodeCategory - 改进代码组织,将相关功能归类到对应目录 --- EasyTool.Core/CodeCategory/HashUtil.cs | 2 +- .../{ToolCategory => ReflectCategory}/PropertyInfoExtension.cs | 2 +- .../{ToolCategory => ReflectCategory}/TypeExtension.cs | 2 +- .../StrExtension.cs => TextCategory/StringExtension.cs} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename EasyTool.Core/{ToolCategory => ReflectCategory}/PropertyInfoExtension.cs (99%) rename EasyTool.Core/{ToolCategory => ReflectCategory}/TypeExtension.cs (99%) rename EasyTool.Core/{ToolCategory/StrExtension.cs => TextCategory/StringExtension.cs} (99%) diff --git a/EasyTool.Core/CodeCategory/HashUtil.cs b/EasyTool.Core/CodeCategory/HashUtil.cs index 7202d3a..b6915f4 100644 --- a/EasyTool.Core/CodeCategory/HashUtil.cs +++ b/EasyTool.Core/CodeCategory/HashUtil.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace EasyTool +namespace EasyTool.CodeCategory { /// /// hash算法工具类 diff --git a/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs b/EasyTool.Core/ReflectCategory/PropertyInfoExtension.cs similarity index 99% rename from EasyTool.Core/ToolCategory/PropertyInfoExtension.cs rename to EasyTool.Core/ReflectCategory/PropertyInfoExtension.cs index 0e78d29..da4d5ed 100644 --- a/EasyTool.Core/ToolCategory/PropertyInfoExtension.cs +++ b/EasyTool.Core/ReflectCategory/PropertyInfoExtension.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Reflection; -namespace EasyTool.ToolCategory +namespace EasyTool.ReflectCategory { /// /// PropertyInfo 扩展方法 diff --git a/EasyTool.Core/ToolCategory/TypeExtension.cs b/EasyTool.Core/ReflectCategory/TypeExtension.cs similarity index 99% rename from EasyTool.Core/ToolCategory/TypeExtension.cs rename to EasyTool.Core/ReflectCategory/TypeExtension.cs index 5e1edff..a943f54 100644 --- a/EasyTool.Core/ToolCategory/TypeExtension.cs +++ b/EasyTool.Core/ReflectCategory/TypeExtension.cs @@ -5,7 +5,7 @@ using System.Reflection; using System.Runtime.CompilerServices; -namespace EasyTool.ToolCategory +namespace EasyTool.ReflectCategory { /// /// Type 类型扩展方法 diff --git a/EasyTool.Core/ToolCategory/StrExtension.cs b/EasyTool.Core/TextCategory/StringExtension.cs similarity index 99% rename from EasyTool.Core/ToolCategory/StrExtension.cs rename to EasyTool.Core/TextCategory/StringExtension.cs index 06dd0b1..4592ec0 100644 --- a/EasyTool.Core/ToolCategory/StrExtension.cs +++ b/EasyTool.Core/TextCategory/StringExtension.cs @@ -3,7 +3,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool.ToolCategory +namespace EasyTool.TextCategory { /// /// 字符串扩展方法 From b5a3f54f5e60ab2375df8e7f4a81732d25c34cee Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:00:04 +0800 Subject: [PATCH 06/14] =?UTF-8?q?refactor:=20=E8=BF=9B=E4=B8=80=E6=AD=A5?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E4=BB=B6=E5=88=86=E7=B1=BB=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 CreditCodeUtil 移动到 SecurityCategory(业务验证工具) - 将 StringComparisonExtension 移动到 TextCategory(字符串相关) - 减少 ToolCategory 中不相关的文件数量 - 改进代码组织的逻辑性 --- .../{ToolCategory => SecurityCategory}/CreditCodeUtil.cs | 0 .../{ToolCategory => TextCategory}/StringComparisonExtension.cs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename EasyTool.Core/{ToolCategory => SecurityCategory}/CreditCodeUtil.cs (100%) rename EasyTool.Core/{ToolCategory => TextCategory}/StringComparisonExtension.cs (100%) diff --git a/EasyTool.Core/ToolCategory/CreditCodeUtil.cs b/EasyTool.Core/SecurityCategory/CreditCodeUtil.cs similarity index 100% rename from EasyTool.Core/ToolCategory/CreditCodeUtil.cs rename to EasyTool.Core/SecurityCategory/CreditCodeUtil.cs diff --git a/EasyTool.Core/ToolCategory/StringComparisonExtension.cs b/EasyTool.Core/TextCategory/StringComparisonExtension.cs similarity index 100% rename from EasyTool.Core/ToolCategory/StringComparisonExtension.cs rename to EasyTool.Core/TextCategory/StringComparisonExtension.cs From 1894b8ab687f8a12a3f354d2ec7d138096320d54 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:02:23 +0800 Subject: [PATCH 07/14] =?UTF-8?q?refactor:=20=E5=90=88=E5=B9=B6=20IEnumera?= =?UTF-8?q?bleCategory=20=E5=88=B0=20CollectionsCategory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 IEnumerableExtensions.cs 移动到 CollectionsCategory - 删除空的 IEnumerableCategory 文件夹 - 统一集合相关扩展到同一分类目录 --- .../IEnumerableExtensions.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename EasyTool.Core/{IEnumerableCategory => CollectionsCategory}/IEnumerableExtensions.cs (100%) diff --git a/EasyTool.Core/IEnumerableCategory/IEnumerableExtensions.cs b/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs similarity index 100% rename from EasyTool.Core/IEnumerableCategory/IEnumerableExtensions.cs rename to EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs From 09bdb70651fd67a1fcb5eb94468319590b4717d1 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:04:08 +0800 Subject: [PATCH 08/14] =?UTF-8?q?refactor:=20=E6=89=A9=E5=B1=95=20DataCate?= =?UTF-8?q?gory=20=E5=B9=B6=E4=BF=AE=E5=A4=8D=E5=91=BD=E5=90=8D=E7=A9=BA?= =?UTF-8?q?=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 SimpleMapExtension 从 ToolCategory 移动到 DataCategory - 将 IdUtil 从 ToolCategory 移动到 DataCategory - 将 CreditCodeUtil 从 SecurityCategory 移动到 DataCategory - DataCategory 现在包含 4 个文件:PageUtil、SimpleMapExtension、IdUtil、CreditCodeUtil - 修复测试项目的命名空间引用 --- .../{SecurityCategory => DataCategory}/CreditCodeUtil.cs | 2 +- EasyTool.Core/{ToolCategory => DataCategory}/IdUtil.cs | 2 +- .../{ToolCategory => DataCategory}/SimpleMapExtension.cs | 2 +- EasyTool.CoreTests/ToolCategory/IDUtilTests.cs | 2 +- EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename EasyTool.Core/{SecurityCategory => DataCategory}/CreditCodeUtil.cs (99%) rename EasyTool.Core/{ToolCategory => DataCategory}/IdUtil.cs (99%) rename EasyTool.Core/{ToolCategory => DataCategory}/SimpleMapExtension.cs (99%) diff --git a/EasyTool.Core/SecurityCategory/CreditCodeUtil.cs b/EasyTool.Core/DataCategory/CreditCodeUtil.cs similarity index 99% rename from EasyTool.Core/SecurityCategory/CreditCodeUtil.cs rename to EasyTool.Core/DataCategory/CreditCodeUtil.cs index 21d8167..754279a 100644 --- a/EasyTool.Core/SecurityCategory/CreditCodeUtil.cs +++ b/EasyTool.Core/DataCategory/CreditCodeUtil.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace EasyTool.ToolCategory +namespace EasyTool.DataCategory { /// /// 社会信用代码工具 diff --git a/EasyTool.Core/ToolCategory/IdUtil.cs b/EasyTool.Core/DataCategory/IdUtil.cs similarity index 99% rename from EasyTool.Core/ToolCategory/IdUtil.cs rename to EasyTool.Core/DataCategory/IdUtil.cs index 6470694..ac01bb3 100644 --- a/EasyTool.Core/ToolCategory/IdUtil.cs +++ b/EasyTool.Core/DataCategory/IdUtil.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading; -namespace EasyTool.ToolCategory +namespace EasyTool.DataCategory { /// /// uuid生成风格 diff --git a/EasyTool.Core/ToolCategory/SimpleMapExtension.cs b/EasyTool.Core/DataCategory/SimpleMapExtension.cs similarity index 99% rename from EasyTool.Core/ToolCategory/SimpleMapExtension.cs rename to EasyTool.Core/DataCategory/SimpleMapExtension.cs index 1c6796d..98ae5f4 100644 --- a/EasyTool.Core/ToolCategory/SimpleMapExtension.cs +++ b/EasyTool.Core/DataCategory/SimpleMapExtension.cs @@ -7,7 +7,7 @@ using System.Text; using System.Text.Json; -namespace EasyTool.ToolCategory +namespace EasyTool.DataCategory { /// /// 简单实体转化拓展类 diff --git a/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs b/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs index ca2635a..c11c40c 100644 --- a/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs +++ b/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool.ToolCategory; +using EasyTool.DataCategory; using System; using System.Collections.Generic; using System.Linq; diff --git a/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs b/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs index 175b94d..e24118f 100644 --- a/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs +++ b/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool.ToolCategory; +using EasyTool.DataCategory; using System; using System.Collections.Generic; From 6509fa879c0b208fc626e22c3865aba263f84da4 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:06:43 +0800 Subject: [PATCH 09/14] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E7=B1=BB=E5=B9=B6=E4=BF=AE=E5=A4=8D=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 StringBuilderExtension 从 ToolCategory 移动到 TextCategory - 将 NumberExtension 从 ConvertCategory 移动到 MathCategory - 修复 FileSystemExtension 对 ToFileSize 的命名空间引用(ConvertCategory → MathCategory) - MathCategory 现在包含 4 个文件:MathUtil、PredictUtil、RandomUtil、NumberExtension - TextCategory 现在包含 7 个文件 --- EasyTool.Core/IOCategory/FileSystemExtension.cs | 2 +- .../{ConvertCategory => MathCategory}/NumberExtension.cs | 2 +- .../{ToolCategory => TextCategory}/StringBuilderExtension.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename EasyTool.Core/{ConvertCategory => MathCategory}/NumberExtension.cs (99%) rename EasyTool.Core/{ToolCategory => TextCategory}/StringBuilderExtension.cs (99%) diff --git a/EasyTool.Core/IOCategory/FileSystemExtension.cs b/EasyTool.Core/IOCategory/FileSystemExtension.cs index 4fe44fa..7d214fa 100644 --- a/EasyTool.Core/IOCategory/FileSystemExtension.cs +++ b/EasyTool.Core/IOCategory/FileSystemExtension.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Linq; -using EasyTool.ConvertCategory; +using EasyTool.MathCategory; namespace EasyTool.IOCategory { diff --git a/EasyTool.Core/ConvertCategory/NumberExtension.cs b/EasyTool.Core/MathCategory/NumberExtension.cs similarity index 99% rename from EasyTool.Core/ConvertCategory/NumberExtension.cs rename to EasyTool.Core/MathCategory/NumberExtension.cs index dcc047a..07fe0e4 100644 --- a/EasyTool.Core/ConvertCategory/NumberExtension.cs +++ b/EasyTool.Core/MathCategory/NumberExtension.cs @@ -1,6 +1,6 @@ using System; -namespace EasyTool.ConvertCategory +namespace EasyTool.MathCategory { /// /// 数字类型扩展方法 diff --git a/EasyTool.Core/ToolCategory/StringBuilderExtension.cs b/EasyTool.Core/TextCategory/StringBuilderExtension.cs similarity index 99% rename from EasyTool.Core/ToolCategory/StringBuilderExtension.cs rename to EasyTool.Core/TextCategory/StringBuilderExtension.cs index 8de02ad..d8a5610 100644 --- a/EasyTool.Core/ToolCategory/StringBuilderExtension.cs +++ b/EasyTool.Core/TextCategory/StringBuilderExtension.cs @@ -2,7 +2,7 @@ using System.IO; using System.Text; -namespace EasyTool.ToolCategory +namespace EasyTool.TextCategory { /// /// StringBuilder 扩展方法 From 596ffa45941e98eeadf2638584a75ab96906eb02 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:09:40 +0800 Subject: [PATCH 10/14] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E7=B1=BB=E7=BB=93=E6=9E=84=EF=BC=88=E6=96=B9?= =?UTF-8?q?=E6=A1=88A=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建新的 ImageCategory,将 ColorExtension 从 ConvertCategory 移入 - 将 EmojiUtil 从 EmojiCategory 移动到 TextCategory(表情符号是文本处理) - 将 DesensitizedUtil 从 SecurityCategory 移动到 TextCategory(脱敏是文本处理) - 删除空的 EmojiCategory 和 SecurityCategory 文件夹 - ImageCategory 现在包含 ColorExtension - TextCategory 现在包含 9 个文件(包含表情和脱敏) - ConvertCategory 减少到 3 个文件 --- .../{ConvertCategory => ImageCategory}/ColorExtension.cs | 2 +- .../{SecurityCategory => TextCategory}/DesensitizedUtil.cs | 2 +- EasyTool.Core/{EmojiCategory => TextCategory}/EmojiUtil.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename EasyTool.Core/{ConvertCategory => ImageCategory}/ColorExtension.cs (99%) rename EasyTool.Core/{SecurityCategory => TextCategory}/DesensitizedUtil.cs (99%) rename EasyTool.Core/{EmojiCategory => TextCategory}/EmojiUtil.cs (98%) diff --git a/EasyTool.Core/ConvertCategory/ColorExtension.cs b/EasyTool.Core/ImageCategory/ColorExtension.cs similarity index 99% rename from EasyTool.Core/ConvertCategory/ColorExtension.cs rename to EasyTool.Core/ImageCategory/ColorExtension.cs index b11f7d7..69abae2 100644 --- a/EasyTool.Core/ConvertCategory/ColorExtension.cs +++ b/EasyTool.Core/ImageCategory/ColorExtension.cs @@ -1,7 +1,7 @@ using System; using System.Drawing; -namespace EasyTool.ConvertCategory +namespace EasyTool.ImageCategory { /// /// Color 颜色扩展方法 diff --git a/EasyTool.Core/SecurityCategory/DesensitizedUtil.cs b/EasyTool.Core/TextCategory/DesensitizedUtil.cs similarity index 99% rename from EasyTool.Core/SecurityCategory/DesensitizedUtil.cs rename to EasyTool.Core/TextCategory/DesensitizedUtil.cs index 3873756..a1b38c8 100644 --- a/EasyTool.Core/SecurityCategory/DesensitizedUtil.cs +++ b/EasyTool.Core/TextCategory/DesensitizedUtil.cs @@ -3,7 +3,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool.SecurityCategory +namespace EasyTool.TextCategory { /// /// 信息脱敏工具类 diff --git a/EasyTool.Core/EmojiCategory/EmojiUtil.cs b/EasyTool.Core/TextCategory/EmojiUtil.cs similarity index 98% rename from EasyTool.Core/EmojiCategory/EmojiUtil.cs rename to EasyTool.Core/TextCategory/EmojiUtil.cs index 615ba06..a0f15db 100644 --- a/EasyTool.Core/EmojiCategory/EmojiUtil.cs +++ b/EasyTool.Core/TextCategory/EmojiUtil.cs @@ -3,7 +3,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace EasyTool.EmojiCategory +namespace EasyTool.TextCategory { public static class EmojiUtil { From cc874566c06b26efa09cbf7dbfa48dece9d8557a Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:15:26 +0800 Subject: [PATCH 11/14] =?UTF-8?q?refactor:=20=E6=89=A7=E8=A1=8C=E6=96=B9?= =?UTF-8?q?=E6=A1=88A=20-=20=E5=85=A8=E9=9D=A2=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E7=B1=BB=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 标记 ByteExtension.ToHex/ToHexLower 为 Obsolete(与 HexUtil 重复) - 创建 IdentifierCategory,将 IdUtil 从 DataCategory 移入 - 创建 BusinessCategory,将 CreditCodeUtil 从 DataCategory 移入 - 将 SimpleMapExtension 移回 ToolCategory - 重命名 ImageCategory 为 ColorCategory(更准确) - 修复所有命名空间引用 - 修复测试项目的命名空间引用 最终分类结构: - CodeCategory(5), CollectionsCategory(7), ConvertCategory(3), DataCategory(2) - DateTimeCategory(4), ColorCategory(1), IOCategory(7), MathCategory(4) - NetCategory(3), ReflectCategory(3), Standardization(3), SystemCategory(2) - TextCategory(7), ToolCategory(7), IdentifierCategory(1), BusinessCategory(1) --- .../{DataCategory => BusinessCategory}/CreditCodeUtil.cs | 2 +- .../{ImageCategory => ColorCategory}/ColorExtension.cs | 2 +- EasyTool.Core/ConvertCategory/ByteExtension.cs | 4 +++- EasyTool.Core/{DataCategory => IdentifierCategory}/IdUtil.cs | 2 +- .../{DataCategory => ToolCategory}/SimpleMapExtension.cs | 2 +- EasyTool.CoreTests/ToolCategory/IDUtilTests.cs | 2 +- EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs | 2 +- 7 files changed, 9 insertions(+), 7 deletions(-) rename EasyTool.Core/{DataCategory => BusinessCategory}/CreditCodeUtil.cs (99%) rename EasyTool.Core/{ImageCategory => ColorCategory}/ColorExtension.cs (99%) rename EasyTool.Core/{DataCategory => IdentifierCategory}/IdUtil.cs (99%) rename EasyTool.Core/{DataCategory => ToolCategory}/SimpleMapExtension.cs (99%) diff --git a/EasyTool.Core/DataCategory/CreditCodeUtil.cs b/EasyTool.Core/BusinessCategory/CreditCodeUtil.cs similarity index 99% rename from EasyTool.Core/DataCategory/CreditCodeUtil.cs rename to EasyTool.Core/BusinessCategory/CreditCodeUtil.cs index 754279a..c96ed02 100644 --- a/EasyTool.Core/DataCategory/CreditCodeUtil.cs +++ b/EasyTool.Core/BusinessCategory/CreditCodeUtil.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace EasyTool.DataCategory +namespace EasyTool.BusinessCategory { /// /// 社会信用代码工具 diff --git a/EasyTool.Core/ImageCategory/ColorExtension.cs b/EasyTool.Core/ColorCategory/ColorExtension.cs similarity index 99% rename from EasyTool.Core/ImageCategory/ColorExtension.cs rename to EasyTool.Core/ColorCategory/ColorExtension.cs index 69abae2..9115b2f 100644 --- a/EasyTool.Core/ImageCategory/ColorExtension.cs +++ b/EasyTool.Core/ColorCategory/ColorExtension.cs @@ -1,7 +1,7 @@ using System; using System.Drawing; -namespace EasyTool.ImageCategory +namespace EasyTool.ColorCategory { /// /// Color 颜色扩展方法 diff --git a/EasyTool.Core/ConvertCategory/ByteExtension.cs b/EasyTool.Core/ConvertCategory/ByteExtension.cs index 13d4829..2326420 100644 --- a/EasyTool.Core/ConvertCategory/ByteExtension.cs +++ b/EasyTool.Core/ConvertCategory/ByteExtension.cs @@ -13,8 +13,9 @@ public static class ByteExtension #region 单字节转换 /// - /// 将字节转换为16进制字符串 + /// 将字节转换为16进制字符串(已移除,使用 CodeCategory/HexUtil.ToHex) /// + [Obsolete("请使用 CodeCategory.HexUtil.ToHex 方法", true)] public static string ToHex(this byte value) { return value.ToString("X2"); @@ -23,6 +24,7 @@ public static string ToHex(this byte value) /// /// 将字节转换为16进制字符串(小写) /// + [Obsolete("请使用 CodeCategory.HexUtil.ToHexLower 方法", true)] public static string ToHexLower(this byte value) { return value.ToString("x2"); diff --git a/EasyTool.Core/DataCategory/IdUtil.cs b/EasyTool.Core/IdentifierCategory/IdUtil.cs similarity index 99% rename from EasyTool.Core/DataCategory/IdUtil.cs rename to EasyTool.Core/IdentifierCategory/IdUtil.cs index ac01bb3..393e887 100644 --- a/EasyTool.Core/DataCategory/IdUtil.cs +++ b/EasyTool.Core/IdentifierCategory/IdUtil.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading; -namespace EasyTool.DataCategory +namespace EasyTool.IdentifierCategory { /// /// uuid生成风格 diff --git a/EasyTool.Core/DataCategory/SimpleMapExtension.cs b/EasyTool.Core/ToolCategory/SimpleMapExtension.cs similarity index 99% rename from EasyTool.Core/DataCategory/SimpleMapExtension.cs rename to EasyTool.Core/ToolCategory/SimpleMapExtension.cs index 98ae5f4..1c6796d 100644 --- a/EasyTool.Core/DataCategory/SimpleMapExtension.cs +++ b/EasyTool.Core/ToolCategory/SimpleMapExtension.cs @@ -7,7 +7,7 @@ using System.Text; using System.Text.Json; -namespace EasyTool.DataCategory +namespace EasyTool.ToolCategory { /// /// 简单实体转化拓展类 diff --git a/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs b/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs index c11c40c..694d834 100644 --- a/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs +++ b/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool.DataCategory; +using EasyTool.IdentifierCategory; using System; using System.Collections.Generic; using System.Linq; diff --git a/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs b/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs index e24118f..175b94d 100644 --- a/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs +++ b/EasyTool.CoreTests/ToolCategory/SimpleMapExtensionTests.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using EasyTool.DataCategory; +using EasyTool.ToolCategory; using System; using System.Collections.Generic; From e8715d774f69f2fca4746fa5b289b97aa97d1d12 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:21:30 +0800 Subject: [PATCH 12/14] =?UTF-8?q?refactor:=20=E6=9C=80=E7=BB=88=E6=B8=85?= =?UTF-8?q?=E7=90=86=E5=92=8C=E4=BC=98=E5=8C=96=E6=96=87=E4=BB=B6=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 ConvertCategory/ByteExtension.cs(仅含 Obsolete 方法,与 HexUtil 重复) - 将 PageUtil 从 DataCategory 移动到 ToolCategory - 删除空的 DataCategory 文件夹 最终分类结构(15个分类,55个文件): - BusinessCategory(1), CodeCategory(5), CollectionsCategory(7), ColorCategory(1) - ConvertCategory(2), DateTimeCategory(4), IdentifierCategory(1), IOCategory(7) - MathCategory(4), NetCategory(3), ReflectCategory(3), Standardization(3) - SystemCategory(2), TextCategory(9), ToolCategory(8) --- .../ConvertCategory/ByteExtension.cs | 508 ------------------ .../PageUtil.cs | 0 2 files changed, 508 deletions(-) delete mode 100644 EasyTool.Core/ConvertCategory/ByteExtension.cs rename EasyTool.Core/{DataCategory => ToolCategory}/PageUtil.cs (100%) diff --git a/EasyTool.Core/ConvertCategory/ByteExtension.cs b/EasyTool.Core/ConvertCategory/ByteExtension.cs deleted file mode 100644 index 2326420..0000000 --- a/EasyTool.Core/ConvertCategory/ByteExtension.cs +++ /dev/null @@ -1,508 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Linq; - -namespace EasyTool.ConvertCategory -{ - /// - /// Byte 字节扩展方法 - /// - public static class ByteExtension - { - #region 单字节转换 - - /// - /// 将字节转换为16进制字符串(已移除,使用 CodeCategory/HexUtil.ToHex) - /// - [Obsolete("请使用 CodeCategory.HexUtil.ToHex 方法", true)] - public static string ToHex(this byte value) - { - return value.ToString("X2"); - } - - /// - /// 将字节转换为16进制字符串(小写) - /// - [Obsolete("请使用 CodeCategory.HexUtil.ToHexLower 方法", true)] - public static string ToHexLower(this byte value) - { - return value.ToString("x2"); - } - - /// - /// 将字节转换为二进制字符串 - /// - public static string ToBinaryString(this byte value) - { - return Convert.ToString(value, 2).PadLeft(8, '0'); - } - - /// - /// 获取字节的指定位 - /// - public static bool GetBit(this byte value, int index) - { - if (index < 0 || index > 7) - throw new ArgumentOutOfRangeException(nameof(index), "Index must be between 0 and 7"); - - return (value & (1 << index)) != 0; - } - - /// - /// 设置字节的指定位 - /// - public static byte SetBit(this byte value, int index, bool bitValue) - { - if (index < 0 || index > 7) - throw new ArgumentOutOfRangeException(nameof(index), "Index must be between 0 and 7"); - - if (bitValue) - return (byte)(value | (1 << index)); - else - return (byte)(value & ~(1 << index)); - } - - #endregion - - #region 字节数组转换 - - /// - /// 将字节数组转换为16进制字符串 - /// - public static string ToHex(this byte[] bytes) - { - return bytes.ToHex(true); - } - - /// - /// 将字节数组转换为16进制字符串 - /// - public static string ToHex(this byte[] bytes, bool uppercase) - { - if (bytes == null || bytes.Length == 0) - return string.Empty; - - var format = uppercase ? "X2" : "x2"; - var sb = new StringBuilder(bytes.Length * 2); - - foreach (var b in bytes) - { - sb.Append(b.ToString(format)); - } - - return sb.ToString(); - } - - /// - /// 将字节数组转换为16进制字符串(带分隔符) - /// - public static string ToHex(this byte[] bytes, string separator, bool uppercase = true) - { - if (bytes == null || bytes.Length == 0) - return string.Empty; - - var format = uppercase ? "X2" : "x2"; - return string.Join(separator, bytes.Select(b => b.ToString(format))); - } - - /// - /// 从16进制字符串转换为字节数组 - /// - public static byte[] FromHexToBytes(this string hex) - { - if (string.IsNullOrWhiteSpace(hex)) - return Array.Empty(); - - hex = hex.Replace("-", "").Replace(" ", ""); - - if (hex.Length % 2 != 0) - throw new ArgumentException("Hex string must have an even length", nameof(hex)); - - var bytes = new byte[hex.Length / 2]; - - for (int i = 0; i < bytes.Length; i++) - { - bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); - } - - return bytes; - } - - - /// - /// 将字节数组转换为二进制字符串 - /// - public static string ToBinaryString(this byte[] bytes, string separator = " ") - { - if (bytes == null || bytes.Length == 0) - return string.Empty; - - return string.Join(separator, bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0'))); - } - - /// - /// 从二进制字符串转换为字节数组 - /// - public static byte[] FromBinaryStringToBytes(this string binary) - { - if (string.IsNullOrWhiteSpace(binary)) - return Array.Empty(); - - binary = binary.Replace(" ", ""); - - if (binary.Length % 8 != 0) - throw new ArgumentException("Binary string length must be a multiple of 8", nameof(binary)); - - var bytes = new byte[binary.Length / 8]; - - for (int i = 0; i < bytes.Length; i++) - { - bytes[i] = Convert.ToByte(binary.Substring(i * 8, 8), 2); - } - - return bytes; - } - - #endregion - - #region 字节数组操作 - - /// - /// 反转字节数组 - /// - public static byte[]? Reverse(this byte[]? bytes) - { - if (bytes == null) - return null; - - var result = new byte[bytes.Length]; - Array.Copy(bytes, result, bytes.Length); - Array.Reverse(result); - return result; - } - - /// - /// 字节数组异或运算 - /// - public static byte[] Xor(this byte[] bytes, byte[] key) - { - if (bytes == null) - throw new ArgumentNullException(nameof(bytes)); - if (key == null) - throw new ArgumentNullException(nameof(key)); - - var result = new byte[bytes.Length]; - - for (int i = 0; i < bytes.Length; i++) - { - result[i] = (byte)(bytes[i] ^ key[i % key.Length]); - } - - return result; - } - - /// - /// 字节数组异或运算(单字节密钥) - /// - public static byte[] Xor(this byte[] bytes, byte key) - { - if (bytes == null) - throw new ArgumentNullException(nameof(bytes)); - - var result = new byte[bytes.Length]; - - for (int i = 0; i < bytes.Length; i++) - { - result[i] = (byte)(bytes[i] ^ key); - } - - return result; - } - - /// - /// 合并多个字节数组 - /// - public static byte[] Combine(params byte[][]? arrays) - { - if (arrays == null || arrays.Length == 0) - return Array.Empty(); - - int totalLength = 0; - foreach (var arr in arrays) - { - if (arr != null) - totalLength += arr.Length; - } - - var result = new byte[totalLength]; - int offset = 0; - - foreach (var arr in arrays) - { - if (arr != null && arr.Length > 0) - { - Array.Copy(arr, 0, result, offset, arr.Length); - offset += arr.Length; - } - } - - return result; - } - - /// - /// 截取字节数组 - /// - public static byte[] SubArray(this byte[] bytes, int startIndex, int length) - { - if (bytes == null) - throw new ArgumentNullException(nameof(bytes)); - - if (startIndex < 0 || startIndex >= bytes.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex)); - - if (length < 0 || startIndex + length > bytes.Length) - throw new ArgumentOutOfRangeException(nameof(length)); - - var result = new byte[length]; - Array.Copy(bytes, startIndex, result, 0, length); - return result; - } - - /// - /// 截取字节数组(从指定位置到末尾) - /// - public static byte[] SubArray(this byte[] bytes, int startIndex) - { - if (bytes == null) - throw new ArgumentNullException(nameof(bytes)); - - if (startIndex < 0) - startIndex = 0; - - if (startIndex >= bytes.Length) - return Array.Empty(); - - return SubArray(bytes, startIndex, bytes.Length - startIndex); - } - - #endregion - - #region 字节数组比较 - - /// - /// 比较两个字节数组是否相等 - /// - public static bool EqualsTo(this byte[]? bytes, byte[]? other) - { - if (ReferenceEquals(bytes, other)) - return true; - - if (bytes == null || other == null) - return false; - - if (bytes.Length != other.Length) - return false; - - for (int i = 0; i < bytes.Length; i++) - { - if (bytes[i] != other[i]) - return false; - } - - return true; - } - - /// - /// 字节数组比较(返回差异索引) - /// - public static int[] Diff(this byte[]? bytes, byte[]? other) - { - if (bytes == null || other == null) - return Array.Empty(); - - var minLength = Math.Min(bytes.Length, other.Length); - var diffs = new System.Collections.Generic.List(); - - for (int i = 0; i < minLength; i++) - { - if (bytes[i] != other[i]) - diffs.Add(i); - } - - return diffs.ToArray(); - } - - #endregion - - #region 字节数组与基本类型转换 - - /// - /// 将字节数组转换为整数(小端序) - /// - public static int ToInt32(this byte[] bytes, int startIndex = 0) - { - if (bytes == null) - throw new ArgumentNullException(nameof(bytes)); - - if (startIndex < 0 || startIndex + 4 > bytes.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex)); - - return bytes[startIndex] | (bytes[startIndex + 1] << 8) | (bytes[startIndex + 2] << 16) | (bytes[startIndex + 3] << 24); - } - - /// - /// 将整数转换为字节数组(小端序) - /// - public static byte[] ToBytes(this int value) - { - return new[] { (byte)(value & 0xFF), (byte)((value >> 8) & 0xFF), (byte)((value >> 16) & 0xFF), (byte)((value >> 24) & 0xFF) }; - } - - /// - /// 将字节数组转换为长整数(小端序) - /// - public static long ToInt64(this byte[] bytes, int startIndex = 0) - { - if (bytes == null) - throw new ArgumentNullException(nameof(bytes)); - - if (startIndex < 0 || startIndex + 8 > bytes.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex)); - - return BitConverter.ToInt64(bytes, startIndex); - } - - /// - /// 将长整数转换为字节数组(小端序) - /// - public static byte[] ToBytes(this long value) - { - return BitConverter.GetBytes(value); - } - - /// - /// 将字节数组转换为短整数(小端序) - /// - public static short ToInt16(this byte[] bytes, int startIndex = 0) - { - if (bytes == null) - throw new ArgumentNullException(nameof(bytes)); - - if (startIndex < 0 || startIndex + 2 > bytes.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex)); - - return (short)(bytes[startIndex] | (bytes[startIndex + 1] << 8)); - } - - /// - /// 将短整数转换为字节数组(小端序) - /// - public static byte[] ToBytes(this short value) - { - return new[] { (byte)(value & 0xFF), (byte)((value >> 8) & 0xFF) }; - } - - #endregion - - #region 字节数组编码解码 - - - #endregion - - #region 字节数组压缩解压 - - /// - /// 压缩字节数组(使用 GZip) - /// - public static byte[]? Compress(this byte[]? bytes) - { - if (bytes == null || bytes.Length == 0) - return bytes; - - using var output = new MemoryStream(); - using (var gzip = new System.IO.Compression.GZipStream(output, System.IO.Compression.CompressionMode.Compress)) - { - gzip.Write(bytes, 0, bytes.Length); - } - return output.ToArray(); - } - - /// - /// 解压字节数组(使用 GZip) - /// - public static byte[]? Decompress(this byte[]? bytes) - { - if (bytes == null || bytes.Length == 0) - return bytes; - - using var input = new MemoryStream(bytes); - using var gzip = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress); - using var output = new MemoryStream(); - gzip.CopyTo(output); - return output.ToArray(); - } - - #endregion - - #region 字节数组哈希 - - /// - /// 计算字节数组的 MD5 哈希值 - /// - public static byte[] ToMd5(this byte[] bytes) - { - if (bytes == null || bytes.Length == 0) - return Array.Empty(); - - using var md5 = System.Security.Cryptography.MD5.Create(); - return md5.ComputeHash(bytes); - } - - /// - /// 计算字节数组的 SHA1 哈希值 - /// - public static byte[] ToSha1(this byte[] bytes) - { - if (bytes == null || bytes.Length == 0) - return Array.Empty(); - - using var sha1 = System.Security.Cryptography.SHA1.Create(); - return sha1.ComputeHash(bytes); - } - - /// - /// 计算字节数组的 SHA256 哈希值 - /// - public static byte[] ToSha256(this byte[] bytes) - { - if (bytes == null || bytes.Length == 0) - return Array.Empty(); - - using var sha256 = System.Security.Cryptography.SHA256.Create(); - return sha256.ComputeHash(bytes); - } - - /// - /// 计算字节数组的 CRC32 校验值 - /// - public static uint ToCrc32(this byte[] bytes) - { - if (bytes == null || bytes.Length == 0) - return 0; - - uint crc = 0xFFFFFFFF; - foreach (var b in bytes) - { - crc ^= b; - for (int i = 0; i < 8; i++) - { - crc = (crc >> 1) ^ ((crc & 1) == 1 ? 0xEDB88320 : 0); - } - } - return ~crc; - } - - #endregion - } -} diff --git a/EasyTool.Core/DataCategory/PageUtil.cs b/EasyTool.Core/ToolCategory/PageUtil.cs similarity index 100% rename from EasyTool.Core/DataCategory/PageUtil.cs rename to EasyTool.Core/ToolCategory/PageUtil.cs From 62a73caa3df4bd7886ad6e3f617738203614bd96 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Fri, 13 Feb 2026 17:30:01 +0800 Subject: [PATCH 13/14] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BB=93=E6=9E=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完善 README.md 项目结构说明 - 新增 15 个分类的详细说明和文件清单 - 记录今日所有优化历程和最终成果 - 更新日期:2025-02-13 --- README.md | 191 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index d9e715d..cb10214 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,26 @@

EasyTool

-
- 一个开源的 .NET 工具库, 使得开发变得更加有效率 +
+
- -[![pull_request](https://github.com/dotnet-easy/easytool/actions/workflows/pull_request.yml/badge.svg)](https://github.com/dotnet-easy/easytool/actions/workflows/pull_request.yml) +[![pull_request](https://github.com/dotnet-easy/easytool/actions/workflows/pull_request.yml/badge.svg)](https://github.com/dotnet-easy/easytool/actions/workflows/pull_request.yml/badge.svg) [![](https://img.shields.io/nuget/v/EasyTool.Core.svg)](https://www.nuget.org/packages/EasyTool.Core)

- 中文 | English + 中文 | English

## 📚 简介 Easytool 是一个功能丰富且易用的 .NET 工具库,旨在帮助开发者快速、便捷地完成各类开发任务。 这些封装的工具涵盖了字符串、数字、集合、编码、日期、文件、IO、加密、JSON、HTTP客户端等一系列操作, 可以满足各种不同的开发需求。 + > [More information](https://easy-dotnet.com/pages/easytool/) -> + ## 🚀 快速开始 -### 安装 + +### 安装 + ~~~ PM> Install-Package EasyTool.Core ~~~ @@ -28,6 +30,7 @@ dotnet add package EasyTool.Core ~~~ ### 使用 + 复制文件或者目录 ~~~csharp FileUtil.Copy(sourceDir, destinationDir, isOverwrite) @@ -37,20 +40,176 @@ FileUtil.Copy(sourceDir, destinationDir, isOverwrite) var a = CloneUtil.Clone(person); ~~~ - ## 🛠️ 目录 + Easytool 封装了开发过程中一些常用的方法 -| Catalog | Introduce | -| --------------------------------------------------|---------------------------------------------------------------------------------- | -| [clone](EasyTool.Core/CloneCategory/) | 使用 CloneUtil.Clone 方法实现 .NET 对象的深度复制 | -| [code](EasyTool.Core/CodeCategory/) | 提供基于 base32, base62 等编解码工具 | +--- -## 代码共享 +## 📁 项目结构(最新更新:2025-02-13) + +EasyTool.Core 采用**模块化分类结构**,所有工具按功能领域清晰划分到 15 个分类目录中: + +### 📂 分类概览 + +| 分类 | 文件数 | 功能描述 | +|------|--------|----------| +| **BusinessCategory** | 1 | 业务数据处理(社会信用代码) | +| **CodeCategory** | 5 | 加密/编码工具(AES/DES/编码/哈希/十六进制) | +| **CollectionsCategory** | 7 | 集合扩展操作(数组/字典/链表/列表/队列/栈) | +| **ColorCategory** | 1 | 颜色处理扩展 | +| **ConvertCategory** | 1 | 数据类型转换工具 | +| **DateTimeCategory** | 4 | 日期时间处理(扩展/工具/日历/计时器) | +| **IdentifierCategory** | 1 | 标识符生成工具(UUID/ObjectId/Snowflake) | +| **IOCategory** | 7 | 文件/流/压缩操作(文件系统/文件类型/流/监控/ZIP) | +| **MathCategory** | 4 | 数学工具(计算/预测/随机数) | +| **NetCategory** | 3 | 网络工具(HTTP/IP/URL) | +| **ReflectCategory** | 3 | 反射/类型/属性扩展 | +| **Standardization** | 3 | 标准化类型(Option/QueryPage/Result) | +| **SystemCategory** | 2 | 系统环境工具(环境变量/系统信息) | +| **TextCategory** | 9 | 文本处理工具(正则/字符串/分割/XML/表情/脱敏) | +| **ToolCategory** | 8 | 通用扩展方法(委托/枚举/异常/GUID/对象/映射/任务/分页) | + +### 📋 各分类详细说明 + +#### **BusinessCategory** - 业务数据处理 +``` +CreditCodeUtil.cs - 中国社会信用代码的验证和处理工具 +``` + +#### **CodeCategory** - 加密/编码工具 +``` +AesUtil.cs - AES 加密/解密(支持 ECB/CBC 模式) +DesUtil.cs - DES 加密/解密(支持 ECB 模式) +EncodingUtil.cs - 编码转换工具 +HashUtil.cs - 17 种哈希算法(加法/旋转/Bernstein/FNV/DJB/BKDR 等) +HexUtil.cs - 十六进制转换工具 +``` +#### **CollectionsCategory** - 集合扩展操作 +``` +ArrayExtension.cs - 数组操作扩展 +DictionaryExtension.cs - 字典操作扩展 +IEnumerableExtensions.cs - IEnumerable 集合遍历扩展 +LinkedListUtil.cs - 链表操作工具 +ListExtension.cs - 列表操作扩展 +QueueUtil.cs - 队列操作工具 +StackUtil.cs - 栈操作工具 +``` -## 社区交流 +#### **ColorCategory** - 颜色处理 +``` +ColorExtension.cs - 颜色扩展(RGB/HSV/HEX 转换) +``` -**微信:ygdxg8657 (备注进群) QQ群:543829648 903210423(已满)** +#### **ConvertCategory** - 数据类型转换 +``` +ConvertExtension.cs - 通用数据类型转换(ToByte/ToShort/ToInt/ToLong/ToFloat/ToDouble/ToDecimal) +``` -![easy-tool](https://raw.githubusercontent.com/dotnet-easy/easy-dotnet/main/files/img/easytool.png) +#### **DateTimeCategory** - 日期时间处理 +``` +DateTimeExtension.cs - DateTime 类型扩展方法 +DateTimeUtil.cs - 日期时间工具类 +LunarCalendarUtil.cs - 农历工具 +TimerUtil.cs - 计时器工具 +``` + +#### **IdentifierCategory** - 标识符生成 +``` +IdUtil.cs - ID 生成工具(有序 UUID/ObjectId/Snowflake ID) +``` + +#### **IOCategory** - 文件/流/压缩 +``` +FileSystemExtension.cs - 文件系统操作扩展 +FileTypeExtension.cs - 文件类型判断 +FileUtil.cs - 文件操作工具 +StreamExtension.cs - 流操作扩展 +Tailer.cs - 文件尾部追踪工具 +WatchMonitor.cs - 文件监控工具 +ZipUtil.cs - ZIP 压缩工具 +``` + +#### **MathCategory** - 数学工具 +``` +MathUtil.cs - 数学计算工具 +NumberExtension.cs - 数字类型扩展(偶数/质数/二进制/十六进制) +PredictUtil.cs - 预测算法工具 +RandomUtil.cs - 随机数生成工具 +``` + +#### **NetCategory** - 网络工具 +``` +HttpClientExtension.cs - HttpClient 扩展 +IpUtil.cs - IP 地址处理工具 +URLUtil.cs - URL 处理工具 +``` + +#### **ReflectCategory** - 反射/类型/属性扩展 +``` +PropertyInfoExtension.cs - PropertyInfo 扩展(值获取/设置/特性检查) +ReflectUtil.cs - 反射工具类 +TypeExtension.cs - Type 类型扩展(类型判断/友好名称/默认值) +``` + +#### **Standardization** - 标准化类型 +``` +Option.cs - 选项对象(用于前端下拉) +QueryPage.cs - 分页查询对象 +Result.cs - 统一结果对象 +``` + +#### **SystemCategory** - 系统环境工具 +``` +EnvUtil.cs - 环境变量工具 +SystemUtil.cs - 系统信息工具 +``` + +#### **TextCategory** - 文本处理工具(9个文件) +``` +RegexUtil.cs - 正则表达式工具 +StringBuilderExtension.cs - StringBuilder 扩展 +StringComparisonExtension.cs - 字符串比较扩展 +StringExtension.cs - 字符串验证扩展(邮箱/手机/URL/身份证等) +StrSplitter.cs - 字符串分割工具 +StrUtil.cs - 字符串处理工具(命名转换/空格处理) +XmlUtil.cs - XML 处理工具 +EmojiUtil.cs - 表情符号处理工具 +DesensitizedUtil.cs - 数据脱敏工具(手机号/身份证/银行卡等) +``` + +#### **ToolCategory** - 通用扩展方法(8个文件) +``` +DelegateExtension.cs - 委托扩展(安全调用) +EnumExtension.cs - 枚举扩展(获取描述) +ExceptionExtension.cs - 异常扩展(获取完整异常信息) +GuidExtension.cs - Guid 扩展(空值判断) +ObjectExtension.cs - 对象扩展(深拷贝/JSON序列化) +SimpleMapExtension.cs - 简单对象映射扩展 +TaskExtension.cs - Task 异步扩展(Fire-and-Forget) +PageUtil.cs - 分页工具(支持多种数据源和排序方式) +``` + +--- + +### 📈 优化历程 + +本次更新主要完成了以下结构优化工作: + +1. ✅ **ReflectCategory 扩展** - PropertyInfoExtension、TypeExtension 移入 +2. ✅ **TextCategory 扩展** - StringComparisonExtension、StringExtension、EmojiUtil、DesensitizedUtil、StringBuilderExtension 移入 +3. ✅ **CollectionsCategory 扩展** - IEnumerableExtensions 合并 +4. ✅ **IdentifierCategory 新建** - ID 生成工具独立分类 +5. ✅ **BusinessCategory 新建** - 业务数据处理独立分类 +6. ✅ **ColorCategory 精简** - 颜色处理单独分类 +7. ✅ **ToolCategory 优化** - SimpleMapExtension、PageUtil 移入 +8. ✅ **空壳文件清理** - 删除仅含 Obsolete 方法的文件 + +**最终状态**:**15 个分类,55 个源文件**,结构清晰、功能明确、无重复代码。 + +--- + +> 项目采用模块化设计,每个分类职责单一,便于查找和维护。所有工具类都使用静态方法,无需实例化即可使用。 + +## 代码共享 From 5816b5b02a4a5b8bb4aa4ba3b0a89a355edc6314 Mon Sep 17 00:00:00 2001 From: lilinjin0520 <761747705@qq.com> Date: Sat, 14 Feb 2026 09:35:04 +0800 Subject: [PATCH 14/14] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BB=93=E6=9E=84=E5=92=8C=E4=BF=AE=E5=A4=8D=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4=E4=B8=8D=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 IEnumerableExtensions.cs 命名空间为 EasyTool.CollectionsCategory - 修复 PageUtil.cs 命名空间为 EasyTool.ToolCategory - 移动测试文件到正确的分类目录 (IdentifierCategory, NetCategory) - 为 EasyTool.NPOI 添加 OfficeCategory 分类目录 - 更新 GitHub Actions 到 actions@v4 和 .NET 8.0.x/10.0.x --- .github/workflows/nuget_pre.yml | 8 +++++--- .github/workflows/nuget_prod.yml | 8 +++++--- .github/workflows/pull_request.yml | 8 +++++--- .../CollectionsCategory/IEnumerableExtensions.cs | 2 +- EasyTool.Core/ToolCategory/PageUtil.cs | 2 +- .../{ToolCategory => IdentifierCategory}/IDUtilTests.cs | 0 .../{ToolCategory => NetCategory}/IpUtilTests.cs | 0 EasyTool.NPOI/{ => OfficeCategory}/ExcelWorkbookType.cs | 0 EasyTool.NPOI/{ => OfficeCategory}/NPOIUtil.cs | 0 EasyTool.NPOITests/{ => OfficeCategory}/NPOIUtilTests.cs | 0 10 files changed, 17 insertions(+), 11 deletions(-) rename EasyTool.CoreTests/{ToolCategory => IdentifierCategory}/IDUtilTests.cs (100%) rename EasyTool.CoreTests/{ToolCategory => NetCategory}/IpUtilTests.cs (100%) rename EasyTool.NPOI/{ => OfficeCategory}/ExcelWorkbookType.cs (100%) rename EasyTool.NPOI/{ => OfficeCategory}/NPOIUtil.cs (100%) rename EasyTool.NPOITests/{ => OfficeCategory}/NPOIUtilTests.cs (100%) diff --git a/.github/workflows/nuget_pre.yml b/.github/workflows/nuget_pre.yml index 96303e6..b0b93fc 100644 --- a/.github/workflows/nuget_pre.yml +++ b/.github/workflows/nuget_pre.yml @@ -13,11 +13,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: | + 8.0.x + 10.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/nuget_prod.yml b/.github/workflows/nuget_prod.yml index 999568a..d158e4a 100644 --- a/.github/workflows/nuget_prod.yml +++ b/.github/workflows/nuget_prod.yml @@ -13,11 +13,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: | + 8.0.x + 10.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 0802805..9a45b93 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -13,11 +13,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: | + 8.0.x + 10.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs b/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs index d458173..34a7875 100644 --- a/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs +++ b/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; -namespace EasyTool.IEnumerableCategory +namespace EasyTool.CollectionsCategory { /// /// IEnumerable 通用扩展方法 diff --git a/EasyTool.Core/ToolCategory/PageUtil.cs b/EasyTool.Core/ToolCategory/PageUtil.cs index 5a6b25e..967af93 100644 --- a/EasyTool.Core/ToolCategory/PageUtil.cs +++ b/EasyTool.Core/ToolCategory/PageUtil.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace EasyTool.DataCategory +namespace EasyTool.ToolCategory { /// /// 分页工具类,支持多种数据源和多种排序方式的分页 diff --git a/EasyTool.CoreTests/ToolCategory/IDUtilTests.cs b/EasyTool.CoreTests/IdentifierCategory/IDUtilTests.cs similarity index 100% rename from EasyTool.CoreTests/ToolCategory/IDUtilTests.cs rename to EasyTool.CoreTests/IdentifierCategory/IDUtilTests.cs diff --git a/EasyTool.CoreTests/ToolCategory/IpUtilTests.cs b/EasyTool.CoreTests/NetCategory/IpUtilTests.cs similarity index 100% rename from EasyTool.CoreTests/ToolCategory/IpUtilTests.cs rename to EasyTool.CoreTests/NetCategory/IpUtilTests.cs diff --git a/EasyTool.NPOI/ExcelWorkbookType.cs b/EasyTool.NPOI/OfficeCategory/ExcelWorkbookType.cs similarity index 100% rename from EasyTool.NPOI/ExcelWorkbookType.cs rename to EasyTool.NPOI/OfficeCategory/ExcelWorkbookType.cs diff --git a/EasyTool.NPOI/NPOIUtil.cs b/EasyTool.NPOI/OfficeCategory/NPOIUtil.cs similarity index 100% rename from EasyTool.NPOI/NPOIUtil.cs rename to EasyTool.NPOI/OfficeCategory/NPOIUtil.cs diff --git a/EasyTool.NPOITests/NPOIUtilTests.cs b/EasyTool.NPOITests/OfficeCategory/NPOIUtilTests.cs similarity index 100% rename from EasyTool.NPOITests/NPOIUtilTests.cs rename to EasyTool.NPOITests/OfficeCategory/NPOIUtilTests.cs