【问题标题】:Calling an exported method on a unexported field在未导出的字段上调用导出的方法
【发布时间】:2019-12-05 13:19:43
【问题描述】:

我知道,有人问过类似的问题,但我没有找到该案例的答案:

type ExportedStruct struct{ //comes from a dependency, so I can't change it
  unexportedResource ExportedType
}

我想在unexportedResource 上调用一个导出 方法Close()

我所做的是:

rs := reflect.ValueOf(myExportedStructPtr).Elem() //myExportedStructPtr is a pointer to an ExportedStruct object
resourceField := rs.FieldByName("unexportedResource")
closeMethod := resourceField.MethodByName("Close")
closeMethod.Call([]reflect.Value{reflect.ValueOf(context.Background())})

,结果为reflect.flag.mustBeExported using value obtained using unexported field

这很烦人,因为我想运行多个使用 ExportedStruct 的测试,但只要不使用底层资源,我就不能。

由于我可以访问私有字段(如 here 所解释的那样),我有点希望我也能以某种方式访问​​该字段的公共方法。也许我只是反映错了?

【问题讨论】:

    标签: go methods reflection


    【解决方案1】:

    未导出的字段仅用于声明包。别再惹他们了。它们不适合你。

    链接的答案只能通过使用包unsafe 访问它,该包不适合日常使用。包裹unsafe 应附带“请勿触摸”手册。

    如果您确实需要访问unexportedResource,请将其导出。要么是字段,要么向调用unexportedResource.Close() 的类型添加方法。或者将实用程序函数添加到执行此操作的包中(同一包中的函数可以访问未导出的字段和标识符)。

    【讨论】:

    • 谢谢。 ExportedStruct 来自依赖项,所以我无法访问它。我会补充这个问题。关于“不适合你”。这不是我喜欢做的事情,一般来说,反思是我喜欢避免的事情。但有些 API 的设计并没有达到应有的水平,尤其是在(永无止境的)v0 迭代中。
    • @NotX 使用反射很好,使用unsafe 是你应该避免的。但是你想要的东西是不可能通过反射实现的,只有(也)使用unsafe
    【解决方案2】:

    虽然@icza 的回答给了你不应该这样做的理由,但这里有一种使用reflectunsafe 的方法:

    var t pkg.T
    v := reflect.ValueOf(&t).Elem()
    f := v.FieldByName("t")
    rf := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
    rf.MethodByName("Print").Call(nil)
    

    游乐场:https://play.golang.org/p/CmG9e4Bl9gg

    【讨论】:

      【解决方案3】:

      恐怕你想做的事通过反思是不可能的。

      下面是reflect.Call的实现:

      func (v Value) Call(in []Value) []Value {
          v.mustBe(Func)
          v.mustBeExported()
          return v.call("Call", in)
      }
      

      如您所见,如果Value 是从导出的字段中获得的,则会进行显式检查(即mustBeExported())。

      通常没有导出字段是有原因的。如果要操作该字段,则必须使用 ExportedStruct 结构实现的方法。

      如果您可以修改定义ExportedStruct 的代码,您可以轻松地在其上实现包装器Close 方法。例如:

      type ExportedStruct struct{
        unexportedResource ExportedType
      }
      
      func (e ExportedStruct) Close(){
        e.unexportedResource.Close()
      }
      

      【讨论】:

      • 你说得对,func (v Value) Call(in []Value) []Value 正是我最终到达的地方。我忘了提到我无法修改 ExportedStruct 的来源。我已经添加了。谢谢!
      猜你喜欢
      • 1970-01-01
      • 2014-08-20
      • 1970-01-01
      • 2021-07-18
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      • 1970-01-01
      • 2018-04-08
      相关资源
      最近更新 更多