【问题标题】:c# Is it possible to supply a lambda when an interface is required?c# 是否可以在需要接口时提供 lambda?
【发布时间】:2012-09-09 21:11:31
【问题描述】:

在某个类方法A中,我需要调用一个库方法B,它以IProgress<Object>为参数。

通常,我可以将IProgress<Object> 实现为A 所在类的一部分,然后将“this”传递给方法B。或者我可以创建一个新类,其唯一目的是实现IProgress<Object> 并处理它正确 - 然后在这种情况下,我将创建该类的一个实例并将其传递给 B。

但我真正想要的是让我的 IProgress<Object> 实现出现在我正在调用 B 的方法中,这样调用代码和 IProgress<Object> 实现之间的视觉断开就会减少. (我认为我的 IProgress 实现是调用方法的一种私有的、非共享的细节,因此我不希望我的 IProgress<Object> 实现在一个完全独立的方法中,可能是另一个类)。

我一直在尝试做的是使用一个 lambda,我将在其中定义我的短进度处理,然后以某种方式将此 lambda 传递给 B,如下所示:

method in class A {
...
Action<Object> Report = (m) => { // do something useful with m };

B(Report)
}

method B(IProgress<Object> reporter) {
   reporter.Report(some object)
}

当然,我知道为什么这不能按原样工作 - B 想要一个实现 IProgress&lt;Object&gt; 的对象,而我将其交给了一个 Action 对象。

有什么方法可以实现我想要实现的目标吗? (如果 IProgress&lt;Object&gt; 出现在方法 A 中,IE 有我的实现吗?

【问题讨论】:

  • C# lambda 是匿名委托。您需要的是实现接口的匿名类型。有关 C# 提供的 only-public-properties 匿名类型的一些解决方法,请参阅 this question

标签: c# interface lambda


【解决方案1】:

委托不能(直接)实现接口。

想到两个不错的选择:

  1. 更改您要调用的方法的定义以采用委托类型而不是 IProgress 类型。 (如果可能;这将是更好的选择)

  2. 创建一个实现所需接口的新类型,并将委托作为参数来实现该功能。

#2 的示例虽然依赖于接口,但可能看起来像这样:

interface IProgress<T>
{
    void doStuff(T param);
}

class LambdaProgress<T> : IProgress<T>
{
    Action<T> action;
    public LambdaProgress(Action<T> action)
    {
        this.action = action;
    }

    public void doStuff(T param)
    {
        action(param);
    }
}

那么你就可以这样做:

B(new LambdaProgress<Object>(obj => ...));

【讨论】:

    【解决方案2】:

    不,当需要接口时,您不能提供 lambda。
    但是您可以使用impromptu-interface 提供匿名对象。

    这个例子来自它的项目页面:

    //Anonymous Class
    var anon = new
    {
        Prop1 = "Test",
        Prop2 = 42L,
        Prop3 = Guid.NewGuid(),
        Meth1 = Return<bool>.Arguments<int>(it => it > 5)
    }
    
    var myInterface = anon.ActLike<IMyInterface>();
    

    在您的示例中,这可以是:

    A
    {
        // Create an anonymous object.
        var anonymous = new
        {
            // Define a method that needs an "object" parameter and returns nothing.
            Report = Return.Arguments<object>(m =>
            {
                // Do whatever you want to do when Report is called.
            })
        }
    
        // Get your anonymous object as an IProgress<Object>.
        var obj = anonymous.ActLike<IProgress<Object>>
    
        // Call B.
        B(obj);
    }
    

    【讨论】:

      【解决方案3】:

      假设您的 IProgress&lt;object&gt; 接口只有一个 void Report(object) 方法,并且您控制 API,您可以简单地将当前需要 IProgress&lt;object&gt; 类型参数的方法重新定义为需要新的委托类型:

      public delegate void ProgressReportDelegate(object someObject);
      

      那么你的例子可以改为:

      method in class A {
      ...
      Action<Object> Report = (m) => { // do something useful with m };
      
      B(Report)
      }
      
      method B(ProgressReportDelegate reporter) {
         reporter(someObject);
      }
      

      对于更复杂的接口,或者您不控制 API(因此无法更改方法以获取委托而不是在实现该接口的对象上),这不是一个真正的选择,但它会似乎适用于您的情况。

      【讨论】:

        【解决方案4】:

        我相信 José 是这里唯一有效的答案,并帮助我解决了同样的问题。

        从问题 Michael 想要调用方法“B”但使用 lambda 而不是实现 IProgress 接口:

        method B(IProgress<Object> reporter) {
           reporter.Report(some object)
        }
        

        要在不创建需要实现 IProgress 接口的新类的情况下执行此操作,可以使用 Progress 类,因为它已经实现了 IProgress 接口。只需创建一个看起来像这样的 lambda 并将其传递到您的方法中:

        var reporter = new Progress<int>((x) => 
        {
           // call your own methods that use the reported value here
           MyProgressMethod.SetProgress(x);
        });
        

        现在您可以根据上述问题在您的方法中使用它:

        B(reporter);
        

        或者,如果您想在一行中执行此操作:

        B(new Progress<int>((x) => { progress.SetProgress(action, x); }));
        

        【讨论】:

        • 这个问题不是在询问 .NET IProgress&lt;T&gt; 接口。在该接口事件存在之前被询问。他们在询问他们自己的自定义界面。
        • 这里的解决方案是将新的 IProgress 与新的 Progress 类结合使用,而不是自己滚动。
        • 问题是如何使用委托实现任意接口。用于演示该问题的一个示例恰好称为IProgress 与该问题无关。该问题没有询问如何根据该后台任务的进度从后台进程更新桌面应用程序的 UI。您正在回答与所问问题不同的问题。鉴于他们的问题没有提到关于编组到 UI 线程,甚至不清楚您的解决方案是否适用于他们报告进度的上下文。
        【解决方案5】:

        使用 .NET 4.5,您可以使用 Progress

        例子:

        var reporter = new Progress<Object>((m) => {
          // do something useful with m
        });
        
        B(reporter);
        

        【讨论】:

        • 当问题实际上是关于 System.IProgress&lt;T&gt; 时,这个答案是正确的,虽然没有明确说明,但它是隐含的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-09-23
        • 1970-01-01
        • 1970-01-01
        • 2012-12-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多