Несмотря на то, что это старый пост, для этого стоит третье решение.
Я был обеспокоен общей проблемой создания таблиц отправки, например, статические таблицы поиска нестатических методов. Типичное использование может быть в обработке событий ASP.NET.
Мы бы хотели, чтобы синтаксис и инициализация были максимально простыми. Если объявление и инициализация таблицы диспетчеризации слишком сложны, было бы проще / безопаснее просто написать явный оператор If-then-else-if или switch, который выполняет диспетчеризацию.
Мы можем очень просто объявить собрание делегатов.
Предполагая некоторые методы Method1, Method2 и тип делегата SomeDelegate для них, тогда мы можем написать:
Dictionary<string, SomeDelegate> dispatchTable = new Dictionary<string, SomeDelegate>
{
{ "Key1", Method1 }
,{ "Key2", Method2 }
....
}
В этом случае мы инициализируем делегатов, используя имя метода напрямую.
Хотя, к сожалению, это работает, оно потерпит неудачу, как только мы попытаемся сделать dispatchTable статическим элементом.
Это связано с тем, что SomeDelegate является закрытым делегатом (связанным с экземпляром) и поэтому не может быть инициализирован из статической области.
Это разочаровывает, поскольку наши требуемые отправки известны во время компиляции, поэтому в идеале мы должны иметь возможность объявлять нашу таблицу отправки статически.
Как указано в выбранном решении в этой теме, вы можете создавать открытые делегаты через CreateDelegate, но это синтаксически неудобно и также полагается на передачу имени метода в виде строки для создания делегата, поэтому вы теряете проверку времени компиляции. Объявление таблицы диспетчеризации с использованием этого синтаксиса было бы очень грязным.
Техника метода расширения менее многословна и сохраняет проверку времени компиляции, но она все еще неудобна по сравнению с синтаксисом выше.
Другой (третий) вариант - заключить закрытые делегаты в (связывающую) функцию, которая с учетом экземпляра класса вернет желаемый (закрытый) делегат. Например, вы можете использовать Func.
Тогда таблица отправки в основном:
public class DispatchTable<Class, Key, Delegate> : Dictionary<Key, Func<Class, Delegate>>
Предполагается, что некоторые методы называются EventHandler1, EventHandler2 и тип делегата для них, например,
delegate int EventHandler(string param1, int param2);
затем объявление и инициализация статической таблицы отправки нестатических элементов так же просто, как:
class MyDispatchTable : DispatchTable<MyClass, string, EventHandler>
static MyDispatchTable dispatchTable = new MyDispatchTable
{
{ "Event1", c => c.EventHandler1 }
,{ "Event2", c => c.EventHandler2 }
};
Методы теперь могут вызываться через таблицу диспетчеризации с учетом экземпляра класса, ключа для обработчика и параметров метода.
В качестве примера, при вызове функции-члена класса iteself, т.е. экземпляра класса = this, для ключа k и параметров p1, p2 синтаксис будет:
var result = dispatchTable[key](this)(p1, p2);
Обратите внимание, что при этом игнорируется соответствующая проверка ошибок, например, несуществующие ключи. Проверка ошибок может быть заключена в метод GetDelegate класса DispatchTable.
Полный пример приведен ниже. Обратите внимание, что он также включает отдельный метод расширения для класса Dictionary, чтобы упростить синтаксис для обработки ошибок.
Расширение словаря:
static public class DictionaryExtensions
{
// extension method to simplify accessing a dictionary
static public V GetValueOrDefault<K, V>(this Dictionary<K, V> dict, K key)
{
V value;
dict.TryGetValue(key, out value);
return value;
}
}
Таблица отправки класса:
// Syntactic sugar for declaring a general dispatch table
// The dictionary maps from a key to a function that can return
// a closed delegate given an instance of a class.
// Note that if keys are always integers then it is simpler to use an
// array rather than a dictionary.
public class DispatchTable<Key, Class, Delegate> : Dictionary<Key, Func<Class, Delegate>>
{
// standardise the method for accessing a delegate
public Delegate GetDelegate(Class c, Key k)
{
var d = GetValueOrDefault(k);
if (d == null)
{
throw new ArgumentException(String.Format("Delegate not found for key [{0}]",k));
}
return d(c);
}
};
Пример использования:
public class Test
{
// some member functions to invoke
public int EventHandler1(string param1, int param2) { return 1; }
public int EventHandler2(string param1, int param2) { return 2; }
// Declaration for a (closed) delegate for the member functions
private delegate int EventHandler(string param1, int param2);
// Syntactic sugar for declaring the table
private class EventDispatchTable : DispatchTable<string, Test, EventHandler> { };
// Declare dispatch table and initialize
static EventDispatchTable dispatchTable = new EventDispatchTable
{
{ "Event1", c => c.EventHandler1 }
,{ "Event2", c => c.EventHandler2 }
};
// Invoke via the dispatch table
public int DoDispatch(string eventName, string param1, int param2)
{
return dispatchTable.GetDelegate(this, eventName)(param1, param2);
}
}