19

In some class method, A, I need to call a library method B, which takes as an IProgress<Object> as a parameter.

Normally, I might either implement IProgress<Object> as part of class where A resides, and then pass "this" to method B. Or perhaps I might create a new class whose only purpose is to implement IProgress<Object> and handle it correctly-- then in this case I'd create an instance of that class and pass it to B.

But what I really want is to have my implementation of IProgress<Object> to appear right inside the method in which I'm calling B, so that there's less of a visual disconnect between the calling code, and the IProgress<Object> implementation. (I consider my implementation of IProgress to be kind of a private, non-shared detail of the calling method and thus I don't want my implementation of IProgress<Object> to be in a whole separate method of perhaps a whole other class).

What I've been trying to do is use a lambda in which I will define my short progress handling, and then somehow pass this lambda to B, like this:

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

B(Report)
}

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

Of course, I know why this won't work as is - B is wanting an object that implements IProgress<Object> and I'm handing it an Action object instead.

Is there any way to achieve what I'm trying to achieve? (IE have my implementation if IProgress<Object> appear inside method A?

Michael Ray Lovett
  • 6,668
  • 7
  • 27
  • 36
  • 2
    C# lambdas are anonymous delegates. What you need is an anonymous type which implements the interface. See [this question](http://stackoverflow.com/questions/191013/can-a-c-sharp-anonymous-class-implement-an-interface) for some workarounds over the only-public-properties anonymous types that C# provides. – Hristo Iliev Sep 17 '12 at 16:01

5 Answers5

18

Delegates cannot implement interfaces (directly).

Two good options come to mind:

  1. Change the definition of the method that you're calling to take a delegate types instead of an IProgress type. (If possible; this would be the preferable option)

  2. Create a new type that implements the interface that you need and takes a delegate as a parameter to implement that functionality.

And example of #2, while dependent on the interface, might look something like this:

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);
    }
}

then you'd be able to do something like:

B(new LambdaProgress<Object>(obj => ...));
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Servy
  • 202,030
  • 26
  • 332
  • 449
4

No, you can't supply a lambda when an interface is required.
But you can supply an anonymous object by using impromptu-interface.

This example is from its project page:

//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>();

In your example this can be:

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);
}
Aage
  • 5,932
  • 2
  • 32
  • 57
Şafak Gür
  • 7,045
  • 5
  • 59
  • 96
1

Assuming that your IProgress<object> interface has just the single void Report(object) method, and that you control the API, you could simply redefine the methods that currently require a parameter of type IProgress<object> to instead require a new delegate type:

public delegate void ProgressReportDelegate(object someObject);

Then your example could change to:

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

B(Report)
}

method B(ProgressReportDelegate reporter) {
   reporter(someObject);
}

For more complex interfaces, or where you don't control the API (and so can't change a method to take the delegate rather than on object implementing that interface), this isn't really an option, but it would appear to work in your case.

Iridium
  • 23,323
  • 6
  • 52
  • 74
-1

I believe José is the only valid answer here and helped me with the same problem.

From the question Michael want to call a method "B" but use a lambda instead of implementing the IProgress interface:

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

To do this without creating a new class that needs to implement the IProgress interface, the Progress class can be used as it implements the IProgress interface already. Just create a lambda that looks like this and pass that into your method:

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

And now you can use this in your methods as per question above:

B(reporter);

Or if you want to do this in one line:

B(new Progress<int>((x) => { progress.SetProgress(action, x); }));
A.T.
  • 31
  • 7
  • 1
    This question isn't asking about the .NET `IProgress` interface. It was asked before that interface event existed. They're asking about their own custom interface. – Servy Dec 22 '21 at 21:11
  • The solution here is to use the new IProgress in combination with the new Progress class rather than rolling your own. – A.T. Dec 22 '21 at 21:55
  • 1
    The problem is how to implement an arbitrary interface using a delegate. That the one example used to demonstrate the problem happened to be called `IProgress` is irrelevant to the question. The question did not ask how to update the UI of a desktop application from a background process with the progress of that background task. You're answering *a different question than the one that was asked*. Given that their question mentions nothing about marshaling to a UI thread, it's not even clear that your solution is even applicable to the context in which they're reporting progress. – Servy Dec 22 '21 at 22:02
-2

With .NET 4.5 you can use Progress class

Example:

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

B(reporter);
wensveen
  • 783
  • 10
  • 20
José
  • 3,041
  • 8
  • 37
  • 58
  • This answer is correct when the question is actually about `System.IProgress` which isn't explicitly stated, but it is implied. – wensveen Oct 04 '21 at 11:20