在 Solidity 中,枚举(enum) 是一种用户自定义的数据类型,用于定义一组具有名字的常量。它能让代码更具可读性和可维护性,尤其适合表示有限的状态集合,如订单状态、流程阶段、选项等。
1. 定义枚举
枚举使用 enum 关键字定义,通常放在合约内部或库中。枚举成员默认为从 0 开始的无符号整数值,后续成员依次递增。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract EnumExample {
// 定义枚举 Status,包含三个状态
enum Status {
Pending, // 0
Shipped, // 1
Delivered, // 2
Canceled // 3
}
}
- 成员之间用逗号分隔。
- 成员名称通常采用大写字母开头(与变量区分),但非强制。
- 枚举成员数量最多为 256 个(因为值范围 0~255,但 Solidity 内部会根据成员数量选择合适的整数类型,最多支持到 256?实际文档说明最多 256 个成员)。
2. 枚举在Solidity中的应用
- 状态变量:将枚举用作状态变量,例如
status public status;,以此跟踪合约中的状态。 - 结构体中的枚举:在结构体中使用枚举来表示特定的属性,如订单结构体中的发货状态。
- 数组中的结构体:创建结构体的数组,例如订单数组,用以存储多个订单信息。
2.1 声明和赋值
Status public currentStatus; // 默认值为第一个成员 Status.Pending
function setStatus(Status _status) public {
currentStatus = _status;
}
2.2 比较
function isDelivered() public view returns (bool) {
return currentStatus == Status.Delivered;
}
2.3 作为函数参数和返回值
function getStatus() public view returns (Status) {
return currentStatus;
}
function updateToNext() public {
// 需要先将枚举转换为 uint 进行算术运算,再转回枚举
require(currentStatus != Status.Canceled, "Terminal state");
currentStatus = Status(uint(currentStatus) + 1);
}
3. 枚举操作的函数示例
- 读取枚举状态:编写函数返回当前的枚举状态,例如 get() 函数返回 status 。
- 设置枚举状态:通过输入参数设置枚举的状态,如 set(status _status) 函数。
- 修改枚举状态到特定值:编写函数直接将状态设置为特定枚举值,如将状态更新为已发货。
- 重置枚举状态:使用 delete 关键字将枚举重置为默认值,通常是定义时的第一个枚举值。
完整示例:简单订单状态机
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract OrderManager {
enum State { Created, Paid, Shipped, Completed, Canceled }
struct Order {
uint256 id;
address customer;
State state;
}
Order[] public orders;
mapping(uint256 => uint256) private idToIndex; // id -> 数组索引(+1)
event OrderCreated(uint256 indexed id, address customer);
event OrderStateChanged(uint256 indexed id, State newState);
function createOrder(uint256 _id) external {
require(idToIndex[_id] == 0, "Order ID already exists");
orders.push(Order(_id, msg.sender, State.Created));
idToIndex[_id] = orders.length; // 存储索引+1
emit OrderCreated(_id, msg.sender);
}
function transitionTo(uint256 _id, State _newState) external {
uint256 index = idToIndex[_id];
require(index > 0, "Order not found");
Order storage order = orders[index - 1];
require(order.customer == msg.sender, "Not the customer");
// 状态转换逻辑(示例仅允许顺序推进,但可自定义)
require(_newState > order.state, "Invalid state transition");
require(_newState != State.Canceled, "Use cancel function");
order.state = _newState;
emit OrderStateChanged(_id, _newState);
}
function cancelOrder(uint256 _id) external {
uint256 index = idToIndex[_id];
require(index > 0, "Order not found");
Order storage order = orders[index - 1];
require(order.customer == msg.sender, "Not the customer");
require(order.state < State.Completed, "Cannot cancel completed order");
order.state = State.Canceled;
emit OrderStateChanged(_id, State.Canceled);
}
function getOrderState(uint256 _id) external view returns (State) {
uint256 index = idToIndex[_id];
require(index > 0, "Order not found");
return orders[index - 1].state;
}
}
注意事项
- 枚举成员数量限制:最多 256 个成员(因为
uint8最大 255,但 Solidity 支持最多 256 个成员,最后一个值为 255)。 - 不要依赖枚举成员的整数值:除非显式指定,否则枚举值可能因添加或删除成员而改变。如需固定值,可手动指定(如
enum Status { Pending = 1, Active = 2 }),但必须确保值唯一且不重叠。 - 枚举不能包含超过 256 个成员:但实际业务中很少需要那么多状态。
- 枚举的可见性:枚举本身没有可见性修饰符,但包含枚举的状态变量或函数可以有可见性。
- 枚举与库:枚举可以定义在库中,供多个合约共享。
编程作业
-
任务:编写一个Solidity智能合约,实现一个简单的订单处理系统。
-
声明一个名为 OrderStatus 的枚举,包括状态: None , Pending , Shipped , Completed , Rejected , Cancelled 。
-
创建一个结构体 Order ,包含买家地址和订单状态。
-
实现功能:
i. 添加新订单到数组。
ii. 更新订单状态。
iii. 获取特定订单的状态。
iv. 重置订单状态到默认值。
-
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract OrderSystem {
// 订单状态枚举
enum OrderStatus { None, Pending, Shipped, Completed, Rejected, Cancelled }
// 订单结构体
struct Order {
address buyer; // 买家地址
OrderStatus status; // 当前状态
}
// 存储所有订单的动态数组
Order[] public orders;
// 事件:订单添加时触发
event OrderAdded(uint indexed orderId, address indexed buyer, OrderStatus status);
// 事件:订单状态更新时触发
event OrderStatusUpdated(uint indexed orderId, OrderStatus oldStatus, OrderStatus newStatus);
/**
* @dev 添加新订单,初始状态为 Pending
* @param _buyer 买家地址
* @return 新订单的 ID(数组索引)
*/
function addOrder(address _buyer) public returns (uint) {
Order memory newOrder = Order({
buyer: _buyer,
status: OrderStatus.Pending
});
orders.push(newOrder);
uint orderId = orders.length - 1;
emit OrderAdded(orderId, _buyer, OrderStatus.Pending);
return orderId;
}
/**
* @dev 更新指定订单的状态
* @param _orderId 订单 ID
* @param _status 新状态
*/
function updateOrderStatus(uint _orderId, OrderStatus _status) public {
require(_orderId < orders.length, "Order does not exist");
Order storage order = orders[_orderId];
OrderStatus oldStatus = order.status;
order.status = _status;
emit OrderStatusUpdated(_orderId, oldStatus, _status);
}
/**
* @dev 获取指定订单的当前状态
* @param _orderId 订单 ID
* @return 订单状态
*/
function getOrderStatus(uint _orderId) public view returns (OrderStatus) {
require(_orderId < orders.length, "Order does not exist");
return orders[_orderId].status;
}
/**
* @dev 将指定订单的状态重置为默认值(None)
* @param _orderId 订单 ID
*/
function resetOrderStatus(uint _orderId) public {
require(_orderId < orders.length, "Order does not exist");
Order storage order = orders[_orderId];
OrderStatus oldStatus = order.status;
order.status = OrderStatus.None;
emit OrderStatusUpdated(_orderId, oldStatus, OrderStatus.None);
}
}
主要说明
- 枚举:
OrderStatus定义了题目要求的六个状态,None作为默认/初始占位状态。 - 结构体:
Order包含buyer地址和当前status。 - 订单存储:使用动态数组
orders存储所有订单,每个订单的索引即为它的 ID。 - 功能实现:
- 添加订单:
addOrder将新订单设为Pending状态并返回订单 ID。 - 更新状态:
updateOrderStatus通过订单 ID 修改状态。 - 获取状态:
getOrderStatus返回指定订单的状态。 - 重置状态:
resetOrderStatus将指定订单状态设为None。
- 添加订单:
- 事件:添加了
OrderAdded和OrderStatusUpdated事件,便于 DApp 监听状态变化。 - 安全考虑:所有需要订单 ID 的函数都使用
require检查索引是否有效,防止数组越界。

1451

被折叠的 条评论
为什么被折叠?



