Project1

标题: 今天被ruby摆了一道……查了一天的bug [打印本页]

作者: 雪在燃    时间: 2016-9-6 17:02
标题: 今天被ruby摆了一道……查了一天的bug
c#转型rgss3的新人脚本君
今天正在愉快的阅读代码之中,偶然看到这样一句代码
  1.   def create_contents
  2.     contents.dispose
  3.     if contents_width > 0 && contents_height > 0
  4.       self.contents = Bitmap.new(contents_width, contents_height)
  5.     else
  6.       self.contents = Bitmap.new(1, 1)
  7.     end
  8.   end
复制代码

哦简单,这里的contents就是引用父类的属性吧,省略了self而已
所以我加了self,果然如我所料,没有任何问题
所以这里的self.contents到底为什么不简化一下呢,所以我改成了contents = Bitmap.new(contents_width, contents_height)
开始报错了……
于是我搜索,self和this的区别(我怀疑self比this有不同的用法,因为在这里self如果和this用法一样,且可以省略的话,这样的修改应该没有问题的)



查了无数代码,p了多少次地址,我都确定 p self.contents 和p contents是一个东西了……
最后,我灵感来了!
  1.       contents = Bitmap.new(contents_width, contents_height)
  2.       p contents
  3.       p self.contents
复制代码

新思路的检测代码
果然……
我去,这里的contents是被声明为了临时变量,所以才会报bug

一下午的光阴就在这坑爹的bug中度过了……

作者: 喵呜喵5    时间: 2016-9-6 18:07
def a
  2
end
def b
  p a
  a = 3
  p a
end
b
b
作者: 雪在燃    时间: 2016-9-6 18:15
喵呜喵5 发表于 2016-9-6 18:07
def a
  2
end

我的天,你想让我疯掉吗
为什么会这样……
等等,又是声明了个临时变量
我差点又歪到可以修改方法上了
一瞬间的心理历程↑
作者: RyanBern    时间: 2016-9-6 19:56
本帖最后由 RyanBern 于 2016-9-7 09:00 编辑

从别的面向对象语言转过来 Ruby 学起来是有点别扭的。建议的做法是少类比,忘掉从前学过的东西,直接从 Ruby 入手。

Ruby 中,'.'后面出现的一般都是方法,很少有“属性”这个说法。这个和 C# 专门引入了 Property 是不太一样的。另外,C# 的 this 和 Ruby 的 self 差的还是有点远。具体的我也不清楚,可以询问@taroxd 来解释。

楼主的代码中不能认为 self.contents 和 contents 相同,也不能认为 self.contents = 和 contents = 相同。
self.contents 和 self.contents= 这两个都是在调用方法,虽然看起来和 C# 中的 Property 极为类似,但是本质上有很大不同。

当你在这里写下 contents 时,Ruby 解释器会先找一下有没有临时变量叫 `contents',如果找不到,则找有没有该类的方法叫 `contents',如果还找不到则报错。

当你在这里写下 contents= 时,这句话实际是在赋值,而写下 self.contents= 时,这句话是在调用本类的 `contents=' 方法,所以效果是截然不同的。

另外,我对此表示疑问:
CSHARP 代码复制
  1. public int X{ get; set; }
  2. public void test(){ X = 5; }

这样真的合法吗?当使用 C# 的 Property 时,this 真的能省略吗?


有点忘了 C# 了,试了一下应该是可以的。
作者: 寒冷魔王    时间: 2016-9-6 21:38
本帖最后由 寒冷魔王 于 2016-9-6 21:39 编辑

原先给Chd114神查过一个离奇Bug(见下面的func2)
RUBY 代码复制
  1. class A
  2.   def initialize
  3.     @x = 0
  4.   end
  5.   def x=(e)
  6.     @x = e
  7.   end
  8.   def x
  9.     @x
  10.   end
  11.   # 注:attr_accessor :x 同理
  12.   def func0
  13.     self.x = 5
  14.   end
  15.   def func1
  16.     x = 5
  17.   end
  18.   def func2
  19.     p x
  20.     if false
  21.       x = 5
  22.     end
  23.     p x
  24.   end
  25. end
  26.  
  27. puts("--0--")
  28. a0 = A.new
  29. a0.func0
  30. p a0.x
  31.  
  32. puts("--1--")
  33. a1 = A.new
  34. a1.func1
  35. p a1.x
  36.  
  37. puts("--2--")
  38. a2 = A.new
  39. a2.func2


Ruby语言十分灵活,有很多匪夷所思的陷阱。很多时候需要通过限制灵活性来减少Bug。比如在类内使用self.调用属性方法(如x, x=)。

另外,像这种名称遮掩在其他语言中也很常见吧,比如
CPP 代码复制
  1. class A
  2. {
  3. public:
  4.         void setX(int x) {
  5.                 this->x = x; // 这种赋值的方法如果要同名的话一般加this。
  6.         }
  7. private:
  8.         int x;
  9. };







作者: 雪在燃    时间: 2016-9-6 21:53
RyanBern 发表于 2016-9-6 19:56
从别的面向对象语言转过来 Ruby 学起来是有点别扭的。建议的做法是少类比,忘掉从前学过的东西,直接从 Rub ...

c#中这样当然可以直接使用 X除非声明了另一个X
问题是c#使用临时变量肯定要显示声明
主要是我想当然的认为了
不过是因为之前用了contents直接用了,然后我试了试,发现可以直接使用父类属性(当然所有语言都可以)
我就很疑惑下面的contents为什么要单独添加self.

作者: 喵呜喵5    时间: 2016-9-6 22:31
寒冷魔王 发表于 2016-9-6 21:38
原先给Chd114神查过一个离奇Bug(见下面的func2)
class A
  def initialize

实际上,这些并不是陷阱,
我一直认为写ruby代码唯一需要保证的是:不要让ruby去猜你想做什么
只要保证这一点,很多东西根本不会匪夷所思而是非常符合直觉

例如这里的赋值,因为ruby调用方法不需要括号,所以ruby没法区分你执行 a= 是要调用方法还是创建临时变量,这时,加一个 self 显式消除这种含糊不清的命令就是义务,而如果你的命令不会有任何歧义,那么,省略不必要的修饰词则能让代码变得优雅
作者: taroxd    时间: 2016-9-7 09:26
本帖最后由 taroxd 于 2016-9-7 09:35 编辑
寒冷魔王 发表于 2016-9-6 21:38
原先给Chd114神查过一个离奇Bug(见下面的func2)
class A
  def initialize


调用方法时(self.x = 这种的除外)不需要加 self,也不建议加。
1. 加了 self 就无法调用 private 方法。(同样 self.x = 这是个例外)
2. 保持这个习惯的话,只要出现了 self.xxx 这样显式写出 self 的方法调用,都可以检查一下代码哪里不规范。(默认脚本中主要是 self.def 和 self.class 这种撞关键字的,还有可以省略 self 但没省略的。self.def 可以理解成为了让技能公式更自然,那个 self.class 我实在无法理解为什么要这么做,又撞关键字而且还和 Kernel 里的方法同名……)

正确的做法是:不要用和方法同名的局部变量,除非两者表达完全相同的意思(此时不会造成混淆)。

RUBY 代码复制
  1. # Good
  2. class Game_BattlerBase
  3.   def hp=(hp)
  4.     @hp = hp
  5.     # 如果这里有东西要访问 hp,那么无论 hp 表示 self.hp 还是局部变量 hp,得到的结果都是一样的。
  6.     refresh
  7.   end
  8. end
  9.  
  10. # Bad。参数名就不能命名为 new_exp 或者 value 之类的吗。
  11. class Game_Actor
  12.   def change_exp(exp, show)
  13.     @exp[@class_id] = [exp, 0].max
  14.     last_level = @level
  15.     last_skills = skills
  16.     # 注意下面两行都使用了 self.exp。不熟悉 ruby 的话是不是很容易掉坑里
  17.     level_up while !max_level? && self.exp >= next_level_exp
  18.     level_down while self.exp < current_level_exp
  19.     display_level_up(skills - last_skills) if show && @level > last_level
  20.     refresh
  21.   end
  22. end





欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1