【问题标题】:Type restriction for class members in case/whencase/when 类成员的类型限制
【发布时间】:2018-01-26 23:35:58
【问题描述】:

假设我有以下课程 Message 代表聊天中的消息。

class Message
    JSON.mapping({
        message_id: {type: Int32},
        text:       {type: String, nilable: true},
        photo:      {type: Photo, nilable: true},
        sticker:    {type: Sticker, nilable: true},
    })

    MESSAGE_TYPES = %w( text photo sticker )

    {% for type in MESSAGE_TYPES %}
        def {{ type.id }}?
        ! {{ type.id }}.nil?
        end
    {% end %}
end

每条消息都可以是文字、照片或贴纸。但只有其中之一。例如,如果 message 是文本,则 photosticker 属性为 nil,但 text 不是 nil。如果 message 是贴纸,则只有 sticker 不是 nil。等等。真实类有更多的直接类型和推断类型。

收到此消息后,我想以特定类型的特定方式对其进行处理。比如:

case message
when .text?
    p message.text
when .photo?
    p message.photo.file_id
when .sticker?
    p message.sticker.file_id
end

当然不行,因为在每个when 子句中对应的属性都是nilable 和undefined method 'file_id' for Nil

有没有办法在每种情况下限制变量类型,确保 message.sticker? 然后 message.sticker 不是 nil 并且 file_id 存在?

也许我在非常接近问题方面完全错了,还有更好、更简洁的编码方式吗?

【问题讨论】:

    标签: crystal-lang


    【解决方案1】:
    require "json"
    
    class Photo
      JSON.mapping(url: String)
    end
    
    struct Message
      JSON.mapping(id: Int32, message: String|Photo)
    end
    
    p Message.from_json(%({"id": 5, "message": {"url": "http://example.org/"}}))
    

    【讨论】:

      【解决方案2】:

      您可以按照 Oleh Prypin 的建议将所有有效负载对象放在一个具有联合类型的实例变量中。 另一种选择,如果你想保持当前的数据布局,你可以让方法返回各自的 Type 或 nil 并将其值分配给一个变量:

      if text = message.text?
        p text
      if photo = message.photo?
        p photo.file_id
      if sticker = message.sticker?
        p sticker.file_id
      end
      

      【讨论】:

      • 这真是个好主意。但是我还是一头雾水,我可以声明一个新类型只继承父类 JSON 映射的一部分,还是必须在每个子类型中声明一个完整的映射列表?
      • Message 类中只有一个 JSON 映射,就像您的示例一样。
      猜你喜欢
      • 2018-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多