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