You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过反射调用ros-sharp的Subscribe泛型方法实现ROSbridge动态订阅

如何在Ros-Sharp中通过字符串指定消息类型动态订阅ROS话题

我使用Ros-Sharp项目连接ROSbridge订阅话题时,直接指定泛型消息类型的订阅逻辑可以正常运行:

rosSocket = new RosSocket(new RosBridgeClient.Protocols.WebSocketNetProtocol(uri), RosSocket.SerializerEnum.Newtonsoft_JSON);
string subscription_id = rosSocket.Subscribe<JointState>("/joint_states", SubscriptionHandler);

对应的Subscribe泛型方法定义如下:

public string Subscribe<T>(string topic, SubscriptionHandler<T> subscriptionHandler, int throttle_rate = 0, int queue_length = 1, int fragment_size = int.MaxValue, string compression = "none") where T : Message
{
    string id;
    lock (SubscriberLock)
    {
        id = GetUnusedCounterID(Subscribers, topic);
        Subscription subscription;
        Subscribers.Add(id, new Subscriber<T>(id, topic, subscriptionHandler, out subscription, throttle_rate, queue_length, fragment_size, compression));
        Send(subscription);
    }
    return id;
}

委托类型定义:

namespace RosSharp.RosBridgeClient
{
    public delegate void SubscriptionHandler<T>(T t) where T : Message;
}

我的订阅处理方法是:

private static void SubscriptionHandler(object message)
{
    string msg = JsonConvert.SerializeObject((Message)message, Formatting.None);
    Console.WriteLine(msg);
}

现在我希望通过字符串指定话题和消息类型来动态订阅,不想用switch穷举所有消息类型(否则新增类型时维护成本太高)。尝试用反射实现时,在传递委托参数时遇到InvalidCastException: Unable to cast object of type 'System.RuntimeType' to type 'RosSharp.RosBridgeClient.Message'异常,错误的反射代码如下:

Type msgType = Type.GetType("RosSharp.RosBridgeClient.MessageTypes.Sensor.JointState,RosBridgeClient");

var mi = typeof(RosSocket).GetMethod("Subscribe");

Type[] aparams = new Type[1];
aparams[0] = msgType;
var fooRef = mi.MakeGenericMethod(aparams);

object[] test2 = new object[1];
test2[0] = msgType;

object[] test = new object[6];
test[0] = "/joint_states";

// 错误的委托获取方式
var subscribe = typeof(RosSocketConsole).GetMethod(nameof(SubscriptionHandler), BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, test2);

test[1] = subscribe;
test[2] = 0;
test[3] = 1;
test[4] = int.MaxValue;
test[5] = "none";
string subscription_id = (string)fooRef.Invoke(rosSocket, test);

正确实现方案

核心问题是需要将非泛型的SubscriptionHandler(object)包装成对应消息类型的泛型SubscriptionHandler<T>委托,以下是完整的动态订阅实现:

public string SubscribeByTypeName(string rosMessageTypeFullName, string topic)
{
    // 1. 根据字符串获取消息类型(注意程序集名称要正确)
    Type msgType = Type.GetType(rosMessageTypeFullName);
    if (msgType == null)
    {
        Console.WriteLine($"无法找到消息类型: {rosMessageTypeFullName}");
        return string.Empty;
    }

    // 2. 获取RosSocket的Subscribe泛型方法
    var subscribeMethod = typeof(RosSocket).GetMethod("Subscribe")
        ?.MakeGenericMethod(msgType);
    if (subscribeMethod == null)
    {
        Console.WriteLine("无法获取Subscribe泛型方法");
        return string.Empty;
    }

    // 3. 创建泛型委托SubscriptionHandler<T>,包装现有非泛型处理方法
    Type delegateType = typeof(SubscriptionHandler<>).MakeGenericType(msgType);
    Delegate genericHandler = Delegate.CreateDelegate(
        delegateType, 
        typeof(RosSocketConsole).GetMethod(nameof(SubscriptionHandler), BindingFlags.NonPublic | BindingFlags.Static)
    );

    // 4. 调用泛型Subscribe方法
    object[] parameters = new object[]
    {
        topic,
        genericHandler,
        0, // throttle_rate默认值
        1, // queue_length默认值
        int.MaxValue, // fragment_size默认值
        "none" // compression默认值
    };

    return (string)subscribeMethod.Invoke(rosSocket, parameters);
}

关键步骤说明

  • 类型获取:确保rosMessageTypeFullName是完整的类型名+程序集名,比如"RosSharp.RosBridgeClient.MessageTypes.Sensor.JointState,RosBridgeClient"
  • 委托转换:用Delegate.CreateDelegate将非泛型的处理方法转换成对应消息类型的泛型SubscriptionHandler<T>委托,这是解决之前异常的核心
  • 泛型方法调用:正确构造泛型方法实例并传递参数

调用示例:

string subId = SubscribeByTypeName(
    "RosSharp.RosBridgeClient.MessageTypes.Sensor.JointState,RosBridgeClient",
    "/joint_states"
);

内容的提问来源于stack exchange,提问作者oddball

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:47:25