这是一个奇怪的问题,但我不太清楚如何查找它。在我们的项目中,我们已经有了一个“转变”的概念。有一段代码如下:
foo.shift
在这种情况下,很容易将其视为尝试访问对象shift
的{{1}}变量。但它也可能是foo
。有没有办法指定我们期望该方法属于哪个类?我尝试了以下变体:
Array#shift
使更明显的方法被调用,但我不能让它工作。有没有办法更明确地说明您尝试调用的方法属于哪个类以帮助代码可读性?
答案 0 :(得分:3)
从根本上讲,你不应该担心这类事情,你绝对不能告诉数组shift
方法对除了数组对象。许多核心Ruby类都是用C实现的,并且具有通常依赖于存在的特定内部的优化。我们采取了一些安全措施来防止您尝试做一些过于疯狂的事情,比如重新绑定并随意应用这种方法。
这里有两个" shifty"对象有助于说明现实世界的情况以及如何应用:
class CharacterArray < Array
def initialize(*args)
super(args.flat_map(&:chars))
end
def inspect
join('').inspect
end
end
class CharacterList < String
def shift
slice!(0, 1)
end
end
你可以将Array#shift
粉碎到第一个,它将通过纯粹的机会工作,因为你正在处理一个数组。它不会使用第二个,因为它不是数组,它缺少shift
方法可能依赖的重要方法。
在实践中,使用它并不重要,它们都是相同的:
list_a = CharacterArray.new("test")
list_a.shift
# => "t"
list_a.shift
# => "e"
list_a << "y"
# => "sty"
list_b = CharacterList.new("test")
list_b.shift
# => "t"
list_b.shift
# => "e"
list_b << "y"
# => "sty"
这些都实现了相同的界面,它们都产生相同的结果,并且就你所关注的那样,作为调用者,这是足够好的。这是Duck Typing的基础,这是Ruby深深接受的哲学。
如果您尝试重新绑定CharacterList
上的重新绑定技巧,它最终会遇到麻烦,它就无法正常工作,但就接口而言,该课程会满足您的所有期望。
编辑:正如Sergio指出的那样,你不能使用重新绑定技术,Ruby会突然爆炸:
Array.instance_method(:shift).bind(list_b).call
# => Error: bind argument must be an instance of Array (TypeError)
如果可读性是目标,那么比list_b.shift
多35个字符,这通常会在错误的方向上显着。
答案 1 :(得分:1)
在评论中进行一些讨论后,一个解决方案是:
Array.instance_method(:shift).bind(foo).call
超级难看,但是我想要的是完全指定实际调用哪个实例方法的想法。替代方案是将变量重命名为foo_array
或将其称为foo.to_a.shift
。
这很难的原因是Ruby不是强类型的,而这个问题就是试图为它带来更强的类型。这就是解决方案的原因!感谢大家的投入!