内容目录
C# 编写 SignalR 客户端时需要手动注入客户端方法:
connection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Dispatcher.Invoke(() =>
{
var newMessage = $"{user}: {message}";
messagesList.Items.Add(newMessage);
});
});
这样处理起来比较麻烦,因此需要实现自动扫描类型的实例方法自动注册。
定义一个保存实例方法信息模型类:
public class RegisterMethod
{
public MethodInfo MethodInfo { get; init; }
public bool IsAsync { get; init; }
public Type[] Types { get; init; }
}
定义特性注解,在需要绑定的实例方法中标记。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class SignalRAttribute : Attribute
{
public string Name { get; private set; }
public SignalRAttribute(string name)
{
Name = name;
}
}
然后实现扫码和自动注册:
public static class SignalRHelper<T> where T : class
{
private static Dictionary<string, RegisterMethod> MethodCache = new();
static SignalRHelper()
{
var methods = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var item in methods)
{
var attr = item.GetCustomAttribute<SignalRAttribute>();
if (attr == null) continue;
MethodCache.Add(attr.Name, new RegisterMethod
{
MethodInfo = item,
IsAsync = item.ReturnType == typeof(Task),
Types = item.GetParameters().Select(x => x.ParameterType).ToArray()
});
}
}
/// <summary>
/// 绑定方法到 HubConnection 中
/// </summary>
/// <param name="connection"></param>
/// <param name="targetObj"></param>
public static void Bind(HubConnection connection, T targetObj)
{
foreach (var item in MethodCache)
{
MethodInfo? onMethod;
// 有泛型参数
if (item.Value.Types.Length > 0)
{
var ms = typeof(HubConnectionExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(x => x.Name == "On" &&
x.GetGenericArguments().Length == item.Value.Types.Length &&
x.GetParameters().Length == 3
);
// 异步方法 (HubConnection,methodName,Func<....,Task>)
if (item.Value.IsAsync)
{
onMethod = ms.FirstOrDefault(x => x.GetParameters()[2].ParameterType!.Name!.Contains("Func"));
}
// 异步方法 (HubConnection,methodName,Action<....>)
else
{
onMethod = ms.FirstOrDefault(x => x.GetParameters()[2].ParameterType!.Name!.Contains("Action"));
}
if (onMethod != null) onMethod = onMethod.MakeGenericMethod(item.Value.Types);
}
else
{
if (item.Value.IsAsync)
{
onMethod = typeof(HubConnectionExtensions).GetMethod("On", BindingFlags.Static | BindingFlags.Public, new Type[]
{
typeof(HubConnection),
typeof(string),
typeof(Func<Task>)
});
}
else
{
onMethod = typeof(HubConnectionExtensions).GetMethod("On", BindingFlags.Static | BindingFlags.Public, new Type[]
{
typeof(HubConnection),
typeof(string),
typeof(Action)
});
}
}
if (onMethod == null) continue;
var del = Delegate.CreateDelegate(onMethod.GetParameters()[2].ParameterType, targetObj, item.Value.MethodInfo);
onMethod.Invoke(null, new object[]
{
connection,
item.Key,
del
});
}
}
}
只需要在实例方法中标记即可:
[SignalR("A")]
public async Task A(string a, string b)
{
await Task.CompletedTask;
}
使用:
MyService a = ....
SignalRHelper<MyService>.Bind(_connection, a);
文章评论
不明觉历