编辑:对于那些批评我更换self
的意图的人,你可以自由点击后退按钮,继续发展自己的意见,让我与我一起发展:)
我想知道是否有办法彻底删除self
引用的对象并将其替换为新实例。
示例:
def refresh_from_server!
self = newly_fetched_object_from_server
end
我不想return
新对象。
似乎我必须构建自己的复制界面并调用self.copy_from(other_object)
,但也许某人有一个很酷的红宝石位分享效果更好!
- 编辑
由于有些人似乎不清楚这个问题,我希望instance.my_method!
用该类的新实例完全替换instance
例如,假设我们有一个类
class Counter
attr_accessor :count
def initialize
count = 0
end
def reset!
# This is what I want to achieve.
# Obviously in this case it would be trivial to write `self.count = 0`
# Or return a new value
# But I have a much more complex object in real life
# which copying would not be trivial
# All I'm looking for is a bit of stylistic sugar to make my code look cooler
# If it doesn't exist, I would love to know why
self = Counter.new
end
def up
count += 1
end
end
答案 0 :(得分:1)
不,您无法替换self
。您只能更改其部分/全部状态,但对象引用将保持不变。
无论如何,你为什么要这样做?如果你只是想捎带你的初始化逻辑(在我看来就是这种情况),一些重构将有所帮助:只需从两个地方调用共享方法。
class Counter
attr_accessor :count
def initialize
init_state
end
def reset!
init_state
end
def up
self.count += 1
end
private
def init_state
self.count = 0
end
end
答案 1 :(得分:0)
正如其他人已经指出的那样,self
无法从封闭的实例中替换。如果需要用新实例替换实例,则需要从外部完成,例如在注册其类实例的类工厂中。
Bellow是使用委托人的最简单的例子,展示了我的意思。 SimpleDelegator代表Counter实例的简单包装:
require 'delegate'
class Counter
attr_accessor :count
def initialize
@count = 0
end
end
class CounterDecorator < SimpleDelegator
def reset!
__setobj__(__getobj__.class.new)
end
end
c = CounterDecorator.new(Counter.new)
p c.__getobj__.object_id
c.count = 123
p c.count
c.reset!
p c.__getobj__.object_id
p c.count
# produces following output
20131160
123
20130900
0
答案 2 :(得分:0)
尽管问题很旧,但仍然可以访问。我将尝试在“ 为什么不能用Ruby代替自己?”中的“ 为什么”中详细阐述。
self
在这种情况下的使用
https://web.archive.org/web/20191217060940/https://www.honeybadger.io/blog/ruby-self-cheat-sheet/
在各种上下文中都可以使用self
。您的问题是在实例方法的上下文中使用它,因此我将重点介绍它。
例如这种情况:
class SomeClass
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
a = SomeClass.new
a.some_method
# prints : SomeClass - 47013616336320 - #<SomeClass:0x000055846bcd7b80>
请注意,self
还有其他用法:例如它在类定义的范围内引用Class
对象。例如
class SomeClass
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
# prints : Class - 47102719314940 - SomeClass
替换self
下面的代码演示了您的期望/期望(据我所知):
class Counter
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
def refresh!
self = Counter.new # not possible
# results in error : "Can't change the value of self"
end
end
a = Counter.new
a.some_method
# prints : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish a to point to a different object
但是其他参考呢?例如。假设您想要:
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish both a and b to point to the same (new) object
如果这样说,它会提示为什么不这样做。
为什么我们不能替换self
简短的回答是,它根本不是语言/解释器提供的东西。关于推理:@matthewd在this answer中以某种方式回答了问题:
所有ruby变量引用本质上都是指针(但不是 指针),用C语言来表达。
您可以对一个对象进行突变(假设它不是不变的),并且所有 引用它的变量将因此指向相同的位置(现在 变异)对象。但是改变变量的唯一方法是 指的是直接分配给该变量-每个变量 变量是一个单独的引用;您不能为单个参考添加别名 有两个名字。
简而言之:变量中可能有其他对该对象的引用,这些引用不在实例方法的范围内。这些不能被该实例方法操纵。
一种达到预期效果的方法
如果您想获得这种效果,而只想触摸Counter
的代码,则可以将所有方法和状态移到内部类Counter::Inner
上,并使Counter
的行为像去耦引用一样。 Counter
的唯一“状态”将是对Counter::Inner
对象的引用,而Counter
可以通过method_missing
方法将接收到的所有调用委派给该引用。如果您使用的是refresh!
,则可以像现在打算替换Counter
一样替换self
中的引用。现在,所有外部代码将间接使用新的Counter:Inner
实例。
class Counter
class Inner
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
def initialize(*args)
@reference = Inner.new(*args)
end
def method_missing(method_id, *args)
@reference.send(method_id, *args)
end
def refresh!
@reference = Inner.new
end
end
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238242100 - #<Counter::Inner:0x0000557a00203e68>
a.refresh!
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238240000 - #<Counter::Inner:0x0000557a00202e00>
仅是档案馆的一个答案:-)我希望这能为将来的访问者提供有用的见识。