Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过反射将接口方法调用映射到远程实例方法调用_C#_Reflection_Interface_Proxy_Invocation - Fatal编程技术网

C# 通过反射将接口方法调用映射到远程实例方法调用

C# 通过反射将接口方法调用映射到远程实例方法调用,c#,reflection,interface,proxy,invocation,C#,Reflection,Interface,Proxy,Invocation,该系统由一组对等连接组成。每个对等方提供一组它可以执行的“操作”。每个动作都由接口上的一个方法表示,例如 public interface IMyCoolActions { int Add(int first, int second); } “客户端”对等方将为操作接口创建一个代理对象,以便调用此接口上的方法。当调用此代理上的某个接口方法时,代理将收集参数数据,打包并通过网络将其发送到“服务器”对等方。“服务器”对等端解压数据,确定调用了哪个方法并调用该方法,即基本上是RPC方法 现在

该系统由一组对等连接组成。每个对等方提供一组它可以执行的“操作”。每个动作都由接口上的一个方法表示,例如

public interface IMyCoolActions
{
    int Add(int first, int second);
}
“客户端”对等方将为操作接口创建一个代理对象,以便调用此接口上的方法。当调用此代理上的某个接口方法时,代理将收集参数数据,打包并通过网络将其发送到“服务器”对等方。“服务器”对等端解压数据,确定调用了哪个方法并调用该方法,即基本上是RPC方法

现在,“服务器”对等方不必实际实现
imycooloactions
接口。它所需要的只是一种能够:

  • 具有相同的参数
  • 具有相同的返回类型
  • 执行被调用接口方法指示的操作
所以它可以有以下类的一个实例

public sealed class DoStuff
{
    public int Combine(int first, int second)
    {
        return first + second;
    }
}
显然,需要有一个映射来映射
IMyCoolActions.Add
方法到
DoStuff.Combine
方法。简单的方法是让
DoStuff
实现
imycooloactions
接口,但是目标是断开这两个接口,以便允许调用者提供仅在本地端使用的参数。e、 g.以下内容仍应可映射

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;
    }
}
公共接口IMyCoolActions
{
任务添加(int-first,int-second,[ConnectionTimeoutAttribute]TimeSpan超时);
}
公共密封等级
{
公共整数合并([RemoteIdAttribute]IPEndpoint原点,整数第一,整数第二)
{
返回允许通信(orgin)?第一+第二:int.MaxValue;
}
}
此映射仍应有效,因为客户端在本地使用超时值(作为..井超时),并且在网络数据解包时向服务器提供源IP数据

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

公共接口ICommandMapper
{
IMethodWithResultMapper ForMethodWithResult(
表达式方法调用);
}
公共接口IMethodWithResultMapper
{
空位法(
例如,,
表达式方法调用);
}
然后可以通过以下方式调用:

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));
var实例=new DoStuff();
ICommandMapper map=CreateMap();
map.ForMethodWithoutResult((命令,第一,第二,超时)=>command.Add(第一,第二,超时))
.ToMethod(instance,(ipaddress,first,second)=>instance.Combine(ipaddress,first,second));
不幸的是,C#编译器无法推断不同的类型。虽然类型推断的缺乏是可以解决的,但它会导致许多难看的类型转换和类型指定

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

  • 可以确定使用哪种接口方法和哪种对象方法(通过使用反射、
    DynamicObject
    或其他方式)
  • 用户不必在太多的角落里争吵
编辑

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

目前对签名的要求是:

  • 签名是通过从我的一个操作接口派生的接口定义的
  • 每个接口可能只有方法,因此没有属性或事件
  • 每个方法必须返回
    任务
    (如果操作未返回值)或
    任务
    (如果操作未返回值)。在后一种情况下,
    t
    必须可序列化
  • 每个方法参数都必须是可序列化的
  • 代理使用的方法参数,即那些不会被传输的参数,将用特殊属性标记

动作实现也有类似(但不完全相同)的要求。

目前,我已经解决了这个问题,接受了这个问题的铸造将是一个现实

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

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
    }
}
MethodMapper
中,除了
MethodInfo
之外,还需要提取实际的对象引用。这稍微有点棘手,因为编译器生成了一个包含实际引用的类,但幸运的是有一个类

var methodCall=method.Body作为MethodCallExpression; if(methodCall==null) { 抛出新的InvalidCommandMethodExpressionException(); }

var methodInfo=methodCall.Method;
//如果调用该方法的对象为null,则它是一个静态方法
对象实例=null;
if(methodCall.Object!=null)
{
var member=methodCall.Object作为MemberExpression;
if(成员==null)
{
抛出新的InvalidCommandMethodExpressionException();
}
//成员表达式包含定义成员的匿名类的实例
var常量=成员。表达式为常量表达式;
if(常数==null)
{
抛出新的InvalidCommandMethodExpressionExx
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
    }
}
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));
var methodCall = method.Body as MethodCallExpression;
if (methodCall == null)
{
    throw new InvalidCommandMethodExpressionException();
}

return methodCall.Method;
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);