Обратные вызовы в C # - PullRequest
       28

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

16 голосов
/ 20 марта 2009

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

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

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

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

Ответы [ 5 ]

40 голосов
/ 20 марта 2009

Два варианта для вас:

  1. Пусть функция принимает делегат (Action для обратного вызова, который ничего не возвращает, Func для одного, который делает) и использовать анонимный делегат или лямбда-выражение при вызове.

  2. Использовать интерфейс

Использование делегата / лямбда

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

Если вашему обратному вызову нужно что-то передать, вы можете использовать параметр типа в Action:

public static void DoWork(Action<string> processAction)
{
  // do work
  if (processAction != null)
    processAction("this is the string");
}

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

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

Если требуется несколько аргументов, вы можете добавить больше параметров типа к Action. Если вам нужен возвращаемый тип, как уже упоминалось, используйте Func и сделайте возвращаемый тип параметром last (Func<string, int> - это функция, принимающая строку и возвращающая int.)

Подробнее о делегатах здесь .

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

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);
}
6 голосов
/ 20 марта 2009

Похоже, идеальный рецепт для делегатов - в частности, обратные вызовы с делегатами именно так и обрабатываются в асинхронном шаблоне в .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<...> типы делегатов, но вы можете найти, что вам будет проще объявить свой собственный. (Название может прояснить, для чего вы его используете.)

1 голос
/ 20 марта 2009

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

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

1 голос
/ 20 марта 2009

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

0 голосов
/ 05 ноября 2015

Вы можете использовать System.Action, доступный в C # .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);
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...