Solidity 合约设计与功能 8| 数据结构--枚举 (Enum)

在 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);
    }
}

主要说明

  1. 枚举OrderStatus 定义了题目要求的六个状态,None 作为默认/初始占位状态。
  2. 结构体Order 包含 buyer 地址和当前 status
  3. 订单存储:使用动态数组 orders 存储所有订单,每个订单的索引即为它的 ID。
  4. 功能实现
    • 添加订单addOrder 将新订单设为 Pending 状态并返回订单 ID。
    • 更新状态updateOrderStatus 通过订单 ID 修改状态。
    • 获取状态getOrderStatus 返回指定订单的状态。
    • 重置状态resetOrderStatus 将指定订单状态设为 None
  5. 事件:添加了 OrderAddedOrderStatusUpdated 事件,便于 DApp 监听状态变化。
  6. 安全考虑:所有需要订单 ID 的函数都使用 require 检查索引是否有效,防止数组越界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值