如何使自定义Ruby类型表现得像字符串?

时间:2011-07-19 14:26:23

标签: ruby

如果我有一个表示某种字符串类型的自定义Ruby类,如

class MyString
end

为了使以下用例成为可能,我应该实现哪些功能:

  1. 每当需要MyString时传递Ruby字符串
  2. 每当需要Ruby字符串时传递MyString
  3. 将Ruby字符串与MyString值进行比较(无论我使用s == t还是t == s都无关紧要。)
  4. 我已经看到了各种有趣的函数,例如to_scmp==eq,但是当我们调用每个函数时,我都不清楚。

    我的具体用例是我正在使用C API编写一个Ruby扩展,它公开了获取(并返回)自定义字符串类型(QString的值的函数),我的扩展也注册了。但是,我想让这些自定义字符串尽可能直观。不幸的是,我不能只从我的C代码中返回Ruby字符串,因为它应该可以在字符串上调用Qt方法。

3 个答案:

答案 0 :(得分:6)

至少有三种方法:

  1. class MyString < String; ...; end
  2. 定义#to_s
  3. 定义#to_str
  4. 同时执行#2和#3将使对象非常像真正的String,即使它不是子类。

    #to_s是一个显式转换器,意味着它必须出现在Ruby代码中才能工作。

    #to_str是一个隐式转换器,意味着Ruby解释器会在需要String时尝试调用它,但是会给出其他内容。

    更新

    以下是您可以使用to_str获得一些乐趣的示例:

    begin
      open 1, 'r'
    rescue TypeError  => e
      p e
    end
    class Fixnum
      def to_str; to_s; end
    end
    open 1, 'r'
    

    运行时,第一次打开失败并显示TypeError,但第二次打开则会查找1

    #<TypeError: can't convert Fixnum into String>
    fun.rb:9:in `initialize': No such file or directory - 1 (Errno::ENOENT)
        from fun.rb:9:in `open'
    

答案 1 :(得分:1)

尽管将String子类赋予它一个新的initialize方法来导入这些QString类型的字符串很诱人,但你可能只想添加一个有助于转换的String扩展,这样你就不会不得不重新实现String本身的版本。

例如,使用两种方法你几乎可以完成这项工作:

class String
  def self.from_qstring(qstring)
    new(...)
  end

  def to_qstring
    # ...
  end
end

在开始比较它们之前,为String提供多种存储类型不会成为问题,但鉴于Ruby的String非常强大,编写类似的工作很困难。

答案 2 :(得分:0)

继承Ruby中其他人构建的类通常不是一个好主意,因为太多事情可能会出错。 (例如,您可能会在不知情的情况下覆盖内部方法。)

1)定义Mystring.to_s以获得从Mystring到String的自动转换。

2)不确定你的意思。如果你想要一个返回Mystring的String方法,你将不得不修补String:

Class String
  def to_mystring
    return Mystring.new(self)
  end
end

3)得到t == s(假设s是String的实例,t是Mystring的一个实例)定义&lt; =&gt;。为了获得s == t,你将不得不再次使用补丁字符串。