|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// 【文件说明】HwPortalControllerBase.cs - 门户控制器基类
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// 这个类是所有门户控制器的"公共基类"。
|
|
|
|
|
|
// 在 Java Spring Boot 中,你可能会写一个 BaseController,提供公共方法。
|
|
|
|
|
|
// 这里概念完全一样:把"返回成功"、"返回失败"、"分页"等公共逻辑封装起来。
|
|
|
|
|
|
//
|
|
|
|
|
|
// 【继承关系】
|
|
|
|
|
|
// HwPortalControllerBase : ControllerBase
|
|
|
|
|
|
// ↑
|
|
|
|
|
|
// HwWebController, HwProductInfoController, ... (具体控制器)
|
|
|
|
|
|
//
|
|
|
|
|
|
// ControllerBase 是 ASP.NET Core 提供的控制器基类,
|
|
|
|
|
|
// 包含 Request、Response、User、HttpContext 等常用属性。
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
namespace Admin.NET.Plugin.HwPortal;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 门户控制器基类。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - abstract 抽象类】
|
|
|
|
|
|
/// abstract class 表示"抽象类",和 Java 的 abstract class 含义完全一致:
|
|
|
|
|
|
/// - 不能直接 new 实例化,只能被继承
|
|
|
|
|
|
/// - 可以包含抽象方法(没有实现体)和具体方法(有实现体)
|
|
|
|
|
|
/// - 子类必须实现所有抽象成员
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - ControllerBase】
|
|
|
|
|
|
/// ControllerBase 是 ASP.NET Core 提供的控制器基类。
|
|
|
|
|
|
/// 它提供了:
|
|
|
|
|
|
/// - Request:HTTP 请求对象
|
|
|
|
|
|
/// - Response:HTTP 响应对象
|
|
|
|
|
|
/// - User:当前登录用户信息
|
|
|
|
|
|
/// - HttpContext:HTTP 上下文
|
|
|
|
|
|
/// - Ok()、BadRequest()、NotFound() 等返回方法
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java Spring Boot:
|
|
|
|
|
|
/// Spring 的控制器通常继承或使用 @RestController 注解,
|
|
|
|
|
|
/// 方法参数可以注入 HttpServletRequest、HttpServletResponse 等。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ASP.NET Core 的方式更"面向对象":
|
|
|
|
|
|
/// 控制器本身就是请求上下文的载体,通过属性访问请求/响应。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【与 Java Spring Boot 的对比】
|
|
|
|
|
|
/// Java 若依的 BaseController:
|
|
|
|
|
|
/// public class BaseController {
|
|
|
|
|
|
/// protected AjaxResult success() { ... }
|
|
|
|
|
|
/// protected AjaxResult error() { ... }
|
|
|
|
|
|
/// protected TableDataInfo getDataTable(List<?> list) { ... }
|
|
|
|
|
|
/// }
|
|
|
|
|
|
///
|
|
|
|
|
|
/// C# 这里的设计几乎一样,只是语法不同。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ApiController]
|
|
|
|
|
|
// 【C# 语法知识点 - 特性(Attribute)】
|
|
|
|
|
|
// [ApiController] 是 ASP.NET Core 的特性,用于标记这是一个 API 控制器。
|
|
|
|
|
|
// 它会自动启用:
|
|
|
|
|
|
// 1. 自动 HTTP 400 响应(模型验证失败时)
|
|
|
|
|
|
// 2. 绑定源推断(自动从 body/query/route 绑定参数)
|
|
|
|
|
|
// 3. 问题详情响应(错误时返回标准格式)
|
|
|
|
|
|
//
|
|
|
|
|
|
// 对比 Java Spring Boot:
|
|
|
|
|
|
// Java 用 @RestController 注解,功能类似:
|
|
|
|
|
|
// @RestController
|
|
|
|
|
|
// public class MyController { ... }
|
|
|
|
|
|
//
|
|
|
|
|
|
// C# 的特性用方括号 [],Java 的注解用 @。
|
|
|
|
|
|
public abstract class HwPortalControllerBase : ControllerBase
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 返回成功结果(带数据)。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - protected 访问修饰符】
|
|
|
|
|
|
/// protected 表示"受保护的",只能在当前类或子类中访问。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 为什么用 protected?
|
|
|
|
|
|
/// 这些方法是给子控制器用的,不应该被外部直接调用。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java:
|
|
|
|
|
|
/// Java 的 protected 含义一样,但 C# 没有"包级访问"的概念。
|
|
|
|
|
|
/// Java 的 protected 还允许同一个包内的类访问,C# 不行。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【方法重载】
|
|
|
|
|
|
/// Success()、Success(data)、Success(msg)、Success(msg, data)
|
|
|
|
|
|
/// 这是"方法重载"(Overload):同名方法,参数不同。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java:
|
|
|
|
|
|
/// Java 的方法重载语法完全一样。
|
|
|
|
|
|
/// 但 C# 还支持"可选参数",可以减少重载数量:
|
|
|
|
|
|
/// protected HwPortalAjaxResult Success(string msg = "操作成功", object data = null)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 这里保持多个重载是为了和原 Java 代码风格一致。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">返回数据,默认为 null</param>
|
|
|
|
|
|
/// <param name="msg">返回消息,默认为 "操作成功"</param>
|
|
|
|
|
|
/// <returns>成功结果</returns>
|
|
|
|
|
|
protected HwPortalAjaxResult Success(object data = null, string msg = "操作成功")
|
|
|
|
|
|
{
|
|
|
|
|
|
return HwPortalAjaxResult.Success(msg, data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 返回成功结果(仅消息)。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="msg">返回消息</param>
|
|
|
|
|
|
/// <returns>成功结果</returns>
|
|
|
|
|
|
protected HwPortalAjaxResult Success(string msg)
|
|
|
|
|
|
{
|
|
|
|
|
|
return HwPortalAjaxResult.Success(msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 返回警告结果。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="msg">警告消息</param>
|
|
|
|
|
|
/// <param name="data">返回数据,默认为 null</param>
|
|
|
|
|
|
/// <returns>警告结果</returns>
|
|
|
|
|
|
protected HwPortalAjaxResult Warn(string msg, object data = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return HwPortalAjaxResult.Warn(msg, data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 返回错误结果。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="msg">错误消息</param>
|
|
|
|
|
|
/// <param name="code">错误码,默认为 500</param>
|
|
|
|
|
|
/// <returns>错误结果</returns>
|
|
|
|
|
|
protected HwPortalAjaxResult Error(string msg, int code = 500)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 【三元运算符】
|
|
|
|
|
|
// condition ? valueIfTrue : valueIfFalse
|
|
|
|
|
|
// 这里判断:如果 code 等于默认错误码,用简化方法;否则用带自定义码的方法。
|
|
|
|
|
|
return code == HwPortalAjaxResult.ErrorCode
|
|
|
|
|
|
? HwPortalAjaxResult.Error(msg)
|
|
|
|
|
|
: HwPortalAjaxResult.Error(code, msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据影响行数返回成功或失败结果。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【业务场景】
|
|
|
|
|
|
/// 在数据库操作中,增删改会返回"影响行数":
|
|
|
|
|
|
/// - rows > 0:表示操作成功,有数据被修改
|
|
|
|
|
|
/// - rows = 0:表示操作失败,没有数据被修改
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 这个方法把"影响行数"转换为"响应结果",简化控制器代码:
|
|
|
|
|
|
/// return ToAjax(await service.Insert(input));
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java 若依:
|
|
|
|
|
|
/// 若依的 BaseController.toAjax(int rows) 做同样的事情:
|
|
|
|
|
|
/// protected AjaxResult toAjax(int rows) {
|
|
|
|
|
|
/// return rows > 0 ? success() : error();
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="rows">数据库影响行数</param>
|
|
|
|
|
|
/// <returns>成功或失败结果</returns>
|
|
|
|
|
|
protected HwPortalAjaxResult ToAjax(int rows)
|
|
|
|
|
|
{
|
|
|
|
|
|
return HwPortalAjaxResult.FromRows(rows);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 导出 Excel 文件。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - 泛型方法 <T>】
|
|
|
|
|
|
/// protected IActionResult ExportExcel<T>(...)
|
|
|
|
|
|
/// 这里的 <T> 是泛型参数,表示"这个方法可以处理任何类型的数据"。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 调用示例:
|
|
|
|
|
|
/// ExportExcel<HwWeb>(list, "官网数据"); // 显式指定类型
|
|
|
|
|
|
/// ExportExcel(list, "官网数据"); // 类型推断,编译器自动识别
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java:
|
|
|
|
|
|
/// Java 的泛型方法写法类似:
|
|
|
|
|
|
/// protected <T> ResponseEntity exportExcel(List<T> source, String fileName)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 但 Java 的泛型有"类型擦除",运行时 T 只是 Object。
|
|
|
|
|
|
/// C# 的泛型在运行时是真实类型,性能更好。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">数据类型</typeparam>
|
|
|
|
|
|
/// <param name="source">数据列表</param>
|
|
|
|
|
|
/// <param name="fileName">文件名</param>
|
|
|
|
|
|
/// <returns>Excel 文件响应</returns>
|
|
|
|
|
|
protected IActionResult ExportExcel<T>(IReadOnlyList<T> source, string fileName)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ExcelHelper 是项目中的工具类,负责把列表导出为 Excel 文件。
|
|
|
|
|
|
// 返回的 IActionResult 会被框架转换为文件下载响应。
|
|
|
|
|
|
return ExcelHelper.ExportData(source, fileName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取分页数据(内存分页)。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【内存分页 vs 数据库分页】
|
|
|
|
|
|
/// 这个方法做的是"内存分页":先查出所有数据,再在内存中分页。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 优点:简单,不依赖数据库的分页语法
|
|
|
|
|
|
/// 缺点:数据量大时性能差
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 适用场景:数据量小(几百条以内),或者已经做过数据库分页的二次分页。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - IReadOnlyList<T>】
|
|
|
|
|
|
/// IReadOnlyList<T> 是"只读列表接口"。
|
|
|
|
|
|
/// 它比 List<T> 更安全,因为调用者不能修改列表内容(不能 Add/Remove)。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java:
|
|
|
|
|
|
/// Java 没有内置的只读列表接口,通常用 List<T> 或 Collections.unmodifiableList()。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">数据类型</typeparam>
|
|
|
|
|
|
/// <param name="source">完整数据列表</param>
|
|
|
|
|
|
/// <returns>分页后的数据包装对象</returns>
|
|
|
|
|
|
protected HwPortalTableDataInfo<T> GetDataTable<T>(IReadOnlyList<T> source)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 【获取分页参数】
|
|
|
|
|
|
// Request.Query 用来读取 URL 查询字符串,例如 ?pageNum=1&pageSize=20。
|
|
|
|
|
|
//
|
|
|
|
|
|
// 对比 Java Spring Boot:
|
|
|
|
|
|
/// Java 通常用 @RequestParam 注解获取:
|
|
|
|
|
|
/// public Result list(@RequestParam(defaultValue = "1") int pageNum,
|
|
|
|
|
|
/// @RequestParam(defaultValue = "10") int pageSize)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 这里直接从 Request.Query 读取,更灵活但需要手动处理默认值。
|
|
|
|
|
|
int pageNum = ParsePositiveInt(Request.Query["pageNum"], 1);
|
|
|
|
|
|
int pageSize = ParsePositiveInt(Request.Query["pageSize"], source.Count == 0 ? 10 : source.Count);
|
|
|
|
|
|
|
|
|
|
|
|
// 【计算跳过的记录数】
|
|
|
|
|
|
// skip = 需要跳过多少条。
|
|
|
|
|
|
// 这是最常见的内存分页算法:
|
|
|
|
|
|
// 第1页:skip = 0,取 1-10 条
|
|
|
|
|
|
// 第2页:skip = 10,取 11-20 条
|
|
|
|
|
|
// 第3页:skip = 20,取 21-30 条
|
|
|
|
|
|
//
|
|
|
|
|
|
// Math.Max(0, ...) 确保不会出现负数(比如 pageNum = 0 的情况)。
|
|
|
|
|
|
int skip = Math.Max(0, (pageNum - 1) * pageSize);
|
|
|
|
|
|
|
|
|
|
|
|
// 【LINQ 分页操作】
|
|
|
|
|
|
// source.Skip(skip).Take(pageSize).ToList()
|
|
|
|
|
|
//
|
|
|
|
|
|
// Skip(n):跳过前 n 条记录
|
|
|
|
|
|
// Take(n):取接下来的 n 条记录
|
|
|
|
|
|
// ToList():将结果转换为 List<T>
|
|
|
|
|
|
//
|
|
|
|
|
|
// 对比 Java Stream:
|
|
|
|
|
|
/// Java 写法:
|
|
|
|
|
|
/// list.stream().skip(skip).limit(pageSize).collect(Collectors.toList());
|
|
|
|
|
|
///
|
|
|
|
|
|
/// C# 的 LINQ 和 Java Stream 非常相似,都是函数式编程风格。
|
|
|
|
|
|
IReadOnlyList<T> rows = source.Skip(skip).Take(pageSize).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// 【对象初始化器】
|
|
|
|
|
|
// new HwPortalTableDataInfo<T> { Total = ..., Rows = ... }
|
|
|
|
|
|
// 这是"对象初始化器"语法,可以在创建对象时直接给属性赋值。
|
|
|
|
|
|
//
|
|
|
|
|
|
// 对比 Java:
|
|
|
|
|
|
/// Java 需要这样写:
|
|
|
|
|
|
/// HwPortalTableDataInfo<T> dto = new HwPortalTableDataInfo<>();
|
|
|
|
|
|
/// dto.setTotal(source.size());
|
|
|
|
|
|
/// dto.setRows(rows);
|
|
|
|
|
|
/// return dto;
|
|
|
|
|
|
///
|
|
|
|
|
|
/// C# 一行搞定,更简洁。
|
|
|
|
|
|
return new HwPortalTableDataInfo<T>
|
|
|
|
|
|
{
|
|
|
|
|
|
Total = source.Count,
|
|
|
|
|
|
Rows = rows
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取不分页数据(返回全部)。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">数据类型</typeparam>
|
|
|
|
|
|
/// <param name="source">数据列表</param>
|
|
|
|
|
|
/// <returns>数据包装对象(不分页)</returns>
|
|
|
|
|
|
protected HwPortalTableDataInfo<T> GetDataTableWithoutPaging<T>(IReadOnlyList<T> source)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new HwPortalTableDataInfo<T>
|
|
|
|
|
|
{
|
|
|
|
|
|
Total = source.Count,
|
|
|
|
|
|
Rows = source
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 解析逗号分隔的长整型数组。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【业务场景】
|
|
|
|
|
|
/// 前端传来的删除请求通常是:DELETE /api/users/1,2,3,4,5
|
|
|
|
|
|
/// 这里的 "1,2,3,4,5" 需要解析成 long[] 数组。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - static 静态方法】
|
|
|
|
|
|
/// protected static 表示"受保护的静态方法"。
|
|
|
|
|
|
/// 静态方法不依赖实例状态,可以直接通过类名调用。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 为什么用 static?
|
|
|
|
|
|
/// 这个方法不访问任何实例成员,只是做数据转换。
|
|
|
|
|
|
/// 标记为 static 可以让编译器优化,也方便子类直接调用。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="value">逗号分隔的字符串,如 "1,2,3"</param>
|
|
|
|
|
|
/// <returns>解析后的长整型数组</returns>
|
|
|
|
|
|
protected static long[] ParseLongArray(string value)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 【空值处理】
|
|
|
|
|
|
// string.IsNullOrWhiteSpace(value) 检查:
|
|
|
|
|
|
// - null
|
|
|
|
|
|
// - 空字符串 ""
|
|
|
|
|
|
// - 纯空白字符串 " "
|
|
|
|
|
|
//
|
|
|
|
|
|
// 对比 Java:
|
|
|
|
|
|
/// Java 需要手动判断:
|
|
|
|
|
|
/// if (value == null || value.trim().isEmpty()) { ... }
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 【Array.Empty<T>()】
|
|
|
|
|
|
// Array.Empty<long>() 返回一个共享的空数组实例。
|
|
|
|
|
|
// 比 new long[0] 更高效,不会每次都创建新对象。
|
|
|
|
|
|
//
|
|
|
|
|
|
// 这是一个小优化点:避免每次 new 空数组产生垃圾对象。
|
|
|
|
|
|
return Array.Empty<long>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 【LINQ 链式操作】
|
|
|
|
|
|
// value.Split(...):按逗号分割字符串
|
|
|
|
|
|
// .Select(long.Parse):把每个字符串解析成 long
|
|
|
|
|
|
// .ToArray():转换成数组
|
|
|
|
|
|
//
|
|
|
|
|
|
// 对比 Java Stream:
|
|
|
|
|
|
/// Java 写法:
|
|
|
|
|
|
/// Arrays.stream(value.split(","))
|
|
|
|
|
|
/// .map(Long::parseLong)
|
|
|
|
|
|
/// .toArray(Long[]::new);
|
|
|
|
|
|
///
|
|
|
|
|
|
/// C# 的 LINQ 更简洁,而且返回的是 long[] 而不是 Long[](值类型数组,更高效)。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 【StringSplitOptions】
|
|
|
|
|
|
/// RemoveEmptyEntries:移除空字符串(比如 "1,,2" 中的空项)
|
|
|
|
|
|
/// TrimEntries:去除每项的前后空白
|
|
|
|
|
|
/// | 是"位或"运算符,用于组合多个枚举标志。
|
|
|
|
|
|
return value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
|
|
|
|
.Select(long.Parse)
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 解析逗号分隔的字符串数组。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="value">逗号分隔的字符串</param>
|
|
|
|
|
|
/// <returns>解析后的字符串数组</returns>
|
|
|
|
|
|
protected static string[] ParseStringArray(string value)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
|
|
|
|
{
|
|
|
|
|
|
return Array.Empty<string>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 解析正整数(私有辅助方法)。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - int.TryParse】
|
|
|
|
|
|
/// int.TryParse 是"安全转换"方法:
|
|
|
|
|
|
/// - 转换成功:返回 true,out 参数得到转换结果
|
|
|
|
|
|
/// - 转换失败:返回 false,out 参数得到默认值 0
|
|
|
|
|
|
// 不会抛异常!
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java:
|
|
|
|
|
|
/// Java 的 Integer.parseInt 失败会抛 NumberFormatException:
|
|
|
|
|
|
/// try {
|
|
|
|
|
|
/// int parsed = Integer.parseInt(value);
|
|
|
|
|
|
/// if (parsed > 0) return parsed;
|
|
|
|
|
|
/// } catch (NumberFormatException e) {
|
|
|
|
|
|
/// // 处理异常
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// return fallback;
|
|
|
|
|
|
///
|
|
|
|
|
|
/// C# 的 TryParse 更优雅,不需要 try-catch。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 【C# 语法知识点 - out 参数】
|
|
|
|
|
|
/// out 关键字表示"输出参数",方法必须给它赋值。
|
|
|
|
|
|
///
|
|
|
|
|
|
// 调用语法:
|
|
|
|
|
|
/// if (int.TryParse(value, out int parsed)) { ... }
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 这里 out int parsed 是"内联变量声明":
|
|
|
|
|
|
/// 在调用方法的同时声明变量,C# 7.0 引入的特性。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 对比 Java:
|
|
|
|
|
|
/// Java 没有 out 参数的概念,通常用返回值包装类:
|
|
|
|
|
|
/// OptionalInt result = parseIntSafe(value);
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="value">字符串值</param>
|
|
|
|
|
|
/// <param name="fallback">失败时的默认值</param>
|
|
|
|
|
|
/// <returns>解析后的正整数,或默认值</returns>
|
|
|
|
|
|
private static int ParsePositiveInt(string value, int fallback)
|
|
|
|
|
|
{
|
|
|
|
|
|
// int.TryParse(value, out int parsed) 尝试把字符串解析成整数。
|
|
|
|
|
|
// && parsed > 0 确保是正整数。
|
|
|
|
|
|
if (int.TryParse(value, out int parsed) && parsed > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return parsed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析失败或不是正整数时,返回默认值。
|
|
|
|
|
|
return fallback;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|