Обратные вызовы в С#

Я хочу иметь библиотеку, которая будет иметь в ней функцию, которая принимает объект для этого параметра.

С помощью этого объекта я хочу иметь возможность вызвать указанную функцию, когда X закончен. Функция, которая будет вызываться, должна быть указана вызывающим, и X будет выполняться и контролироваться библиотекой.

Как я могу это сделать?

Для справки я использую С# и .NET 3.5

Ответы

Ответ 1

Предоставление делегата и использование анонимного делегата или выражения Lambda

public static void DoWork(Action processAction)
{
  // do work
  if (processAction != null)
    processAction();
}

public static void Main()
{
  // using anonymous delegate
  DoWork(delegate() { Console.WriteLine("Completed"); });

  // using Lambda
  DoWork(() => Console.WriteLine("Completed"));
}

Или используйте интерфейс

public interface IObjectWithX
{
  void X();
}

public class MyObjectWithX : IObjectWithX
{
  public void X()
  {
    // do something
  }
}

public class ActionClass
{
  public static void DoWork(IObjectWithX handlerObject)
  {
    // do work
    handlerObject.X();
  }
}

public static void Main()
{
  var obj = new MyObjectWithX()
  ActionClass.DoWork(obj);
}

Ответ 2

Звучит как идеальный рецепт для делегатов - в частности, обратные вызовы с делегатами именно так обрабатываются в асинхронном шаблоне в .NET.

Абонент обычно передает вам какое-то состояние и делегат, и вы сохраняете их оба в любом контексте, а затем вызываете делегата, передавая ему состояние и любой результат, который у вас может быть.

Вы можете сделать состояние просто object или потенциально использовать общий делегат и принять состояние соответствующего типа, например.

public delegate void Callback<T>(T state, OperationResult result)

Тогда:

public void DoSomeOperation(int otherParameterForWhateverReason,
                            Callback<T> callback, T state)

Как вы используете .NET 3.5, вы можете использовать существующие Func<...> и Action<...> типы делегатов, но вы можете обнаружить, что он дает четкое объявление о себе. (Имя может уточнить, для чего вы его используете.)

Ответ 3

Объект, о котором идет речь, должен будет реализовать предоставленный вами интерфейс. Возьмите интерфейс в качестве параметра, а затем вы можете вызвать любой метод, который предоставляет интерфейс. В противном случае у вас нет способа узнать, на что способен объект. Это, или вы могли бы взять делегат в качестве параметра и вызвать его.

Ответ 4

Есть ли причина, по которой ваша библиотека не предоставляет публичное мероприятие, которое будет запущено, когда операция будет завершена? Затем вызывающий может просто зарегистрироваться для обработки события, и вам не нужно беспокоиться о передаче объектов или делегатов.

Объект, реализующий интерфейс, который вы предоставили, будет работать, но, похоже, это скорее подход Java, чем подход .NET. События кажутся мне более чистыми.

Ответ 5

Вы можете использовать System.Action, доступный в С#.NET для функций обратного вызова. Пожалуйста, проверьте этот пример:

    //Say you are calling some FUNC1 that has the tight while loop and you need to 
    //get updates on what percentage the updates have been done.
    private void ExecuteUpdates()
    {
        Func1(Info => { lblUpdInfo.Text = Info; });
    }

    //Now Func1 would keep calling back the Action specified in the argument
    //This System.Action can be returned for any type by passing the Type as the template.
    //This example is returning string.
    private void Func1(System.Action<string> UpdateInfo)
    {
        int nCount = 0;
        while (nCount < 100)
        {
            nCount++;
            if (UpdateInfo != null) UpdateInfo("Counter: " + nCount.ToString());
            //System.Threading.Thread.Sleep(1000);
        }
    }