gpt4 book ai didi

c# - 通过反射将接口(interface)方法调用映射到远程实例方法调用

转载 作者:太空宇宙 更新时间:2023-11-03 15:56:53 24 4
gpt4 key购买 nike

系统由一组对等连接组成。每个对等点都提供了一组它可以执行的“操作”。每个 Action 都由接口(interface)上的一个方法表示,比如

public interface IMyCoolActions
{
int Add(int first, int second);
}

“客户端”节点将为操作接口(interface)创建一个代理对象,以便它可以调用此接口(interface)上的方法。当调用此代理上的接口(interface)方法之一时,代理收集参数数据,将其打包并通过网络将其发送到“服务器”对等体。 “服务器”对等方解压缩数据,确定调用了哪个方法并调用该方法,即基本上是一种 RPC 方法

现在“服务器”对等端不必实际执行 IMyCoolActions界面。它所需要的只是一种方法,它将:

  • 具有相同的参数
  • 具有相同的返回类型
  • 执行被调用接口(interface)方法指示的操作

所以它可以有一个下面类的实例

public sealed class DoStuff
{
public int Combine(int first, int second)
{
return first + second;
}
}

显然需要一个映射 IMyCoolActions.Add 的映射DoStuff.Combine 的方法方法。简单的方法是制作 DoStuff实现IMyCoolActions接口(interface),但是目标是断开这两者,以便可以允许调用者提供仅在本地端使用的参数。例如以下应该仍然是可映射的

public interface IMyCoolActions
{
Task<int> Add(int first, int second, [ConnectionTimeoutAttribute]TimeSpan timeout);
}

public sealed class DoStuff
{
public int Combine([RemoteIdAttribute]IPEndpoint origin, int first, int second)
{
return IsAllowedToCommunicate(orgin) ? first + second : int.MaxValue;
}
}

此映射应该仍然有效,因为客户端在本地使用超时值(作为 .. 超时)并且在解包网络数据时向服务器提供原始 IP 数据。

除了映射的生成之外,整个系统已经实现。迄今为止,事实证明,找到一种合适的方法来创建正确的映射是一种幻想。我尝试了以下方法(及其派生方法):

public interface ICommandMapper<TCommand>
{
IMethodWithoutResultMapper ForMethodWithResult<T1, T2, T3, TOut>(
Expression<Func<TCommand, T1, T2, T3, Task<TOut>>> methodCall);
}

public interface IMethodWithResultMapper
{
void ToMethod<TInstance, T1, T2, T3, TOut>(
TInstance instance,
Expression<Func<TInstance, T1, T2, T3, TOut>> methodCall);
}

然后可以通过以下方式调用:

var instance = new DoStuff();

ICommandMapper<IMyCoolActions> map = CreateMap();
map.ForMethodWithoutResult((command, first, second, timeout) => command.Add(first, second, timeout))
.ToMethod(instance, (ipaddress, first, second) => instance.Combine(ipaddress, first, second));

不幸的是,C# 编译器无法推断出不同的类型。虽然缺少类型推断是可以解决的,但它会导致大量丑陋的转换和类型指定。

所以我想要的是在这些方法之间进行映射的建议/想法,以便

  • 可以确定使用了哪个接口(interface)方法和哪个对象方法(通过使用反射、DynamicObject或其他方式
  • 用户不必纠结于太多角落。

编辑

实际的 Action 签名(即 IMyCoolActions )和 Action 的实现(即 DoStuff )由我的代码的用户控制。我的代码只负责生成代理、传输调用数据和调用正确的操作方法。

目前对签名的要求是:

  • 签名是通过派生 self 的操作接口(interface)之一的接口(interface)定义的。
  • 每个接口(interface)可能只有方法,所以没有属性或事件。
  • 每个方法必须返回一个 Task (以防操作不返回值)或 Task<T> (以防操作确实返回一个值)。在后一种情况下 T必须是可序列化的。
  • 每个方法参数都必须是可序列化的。
  • 代理使用的方法参数,即那些不会被传输的方法参数要用特殊属性标记。

操作实现有相似(但不相同)的要求。

最佳答案

目前我已经通过接受类型转换将成为这个问题的生活事实来解决这个问题。

接口(interface)已更改为类,因为每个接口(interface)实际上应该只有一个实现,并且通过删除输出的类型参数简化了方法。鉴于代码交易从表达式中提取了 MethodInfo,它仍然可以获取返回类型,而无需定义多个方法重载以便能够在方法签名中包含返回类型。

public sealed class CommandMapper<TCommand>
{
public MethodMapper For<T1, T2, T3>(Expression<Action<TCommand, T1, T2, T3>> methodCall)
{
return CreateMethodMapper(methodCall);
}
}

public sealed class MethodMapper
{
public void To<T1, T2, T3>(Expression<Action<T1, T2, T3>> methodCall)
{
// Do stuff
}
}

使用此接口(interface),用户可以调用如下方法:

var map = CommandMapper<IMyCoolActions>.CreateMap();
map.For<int, int, TimeSpan>((command, first, second, timeout) => command.Add(first, second, timeout))
.To((IPEndpoint ipaddress, int first, int second) => instance.Combine(ipaddress, first, second));

CommandMapper 中,MethodInfo 是通过以下方式获得的:

var methodCall = method.Body as MethodCallExpression;
if (methodCall == null)
{
throw new InvalidCommandMethodExpressionException();
}

return methodCall.Method;

MethodMapper 中,除了 MethodInfo 之外,还需要提取实际的对象引用。这有点棘手,因为编译器生成了一个包含实际引用的类,但幸运的是有一个 solution on StackOverflow。 .

var methodCall = method.Body as MethodCallExpression; 如果 (methodCall == null) { 抛出新的 InvalidCommandMethodExpressionException();

var methodInfo = methodCall.Method;

// if the object on which the method is called is null then it's a static method
object instance = null;
if (methodCall.Object != null)
{
var member = methodCall.Object as MemberExpression;
if (member == null)
{
throw new InvalidCommandMethodExpressionException();
}

// The member expression contains an instance of an anonymous class that defines the member
var constant = member.Expression as ConstantExpression;
if (constant == null)
{
throw new InvalidCommandMethodExpressionException();
}

var anonymousClassInstance = constant.Value;

// The member of the class
var calledClassField = member.Member as FieldInfo;

// Get the field value
instance = calledClassField.GetValue(anonymousClassInstance);
}

return new Tuple<object, MethodInfo>(instance, methodInfo);

关于c# - 通过反射将接口(interface)方法调用映射到远程实例方法调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23230912/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com