Golang中如何从接口提取结构体并放入另一个接口

Golang中如何从接口提取结构体并放入另一个接口 我正在开发一些依赖于其他包的测试平台,现在包的所有者更改了一个方法的签名,该方法最初接收一个结构体,而现在他开始传递接口。由于我正在创建一个不同的接口,现在我的代码开始出现错误。

因此,我在想是否有一种方法可以复制我方法中接收到的接口中包含的结构体,这样我就不必进行大量修改。 以下是结构体:

有两个接口: consumer.TraceConsumer testbed.MockTraceDataConsumer

最初,我们有一个实现 Start(struct a) 的方法。 现在包的所有者更改了这个方法的实现,使其变为 Start(interface b)。 现在我想从这个接口中提取出结构体,然后将其传递给我的下一个方法,这可行吗?

为了使其更易于理解,添加以下代码:

type MockTraceConsumer struct {
	numSpansReceived atomic.Uint64
	backend          *MockBackend
}

type TraceConsumer interface {
	ConsumeTraces(ctx context.Context, td pdata.Traces) error
}

type MockTraceDataConsumer interface {
	MockConsumeTraceData(spansCount int) error
}

method I have to implement
Start(TraceConsumer)

I want to convert this interface TraceConsumer to  MockTraceDataConsumer,
so one way that i can think of is extracting the struct  MockTraceConsumer from the interface TraceConsumer and and pass it to my interface MockTraceDataConsumer

please let me know if that is possible.

更多关于Golang中如何从接口提取结构体并放入另一个接口的实战教程也可以访问 https://www.itying.com/category-94-b0.html

11 回复

是的,完全正确。

更多关于Golang中如何从接口提取结构体并放入另一个接口的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的回复,我对您之前分享的示例进行了一些修改,希望现在好一些了。 在注释中我也提到了我想要实现的目标。 https://play.golang.org/p/R6YaJSCMEE5

抱歉,我不太理解这个问题。是这样的吗?https://play.golang.org/p/nFaeF5ertFw

如果是这样,你可以这样做:https://play.golang.org/p/mj98K_o78GM

所以,您希望 github.com/open-telemetry/opentelemetry-collector-contrib 包中的 *MockAwsXrayDataReceiver 实现 github.com/open-telemetry/opentelemetry-collector 包中的 DataReceiver 接口?

所以更像是这样?https://play.golang.org/p/UGhDahuHhx_w 如果是的话,这个能行吗?https://play.golang.org/p/qrpmcq5doeA

那么,为什么不直接将 mockawsxraydatareceiver.go 文件的第 41 行从:

func (ar *MockAwsXrayDataReceiver) Start(tc *testbed.MockTraceConsumer, mc *testbed.MockMetricConsumer) error

改为:

func (ar *MockAwsXrayDataReceiver) Start(tc consumer.TraceConsumer, mc consumer.MetricsConsumer, lc consumer.LogsConsumer) error {

要从接口中提取结构体,只需对接口进行类型断言,指定你期望提取的结构体类型。

示例:https://play.golang.org/p/izvMOwq-nXM

package main

import "fmt"

type MyStruct struct {
    Field string
}

func main() {
    var i interface{} = MyStruct{Field: "value"}

    // 类型断言
    s, ok := i.(MyStruct)
    if ok {
        fmt.Println(s.Field) // 输出: value
    }
}

我不太清楚这个项目中发生了什么,因此很难提供具体的建议。看起来你添加了对某些功能的支持,但随后API发生了变化,导致你的代码现在无法工作。我正在查看其他接收器的实现,但根据我的观察,它们都使用 consumer.TraceConsumerconsumer.MetricConsumer。似乎有一个辅助函数 ToTraces,可以将字节切片转换为 *pdata.Traces,然后传递给 consumer.TraceConsumer,但这看起来(也许?)只处理UDP数据段。我不确定它是否适用于来自 (*MockAwsXrayReceiver).handleRequest*http.Request 的请求体。你可能需要查看其他接收器的差异,看看它们是如何处理这个变化的。

谢谢 sean,我认为这同样行不通,因为在 NewFunc() 中你返回的仍然是旧函数。

func (n newFromOld) NewFunc() int { return n.o.OldFunc() }

让我给你看看 GitHub 上的实际代码。

这是旧的方法签名:

https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/700/files#diff-8035efcefc9a36760d52eaf93887cf41R41

现在我依赖的库已经更改了接口。

https://github.com/open-telemetry/opentelemetry-collector/blob/master/testbed/testbed/receivers.go#L47

start 方法是从这里调用的:

https://github.com/open-telemetry/opentelemetry-collector/blob/master/testbed/testbed/mock_backend.go#L87

它只是传递了结构体 MockTraceConsumer。

那么,在不更改 collector 包中任何代码的情况下,我如何在 collector-contrib 包中使用它呢?

那么,你为什么不直接把 mockawsxraydatareceiver.go 的第 41 行从:

因为我需要在第 51 行传入 *testbed.MockTraceConsumer 的对象。

调用新的 mockawsreceiver 方法

https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/700/files#diff-8035efcefc9a36760d52eaf93887cf41R51

这个对象在 trace_receiver 中被接收,它只接受接口 *testbed.MockTraceConsumer 的对象,之后它会调用 MockConsumeTraceData(count) 方法。 这个方法仅在接口 *testbed.MockTraceConsumer 中定义,而不在 consumer.TraceConsumer 中。

MockConsumeTraceData(count) 方法调用的代码片段

https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/700/files?file-filters%5B%5D=.go#diff-e7727f150535787f058976c1a94736f3R126

trace_receiver 的 New() 方法定义的代码片段

https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/700/files?file-filters%5B%5D=.go#diff-e7727f150535787f058976c1a94736f3R49

MockConsumeTraceData(count) 方法实现的代码片段

https://github.com/open-telemetry/opentelemetry-collector/blob/master/testbed/testbed/mock_backend.go#L222

调用 Start 方法的代码片段

https://github.com/open-telemetry/opentelemetry-collector/blob/master/testbed/testbed/mock_backend.go#L87

在Go中,可以通过类型断言从接口提取底层结构体,然后将其转换为另一个接口。根据你的代码示例,以下是实现方法:

// 假设这是你的方法实现
func Start(tc consumer.TraceConsumer) error {
    // 类型断言提取 MockTraceConsumer
    if mockConsumer, ok := tc.(*MockTraceConsumer); ok {
        // 现在 mockConsumer 是 *MockTraceConsumer 类型
        // 可以直接调用 MockTraceDataConsumer 的方法
        return mockConsumer.MockConsumeTraceData(10)
    }
    
    // 或者如果需要显式转换为 MockTraceDataConsumer 接口
    var dataConsumer testbed.MockTraceDataConsumer
    if dataConsumer, ok = tc.(testbed.MockTraceDataConsumer); ok {
        return dataConsumer.MockConsumeTraceData(10)
    }
    
    return fmt.Errorf("unsupported consumer type")
}

// 确保 MockTraceConsumer 实现 MockTraceDataConsumer 接口
func (m *MockTraceConsumer) MockConsumeTraceData(spansCount int) error {
    // 实现逻辑
    m.numSpansReceived.Add(uint64(spansCount))
    return nil
}

// 同时确保 MockTraceConsumer 实现 TraceConsumer 接口
func (m *MockTraceConsumer) ConsumeTraces(ctx context.Context, td pdata.Traces) error {
    // 实现逻辑
    return nil
}

如果需要更通用的转换,可以创建一个适配器:

type TraceConsumerAdapter struct {
    consumer.TraceConsumer
}

func (a *TraceConsumerAdapter) MockConsumeTraceData(spansCount int) error {
    // 通过类型断言访问底层结构体
    if mock, ok := a.TraceConsumer.(*MockTraceConsumer); ok {
        return mock.MockConsumeTraceData(spansCount)
    }
    
    // 或者处理其他实现
    return fmt.Errorf("cannot convert to MockTraceDataConsumer")
}

// 使用适配器
func Start(tc consumer.TraceConsumer) error {
    adapter := &TraceConsumerAdapter{TraceConsumer: tc}
    return adapter.MockConsumeTraceData(10)
}

如果接口包含的是值而不是指针,使用值类型断言:

func Start(tc consumer.TraceConsumer) error {
    // 值类型断言
    if mockConsumer, ok := tc.(MockTraceConsumer); ok {
        return mockConsumer.MockConsumeTraceData(10)
    }
    
    // 指针类型断言
    if mockConsumer, ok := tc.(*MockTraceConsumer); ok {
        return mockConsumer.MockConsumeTraceData(10)
    }
    
    return fmt.Errorf("conversion failed")
}

关键点:

  1. 使用类型断言 interface.(ConcreteType) 提取具体类型
  2. 确保结构体实现了目标接口的所有方法
  3. 类型断言失败时会返回零值和 false,需要处理错误情况
回到顶部