提问人:Warstek 提问时间:2/7/2023 最后编辑:Warstek 更新时间:2/8/2023 访问量:146
C#:自定义委托,但给出了第一个参数
C#: Custom Delegate but the first parameter is given
问:
我想存储来自客户端的 WebSocket 调用的回调。
我想告诉IDE第一个参数应该始终是。ClientMetadata
问题是,我想注册具有无限数量和未知类型参数的方法,但第一个必须是 ClientMetadata 对象。
我想像这样注册回调:
RegisterEventHandler("callback1", (ClientMetadata client, string smth1, int smth2);
RegisterEventHandler("callback2", (ClientMetadata client, bool smth3);
我有一个方法来存储回调:
public static void RegisterEventHandler(string identifier, Delegate callback)
{
callbacks.Add(identifier, callback);
}
以下是回调的存储位置:
private static Dictionary<string, EventHandlerCallback> callbacks = new Dictionary<string, EventHandlerCallback>();
以下是存储方法的调用方式:
private static void Server_MessageReceive2d(object? sender, MessageReceivedEventArgs e)
{
string jsonObj = Encoding.UTF8.GetString(e.Data);
if (jsonObj != null)
{
WSMessage obj = JsonConvert.DeserializeObject<WSMessage>(jsonObj);
if (obj == null || obj.message == null)
{
return;
}
//obj.identifier is the id of the callback in the serverside
if (callbacks.ContainsKey(obj.identifier))
{
MethodInfo mi = callbacks[obj.identifier].GetMethodInfo();
if (obj.callbackId != null && mi.ReturnType.ToString() != "System.Void")
{
//e.Client --> ClientMetaData
//obj.message --> dynamic[]?
var ret = callbacks[obj.identifier].DynamicInvoke(e.Client, obj.message);
//obj.callbackId is the callback to call when the method ended
if (ret != null)
{
SendMessage(e.Client, obj.callbackId, ret);
}
else
{
SendMessage(e.Client, obj.callbackId);
}
}
else
{
callbacks[obj.identifier].DynamicInvoke(e.Client, obj.message);
}
}
else
{
return;
}
}
}
我尝试制作一个自定义委托:
delegate void EventHandlerCallback(ClientMetadata clientMetadata, params object[] data);
事实证明,在这种情况下,关键字绝对没有任何作用。无论我使用与否,我的 IDE 都期望这样做:params
params
RegisterEventHandler("callback2", (ClientMetadata client, object[] smthObj);
答:
0赞
Aljaz Brodar
2/7/2023
#1
可以创建一个委托,该委托将 ClientMetadata 对象作为第一个参数,将对象数组作为第二个参数。这样,您可以存储包含任意数量的未知参数的回调:
delegate void EventHandlerCallback(ClientMetadata clientMetadata, object[] data);
public static void RegisterEventHandler(string identifier,
EventHandlerCallback callback)
{
callbacks.Add(identifier, callback);
}
用法:
RegisterEventHandler("callback1", (ClientMetadata client, object[] args) =>
{
string smth1 = (string)args[0];
int smth2 = (int)args[1];
});
RegisterEventHandler("callback2", (ClientMetadata client, object[] args) =>
{
bool smth3 = (bool)args[0];
});
评论
0赞
Warstek
2/7/2023
这个解决方案最终会很好,但是当你编写一个事件处理程序的脚本时,它有点难以使用。我想像这样独立声明参数:RegisterEventHandler("callback1", (ClientMetadata client, string smth1, int smth2);
-2赞
Selvin
2/8/2023
#2
你可以去代码分析器...像这样的东西应该可以解决问题:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
namespace AnalyzerTest
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AnalyzerTestAnalyzer : DiagnosticAnalyzer
{
public const string ExpectedParameterType = "ClientMetadata";
public const string MethodName = "RegisterEventHandler";
public const string DiagnosticId = "SEL001";
public const string Title = "AnalyzerTest";
public const string MessageFormat = "Method call '{0}' wrong argument. First parameter of delegate should be '{1}'";
public const string Category = "Error";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(NodeAction, new SyntaxKind[] { SyntaxKind.InvocationExpression });
}
private void NodeAction(SyntaxNodeAnalysisContext context)
{
var registerMethodSymbol = (IMethodSymbol)context.SemanticModel.GetSymbolInfo(context.Node).Symbol;
if(registerMethodSymbol.Name == MethodName)
{
IMethodSymbol methodSymbol;
var callbackSyntax = ((InvocationExpressionSyntax)context.Node).ArgumentList.Arguments[1];
var callbackTypeInfo = context.SemanticModel.GetTypeInfo(callbackSyntax.Expression);
if(callbackTypeInfo.Type != null)
{
var namedType = callbackTypeInfo.Type as INamedTypeSymbol;
//Action<ClientMetadata, ...> or Func<ClientMetadata, ..., ret> or delegate
methodSymbol = namedType.DelegateInvokeMethod;
}
else
{
//Method
var symbolInfo = context.SemanticModel.GetSymbolInfo(callbackSyntax.Expression);
methodSymbol = (symbolInfo.Symbol ?? (symbolInfo.CandidateSymbols != null ? symbolInfo.CandidateSymbols[0] : null)) as IMethodSymbol;
}
if(methodSymbol != null && methodSymbol.Parameters.Length > 0 && methodSymbol.Parameters[0].Type.Name == ExpectedParameterType)
{
return;
}
var diagnostic = Diagnostic.Create(Rule, callbackSyntax.GetLocation(), MethodName, ExpectedParameterType);
context.ReportDiagnostic(diagnostic);
}
}
}
}
评论
0赞
Warstek
6/5/2023
您好,对不起,我的回复晚了。我最近没有太多时间做这个项目。你能把完整的代码发给我吗?对不起,我不明白我应该如何实现您的解决方案。
评论
RegisterEventHandler
if(callback.Method.GetParameters()[0].ParameterType != typeof(ClientMetadata)) throw new ArgumentException("Wrong delegate");
ClientMetadata