只是为了好玩 - 我可以为特定用途编写自定义 NilClass 吗?
编辑 |或者关于同一对象主题的另一个问题。我可以编写自己的类定义来使以下所有内容正常工作吗?
o = WeirdObject.new
puts "Object o evaluates as true in boolean expressions" if o # Line never output
puts "Object o is nil?" if o.nil? # Line is output
我可以轻松地完成 nil?
事情,因为这只是一个方法。但不知道如何使其在布尔表达式(包括最基本的表达式“o”)中计算为非真值。
最初的问题如下...
更多出于好奇我真的可以使用 Ruby (1.9.2) 获得多少乐趣...
当用户未登录时,我希望能够使用 unless @user
或 except @user.nil?
等,但同时,我希望能够调用 User
的方法在此对象上,并让它们返回相同的 NilClass
(ala Objective-C),因为在大多数地方,这将消除对样板 if @user
代码的需要,同时仍然起作用就像布尔表达式中的 nil
一样。
我发现您无法对 NilClass
进行子类化,因为它没有 #new
方法。您可以直接向实例添加方法,但它是单例,因此如果我想观察此行为的唯一地方是非登录用户,这会导致问题。
是否可以?
我的意思是这样的:
class CallableNil < NilClass
def method_missing(meth, *args, &block)
self
end
end
user = CallableNil.new # or .singleton, or something
puts "Got here" unless user # Outputs
puts "And here" unless user.nil? # also outputs
puts user.username # no-op, returns nil
这基本上就是 Objective-C 处理 nil 的方式,并且在某些情况下很有用。
我想我不能子类 NilClass 并只实现会导致它在布尔表达式中为 false 的方法?
编辑 |哈哈,如果你重新定义顶级 NilClass
,你真的会严重破坏 Ruby...它根本停止响应任何方法调用。
>> class NilClass
>> def method_missing(meth, *args, &block)
>> self
>> end
>> end
>>
?> nil.foo
>>
?>
?> puts "here"
>> quit
>> # WTF? Close dammit!
EDIT | Or another question, on the same object subject. Can I write my own class definition that would cause the following all to work?
o = WeirdObject.new
puts "Object o evaluates as true in boolean expressions" if o # Line never output
puts "Object o is nil?" if o.nil? # Line is output
I can do the nil?
thing easy, since that's just a method. But no idea how to make it evaluate as a non-true value in a boolean expression (including just the most basic expression "o").
Original question follows...
More out of curiosity as to how much fun I can have with Ruby (1.9.2) really...
When a user is not logged in, I'd like to be able to detect it with unless @user
or unless @user.nil?
etc, but at the same time, I'd like to be able to call the methods of User
on this object and have them return the same NilClass
(ala Objective-C), since in most places this would remove the need for boilerplate if @user
code, while still acting like nil
in boolean expressions.
I see that you can't subclass NilClass
, since it doesn't have a #new
method. You can add methods directly to the instance, but it's a singleton, so this would cause issues if the only place I wanted to observe this behaviour was with non-logged-in users.
Is it possible?
What I mean, is something like this:
class CallableNil < NilClass
def method_missing(meth, *args, &block)
self
end
end
user = CallableNil.new # or .singleton, or something
puts "Got here" unless user # Outputs
puts "And here" unless user.nil? # also outputs
puts user.username # no-op, returns nil
This is basically how Objective-C handles nil and it's useful in some circumstances.
I guess I could not subclass NilClass and just implement the methods that would cause it to be false in boolean expressions?
EDIT | Haha, you really break Ruby pretty badly if you redefine the top-level NilClass
... it stops responding to any method call at all.
>> class NilClass
>> def method_missing(meth, *args, &block)
>> self
>> end
>> end
>>
?> nil.foo
>>
?>
?> puts "here"
>> quit
>> # WTF? Close dammit!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您的主要要求是能够调用
nil
值的方法而不引发异常(或必须检查它是否已定义),则ActiveSupport
添加Object#try
这使您能够执行此操作:正如你所看到的,搞砸了
NilClass
有一些有趣的副作用。If your primary requirement is to be able to call methods on a
nil
value without raising an exception (or having to check that it's defined),ActiveSupport
addsObject#try
which gives you the ability to do this:As you've seen, screwing around with
NilClass
has some interesting side-effects.另一个潜在的解决方案(非线程安全):
Another potential solution (not thread safe):
一般来说,不建议以这种方式更改
nil
对象 -- 快速失败。我建议使用 egonil 或 andand 宝石。有关此主题的更多阅读/链接 [并且为了好玩;)],请查看 这篇博文。Generally, it's not recommended to change the
nil
object in this way -- fail-fast. I'd recommend to use the egonil or the andand gem. For some more reading/links on this topic [and for fun ;)], checkout this blog post .我今天正在寻找一个非常相似的东西,普遍的共识是,在不破坏一切的情况下将“nil”变成“null”是“不可能”的。所以,这就是你如何做到不可能的事情:(
如果格式搞砸了,请原谅,我是在 iPhone 上做的)
这会添加一个“null!”一般情况下,nil 和所有对象的方法。对于普通对象,这是一个无操作,并且一切正常(包括在缺少方法时抛出异常)。然而,对于 nil 来说,无论链中有多少个方法,它都不会抛出异常,并且它仍然会精确地计算为 nil(因为它仍然只是一个普通的 nil)。新行为的范围仅限于“null!”所在的单行。被称为。之后 nil 恢复到正常行为。
它通过遍历调用树并标记不是它自己的代码的第一个文件/行,并将其用作标志来指示它现在处于“空”模式来实现这一点。只要文件/行不改变,它就会继续表现得像“null”。一旦它发现解释器已经移动,它就会将自己重置回正常行为。
此方法的注意事项是:
您的语句必须全部放在一行中。
你的语句可能必须有一个实际的文件和行号(我没有在IRB中测试过它,但我怀疑它不会工作)。如果没有这些,它就无法知道它仍在同一行上执行,所以它可能会立即恢复正常。
新行为将继续影响其他 nil,直到该行结束。您可以通过调用“nil!”来强制它再次正常运行。不过,稍后。例如:
obj.null!.foo.bar.nil! || normal.nil.behavior
成功的方法调用在其自己的主体中执行的任何操作,这些操作针对 nil 调用不存在的方法或调用“null!”可能会重置行为,因为这些行为发生在其他行号,但是从理论上讲,除非您正在做非常奇怪的事情,否则这不应该成为问题。
如果你想要这种“有趣”的东西,我在 gemcutter 上有一个名为“mobj”的 gem,里面有一堆古怪的黑客,或者从这里获取源代码:
https://github.com/gnovos/mobj
我有另一个 gem,可以以不同的方式做到这一点,如果你想要一个更强大的解决方案(并且不'不介意将东西包装成块):
然后,在您想要这种行为的任何地方;
我还没有测试过最后一个,但它可能会起作用。如果您对此感兴趣,可以在这里找到 ctx:
https://github.com/gnovos/ctx
干杯!
I was looking for a very similar thing today and the general consensus is that it's "impossible" to turn "nil" into "null" without breaking everything. So, this is how you do impossible:
(Forgive the formatting if it's screwed up, I'm doing this on an iPhone)
This adds a "null!" method to both nil and all objects in general. For normal objects, it's a no-op, and things work as normal (including throwing exceptions when methods are missing). For nil, however, it won't throw exceptions, no matter how many methods are in the chain, and it will still evaluate exactly as nil (since it's still just a normal nil). The new behavior is scoped only to the single line from which "null!" was called. Afterwards nil returns to it's normal behavior.
It achieves this by walking up the call tree and marking the first file/line that isn't it's own code, and using that as a flag to indicate that it is now in "null" mode. As long as the file/line don't change, it will continue to act like a "null". As soon as it sees that the interpreter has moved on, it resets itself back to normal behavior.
The caveats for this approach are:
Your statement must all fit on a single line.
Your statement probably must have an actual file and line number (I've not tested it in IRB, but I suspect it will not work).. Without these it can't tell that it's still executing on the same line, so it will probably revert to normal right away.
The new behavior will continue to affect other nils until the end of the line. You can force it to act like normal again by calling "nil!" later in the line, though. For example:
obj.null!.foo.bar.nil! || normal.nil.behavior
Anything that successful method calls do in their own bodies that call non existant methods against nil or call "null!" will probably reset the behavior since those take place at other line numbers, but, in theory, that shouldn't be a problem unless you are doing very weird things.
If you'd like this kind of "fun" stuff, I have a gem called "mobj" on gemcutter with a bunch of this wacky hackery, or pull the source from here:
https://github.com/gnovos/mobj
I have another gem that could do this in a different way, if you want a more robust solution (and don't mind wrapping things in blocks):
And then, anywhere that you want this behavior;
I haven't tested this last one, but it will probably work. If this interests you, you can find ctx here:
https://github.com/gnovos/ctx
Cheers!