tho*_*8wp 10 .net c# events .net-2.0
好的,代码结构问题:
假设我有一个类,FruitManager它定期Fruit从某些数据源接收对象.我还有一些其他类需要在收到这些Fruit对象时得到通知.然而,每个类只对某些类型的水果感兴趣,并且每种水果对于如何处理它们具有不同的逻辑.比方说是CitrusLogic类有方法OnFruitReceived(Orange o)和OnFruitReceived(Lemon l),在接收水果的各亚型时应该叫,但它并不需要通知其他水果.
有没有办法在C#中优雅地处理这个问题(可能是事件或代表)?显然我可以添加通用OnFruitReceived(Fruit f)事件处理程序,并使用if语句来过滤不需要的子类,但这看起来不太优雅.有没有人有更好的主意?谢谢!
编辑:我刚刚找到了通用代表,他们似乎是一个很好的解决方案.这听起来像是一个好方向吗?
小智 3
首先,Unity 支持 .NET 3.5 的一个子集,其中特定子集取决于您的构建参数。
继续你的问题,C# 中的一般事件模式是使用委托和 event 关键字。由于您希望仅在传入的水果与其方法定义兼容时才调用处理程序,因此您可以使用字典来完成查找。诀窍在于将委托存储为什么类型。您可以使用一点类型魔法来使其工作并将所有内容存储为
Dictionary<Type, Action<Fruit>> handlers = new Dictionary<Type, Action<Fruit>>();
Run Code Online (Sandbox Code Playgroud)
这并不理想,因为现在所有处理程序似乎都接受Fruit而不是更具体的类型。这只是内部表示,但是,公开人们仍然会通过添加特定的处理程序
public void RegisterHandler<T>(Action<T> handler) where T : Fruit
Run Code Online (Sandbox Code Playgroud)
这使公共 API 保持干净且类型特定。在内部,委托需要从 更改Action<T>为Action<Fruit>。为此,创建一个新的委托,该委托接受 aFruit并将其转换为T.
Action<Fruit> wrapper = fruit => handler(fruit as T);
Run Code Online (Sandbox Code Playgroud)
这当然不是一个安全的转换。如果传递任何不是T(或继承自T)的内容,它将崩溃。这就是为什么它只存储在内部而不暴露在类之外非常重要。将此函数存储在处理程序字典中的Type键下。typeof(T)
接下来调用该事件需要一个自定义函数。该函数需要调用从参数类型一直到继承链到最通用Fruit处理程序的所有事件处理程序。这允许函数在任何子类型参数上触发,而不仅仅是其特定类型。这对我来说似乎是直观的行为,但如果需要的话可以忽略不计。
最后,可以公开一个普通事件,以允许Fruit以通常的方式添加所有处理程序。
下面是完整的示例。请注意,该示例相当小,并且不包括一些典型的安全检查,例如空检查。如果没有从childto的继承链,则还存在潜在的无限循环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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1123 次 |
| 最近记录: |