【问题标题】:How to make a reference to a method name on an interface strongly typed如何在强类型接口上引用方法名称
【发布时间】:2012-03-22 22:04:09
【问题描述】:

对不起,如果这个问题已经得到回答,但我认为我实际上缺乏正确提出这个问题的正规教育,因此也缺乏成功搜索它的正确标准。

我有一个 API,它有几个调用几乎做同样的事情,但使用不同的方法作用于不同的输入对象,但总是形成相同的接口。我想从 API 方法调用过程中取出剪切和粘贴方面,以便公共代码在所有方法调用中都完成相同的操作。我已经设法为输入和输出对象使用泛型获得了一个可行的解决方案,并且正在引用要从字符串调用的方法名称。我希望对方法的引用是强类型而不是基于字符串的,以便在重构时重命名方法名称不会潜在地让方法名称的“魔术”字符串在运行时等待爆炸。

下面是我想要实现的一个非常简化的版本。

class ARequest { };
class AResponse { };
class BRequest { };
class BResponse { };

interface IWorker
{
    AResponse DoA(ARequest aRequest);
    BResponse DoB(BRequest bRequest);
}
class Worker : IWorker
{
    public AResponse DoA(ARequest aRequest)
    {
        return new AResponse();
    }
    public BResponse DoB(BRequest bRequest)
    {
        return new BResponse();
    }
}

class Program
{
    static void Main(string[] args)
    {
        // current concrete copy & paste implementation
        var a1 = API.DoA(new ARequest { });
        var b1 = API.DoB(new BRequest { });
        // new generic implementation
        var a2 = API.DoA2(new ARequest { });
        var b2 = API.DoB2(new BRequest { });
    }
}

static class API
{
    // current concrete copy & paste implementation
    public static AResponse DoA(ARequest aRequest)
    {
        // lots of common code for logging & preperation
        var worker = GetWorker();
        return worker.DoA(aRequest);
    }
    public static BResponse DoB(BRequest bRequest)
    {
        // lots of common code for logging & preperation
        var worker = GetWorker();
        return worker.DoB(bRequest);
    }
    private static IWorker GetWorker()
    {
        return new Worker();
    }
    // new generic implementation Attempt
    public static AResponse DoA2(ARequest aRequest)
    {
        return DoGen<ARequest, AResponse>(aRequest, "DoA"); // how to make references to DoA and DoB methods on the IWorker strongly typed?
    }
    public static BResponse DoB2(BRequest bRequest)
    {
        return DoGen<BRequest, BResponse>(bRequest, "DoB"); // how to make references to DoA and DoB methods on the IWorker strongly typed?
    }
    public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, string methodname)
        where TRequest : class
        where TResponse : class
    {
        // lots of common code for logging & preperation
        var worker = GetWorker();
        var mi = worker.GetType().GetMethod(methodname);
        var result = mi.Invoke(worker, new Object[] { requestObj });
        return result as TResponse;
    }

}

【问题讨论】:

  • 响应类型是否有任何关联?
  • 看看这篇博文,它描述了一个可以满足你需要的模型:cuttingedge.it/blogs/steven/pivot/entry.php?id=92
  • Foo42:它们在最终实现中有一些来自公共基类的公共字段。在示例中,它们不相关。

标签: c# reflection interface strongly-typed-view strong-typing


【解决方案1】:

方法名称的“魔术”字符串更改为委托时委托

  public static AResponse DoA2(ARequest aRequest)
  {
    return DoGen<ARequest, AResponse>(aRequest, worker => worker.DoA);
  }
  public static BResponse DoB2(BRequest bRequest)
  {
    return DoGen<BRequest, BResponse>(bRequest, worker => worker.DoB); 
  }
  public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, 
       Func<IWorker, Func<TRequest, TResponse>> methodRef)
    where TRequest : class
    where TResponse : class
  {
    // lots of common code for logging & preparation 
    var worker = GetWorker();
    var method = methodRef(worker);

    return method(requestObj);
  }

【讨论】:

  • 这就是(据我所知)完全按照我的意愿行事。现在我只需要了解它在做什么。我想是时候停止回避仿函数了。
【解决方案2】:

Func 可能会满足您的需求:

        var a1 = new Func<ARequest, AResponse>(API.DoA);
        var b1 = new Func<BRequest, BResponse>(API.DoB);
        var a2 = new Func<ARequest, AResponse>(API.DoA2);
        var b2 = new Func<BRequest, BResponse>(API.DoB2);
        a1.Invoke(new ARequest { });
        b1.Invoke(new BRequest { });
        a2.Invoke(new ARequest { });
        b2.Invoke(new ARequest { }); // fails at compile time

【讨论】:

    【解决方案3】:

    为方法添加一个委托:

        public delegate TResponse DoXDelegate<in TRequest, out TResponse>(TRequest request);
    
        public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, DoXDelegate<TRequest, TResponse> method)
            where TRequest : class
            where TResponse : class
        {
            // lots of common code for logging & preperation
            var worker = GetWorker();
            /*var mi = worker.GetType().GetMethod(methodname);
            var result = mi.Invoke(worker, new Object[] { requestObj });
            return result as TResponse;*/
            return method.Invoke(requestObj);
        }
    

    【讨论】:

    • 我不知道怎么称呼它?你能举个例子吗?
    • 对不起,通过阅读其他答案,我认为我弄错了。坚持 DarkGray 的答案。
    【解决方案4】:

    像这样使用Func&lt;TRequest, TResponse&gt;

    编辑:该解决方案示例仅在工作对象来自何处无关紧要时才可用。

    // new generic implementation Attempt
    public static AResponse DoA2(ARequest aRequest)
    {
      return DoGen<ARequest, AResponse>(aRequest, DoA); // how to make refreces to DoA and DoB methods strongly typed?
    }
    
    public static BResponse DoB2(BRequest bRequest)
    {
      return DoGen<BRequest, BResponse>(bRequest, DoB); // how to make refreces to DoA and DoB methods strongly typed?
    }
    
    public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, Func<TRequest, TResponse> func)
      where TRequest : class
      where TResponse : class
    {
      // lots of common code for logging & preperation
      var result = func(requestObj);
      return result as TResponse;
    }
    

    【讨论】:

    • 那不是使用 API 的 DoA 和 DoB 方法,而是使用 worker 对象上的方法吗?期望的效果是调用工作对象上的方法。
    • 也许我误解了你。我认为 API.DoA 已经是 static 并且已经调用了 GetWorker()。所以不需要在 DoGen(...) 方法中调用第二个 GetWorker()
    • 我解释得很糟糕:DoA 和 DoB 的问题在于它们是彼此的“剪切和粘贴”实现,只是改变了 worker 上的方法名称。 DoA2 和 DoB2 旨在调用一个通用方法,该方法在一个地方执行所有公共部分,唯一的区别是调用的 IWorker 接口上的方法、输入对象类型和输出对象类型。
    • @MyOtherMe 现在我明白了。我刚刚读到 DarkGray 的答案对你有用。所以我可以删除我的答案。你可以删除吗?
    • 我不介意你删除它,但我认为如果你删除它会失去声誉。您的回答可能会帮助其他人解决问题并区分不同的场景。
    猜你喜欢
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多