首先,Unity支持一部分.NET 3.5,具体取决于您的构建参数。
接下来回答您的问题,C#中通用的事件模式是使用委托和event关键字。由于您希望仅在传入的水果与其方法定义兼容时调用处理程序,因此可以使用字典来完成查找。诀窍是存储委托的类型。您可以使用一些类型魔法使其正常工作,并将所有内容存储为
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);
}
}
}
}
Fruit<T>
- Daniel A. White