我现在正在阅读两篇文章并且有点困惑。
这篇文章 - http://blog.golang.org/laws-of-reflection说
> var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
r = tty
r示意性地包含(值,类型)对,(tty,* os.File)。 请注意,类型* os.File实现除Read之外的方法;甚至 虽然接口值仅提供对Read方法的访问, 里面的值包含有关该值的所有类型信息。
另一篇文章说
就我们的例子而言,Stringer持有Binary的类型 列出用于满足Stringer的方法,它只是String: 二进制的其他方法(Get)没有出现在itable中。
感觉这两者是对立的。根据第二篇文章,第一个提取中的变量r应该是(tty,io.Reader),因为它是r的静态类型。相反,文章说* os.File是tty的类型。如果第二个示例是正确的,那么第一个示例中的图表应该具有二进制类型实现的所有方法。
我哪里错了?
答案 0 :(得分:3)
这两篇文章在两个非常不同的粒度级别上解释了类似的概念。正如沃尔克所说,“反思法则”是对通过反射检查对象时实际发生的事情的基本概述。您的第二篇文章正在研究接口的动态调度属性(也可以通过反射解析)以及运行时如何在运行时解析它们 。
根据第二篇文章,第一个提取中的变量r应为(tty,io.Reader)
鉴于这种理解,将运行时的接口视为“包装器对象”。它的存在是为了提供有关另一个对象(第二篇文章中的itable
)的信息,以了解在包装对象布局中跳转到哪里(版本之间的实现可能不同......但对于大多数语言,原理基本相同)
这就是Read
上r
调用的原因..首先它会检查itable
并跳转到为os.File
类型布局的函数。如果那是一个界面..你会看到另一个解除引用和派遣(IIRC根本不适用于Go)。
RE:反思 - 您以(value, type)
对的形式(通过reflect.ValueOf
和reflect.TypeOf
方法)获得了一个易于理解的表示形式。
答案 1 :(得分:1)
两者都是正确的,r“持有”(tty,* os.File),这就是第二篇文章所说的。请注意,反射法则更高层次,并没有提到实现细节(可能会在每个版本中更改),如第二篇文章中所讨论的那样。第二篇文章的图解如下:“s包含示意图(b,* Binary).s是Stringer类型,它的数据是二进制,值为200,s的itable包含一个方法String和二进制的其他方法(或*二进制文件没有在itable中表示,因此s不可访问。
请注意,我认为Go 1.4中接口的实际实现与第二篇文章(Russ的?)不同。 “