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

Никто не предоставил « классическое решение Reflection », так что вот полный пример кода:

using System;
using System.Collections;
using System.Collections.Generic;

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

Выше DynamicDictionaryFactory класс имеет метод

CreateDynamicGenericInstance(Type keyType, Type valueType)

, и он создает и возвращает экземпляр IDictionary, типы ключей и значений которых точно указаны в запросе keyType и valueType.

Вот полный пример того, как вызвать этот метод для создания экземпляра и использования Dictionary<String, int>:

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

. Когда указанное консольное приложение выполняется, мы получаем правильный ожидаемый результат:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3
16
задан Malfist 20 March 2009 в 20:01
поделиться

4 ответа

Две опции для Вас:

  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 и заставляете возврат ввести последний параметр типа (Func<string, int>, функция, принимающая строку и возвращающая интервал)

[еще 1124] о делегатах здесь .

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

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);
}
40
ответ дан 30 November 2019 в 15:52
поделиться

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

6
ответ дан 30 November 2019 в 15:52
поделиться

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

1
ответ дан 30 November 2019 в 15:52
поделиться

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

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

1
ответ дан 30 November 2019 в 15:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: