Прежде всего, Unity поддерживает подмножество .NET 3.5, где конкретное подмножество зависит от ваших параметров сборки.
Переходя к вашему вопросу, общий шаблон событий в C# заключается в использовании делегатов и ключевого слова события. Поскольку вы хотите, чтобы обработчики вызывались только в том случае, если входящий фрукт совместим с его методом определения, вы можете использовать словарь для выполнения поиска. Трюк - это тип хранения делегатов. Вы можете использовать немного типа магии, чтобы заставить его работать и хранить все как
Dictionary<Type, Action<Fruit>> handlers = new Dictionary<Type, Action<Fruit>>();
Это не идеально, потому что теперь все обработчики, кажется, принимают Fruit
вместо более конкретных типов. Это только внутреннее представление, однако публично люди по-прежнему будут добавлять специальные обработчики через
public void RegisterHandler<T>(Action<T> handler) where T : Fruit
Это позволяет публичному API быть чистым и специфичным для конкретного типа.Внутренне делегат должен измениться с Action<T>
на Action<Fruit>
. Для этого создайте новый делегат, который принимает Fruit
и преобразует его в T
.
Action<Fruit> wrapper = fruit => handler(fruit as T);
Это, конечно, не безопасный литой. Он выйдет из строя, если ему передано что-либо, что не T
(или наследуется от T
). Вот почему очень важно, что он хранится только внутри, а не снаружи класса. Сохраните эту функцию под ключом Type
typeof(T)
в словаре обработчиков.
Для вызова события требуется специальная функция. Эта функция должна вызывать все обработчики событий от типа аргумента вплоть до цепочки наследования до самых общих обработчиков Fruit
. Это позволяет запускать функцию и для любых подтипов, а не только для определенного типа. Это кажется мне интуитивным поведением, но при желании его можно оставить без внимания.
И наконец, обычное событие может быть подвергнуто воздействию, чтобы разрешить использование всех обработчиков Fruit
, которые будут добавлены обычным способом.
Ниже приведен полный пример. Обратите внимание, что пример довольно минимален и исключает некоторые типичные проверки безопасности, такие как проверка нуля. Существует также потенциальный бесконечный цикл, если нет цепи наследования от child
до parent
. Фактическая реализация должна быть расширена по мере возможности. Он также может использовать несколько оптимизаций. В частности, в сценариях с высоким уровнем использования кэширование цепей наследования может быть важным.
public class Fruit { }
class FruitHandlers
{
private Dictionary<Type, Action<Fruit>> handlers = new Dictionary<Type, Action<Fruit>>();
public event Action<Fruit> FruitAdded
{
add
{
handlers[typeof(Fruit)] += value;
}
remove
{
handlers[typeof(Fruit)] -= value;
}
}
public FruitHandlers()
{
handlers = new Dictionary<Type, Action<Fruit>>();
handlers.Add(typeof(Fruit), null);
}
static IEnumerable<Type> GetInheritanceChain(Type child, Type parent)
{
for (Type type = child; type != parent; type = type.BaseType)
{
yield return type;
}
yield return parent;
}
public void RegisterHandler<T>(Action<T> handler) where T : Fruit
{
Type type = typeof(T);
Action<Fruit> wrapper = fruit => handler(fruit as T);
if (handlers.ContainsKey(type))
{
handlers[type] += wrapper;
}
else
{
handlers.Add(type, wrapper);
}
}
private void InvokeFruitAdded(Fruit fruit)
{
foreach (var type in GetInheritanceChain(fruit.GetType(), typeof(Fruit)))
{
if (handlers.ContainsKey(type) && handlers[type] != null)
{
handlers[type].Invoke(fruit);
}
}
}
}
вы могли бы сделать фрукты родовым - '' Фрукты –
@ DanielA.White Я не уверен, я понимаю, как это будет касаться его. Не могли бы вы уточнить? – thomas88wp
Я отредактировал ваш заголовок. Пожалуйста, смотрите: «Если вопросы включают« теги »в их названиях?] (Http://meta.stackexchange.com/questions/19190/), где консенсус« нет, они не должны ». –