2

In Java, this is normal:

public class Test {
    public static void main(String[] args) throws  Exception {
        new Test().doWork(new Callback() { // implementing class            
            @Override
            public void call() {
                System.out.println("callback called");
            }
        });
    }

    public void doWork(Callback callback) {
        System.out.println("doing work");
        callback.call();
    }

    public interface Callback {
        void call();
    }
}

I wonder if there is equivalence in C#? I experimented in C# but had problem instatiated new interface after doWork() function. Is that because in JAVA you can create an instacne of interface but C# you cannot?

weijia_yu
  • 965
  • 4
  • 14
  • 31

3 Answers3

1

Not the same, but a similar approach(passing a delegate) would be:

DoWork(() => Console.WriteLine("callback called"));

void DoWork(Action callback)
{
    Console.WriteLine("doing work");
    callback();
}

EDIT

especially when I deal with web service which has OnSuccess, OnError.

DoWork(new MyResult() {
    OnSuccess = () => Console.WriteLine("Success"),
    OnError = ()=>Console.WriteLine("Error") }
);

public class MyResult
{
    public Action OnSuccess { set; get; }
    public Action OnError { set; get; }
}

void DoWork(MyResult callback)
{
    Console.WriteLine("doing work");
    callback.OnSuccess();
}
L.B
  • 114,136
  • 19
  • 178
  • 224
  • But this one is much less clean, especially when I deal with web service which has OnSuccess, OnError. – weijia_yu Aug 09 '17 at 23:17
  • 2
    @ywj7931 Generally speaking callbacks like this are not encouraged in C# code to start with. We instead use Tasks with `await/async` – BradleyDotNET Aug 09 '17 at 23:25
0

Java's rather neat 'inline implementations of an interface' / 'inline class definitions' (which you've illustrated above) aren't a part of C#.

You could go ahead and implement listener interfaces/classes and pass them into objects, which is in effect the same thing - however, this can get quite verbose very quickly, and oftentimes you really only want to provide a single callback anyway.

C# has two helpful tools that make life a lot easier:

  1. Delegates
  2. Events

Delegates

It makes sense to understand Delegates before looking at Events. A Delegate is a neat way to pass a method around as a variable or parameter.

You can define Delegate types (if you wish):

public delegate void AMethodThatHandlesAnInteger(int theInt);

And then use them as parameters for methods:

public void doStuffAndThenCallADelegate(AMethodThatHandlesAnInteger theDelegate)
{
  ...
  theDelegate(4);
}

Or, more often than not, it's easier to use one of the pre-defined delegates that already exist:

public void doStuffAndThenCallADelegate(Action<int> theDelegate)
{
  ...
  theDelegate(4);
}

Delegates can be generic - and this makes them a very powerful tool.

There are a number of delegates defined by the framework, (as listed by the handy Q&A linked to). I often find myself using Action (void method, takes no parameters), and Action<Something> (void method, takes a single parameter) in simple cases.

Func<Something,Else> allows a return type, and it gets more interesting from there! Actions and Funcs allow 0-4 parameters (not including the return type for Func).

Events

Whilst it's possible to build your own pattern to monitor object changes using delegates, C# goes one step further and provides ready-made events for you...

Events are an extremely convenient way for an object to collate 'handler' delegate methods, and then to be able to call them all with a set of parameters.

First define the delegate type for the 'handler' methods:

public delegate void StuffHappenedHandler(int stuffs, string summary);

Then by defining a public event, you create a reachable field for registration/deregistration of delegates. I sometimes prefix my events with 'On' - it's a matter for code styling:

public event StuffHappenedHandler OnStuff;

An object that wishes to monitor this event needs a method that matches the StuffHappenedHandler delegate type signature:

public void MonitoredObjectStuffHandler(int howMany, string summary)
{
  ...
}

Now it can register that method with the OnStuff event:

monitoredObject.OnStuff += ObjectStuffHandler;

To deregister a delegate method from the event, it's just as simple:

monitoredObject.OnStuff -= ObjectStuffHandler;

When it comes to notifying objects that might be listening, it's important to check for a null event. The event will be null if there's nothing registered:

if (OnStuff != null)
  OnStuff(4, "four things");
instantiator
  • 114
  • 4
  • My current setup is actually event-driven, but I found its not clear in my situation which is a sequential process, after I succeed in one web request and then continue to the next. use lambda for event is easier to make it clean, but it is kinda hard to unregister the lambda event. – weijia_yu Aug 10 '17 at 01:28
  • If you're doing sequential asynchronous things, perhaps you should invest some time in learning about Rx: https://msdn.microsoft.com/en-us/library/hh242985(v=vs.103).aspx – instantiator Aug 10 '17 at 01:48
  • yeah but in that case I need to import library which I want to avoid – weijia_yu Aug 10 '17 at 03:29
0

If you really want to do something exactly like this in C#, you can use Moq which is a unit testing framework (and is typically only used in unit testing, not production code). I don't recommend actually doing this, as others have suggested C# has alternate patterns that achieve the same goal you are trying to here in a much better way, such as Delegates or async/await. However, creating instances of interfaces is possible via Moq and is incredibly useful for unit testing in C# (especially if you use IoC).

public class Test 
{
    public static int Main(string[] args)
    {
        var mockCallback = new Mock<ICallback>();
        mockCallback.Setup(t=>t.Call()).Callback(() =>
        { 
            Console.WriteLine("callback called");
        });
        new Test().doWork(mockCallback.Object);
    }

    public void DoWork(ICallback callback) 
    {
        Console.WriteLine("doing work");
        callback.Call();
    }
}

public interface ICallback
{
    void Call();
}
Dave M
  • 2,863
  • 1
  • 22
  • 17