我正在尝试一种对象可以用来触发自己事件的扩展方法。
我的工作几乎是我想要的,但想知道我是否可以改进它,以便传递的参数可以转换为EventArgs的构造函数参数而不需要使用Activator。
我会提前说我很怀疑这是可能的,但我会试一试,因为有时我真的对其他人的编码技巧感到惊讶......
void Main()
{
var c = new C();
c.E += (s, e) => Console.WriteLine (e.Message);
c.Go();
}
public class C
{
public event EventHandler<Args> E;
public void Go()
{
Console.WriteLine ("Calling event E...");
// This version doesn't know the type of EventArgs so it has to use Activator
this.Fire(E, "hello");
// This version doesn't know ahead of time if there are any subscribers so it has to use a delegate
this.Fire(E, () => new Args("world"));
// Is there some way to get the best of both where it knows the type but can delay the
// creation of the event args?
//this.Fire<Args>("hello");
}
}
public class Args : EventArgs
{
public Args(string s)
{
Message = s;
}
public string Message { get; set; }
}
public static class Ext
{
public static void Fire<T>(this object source, EventHandler<T> eventHander, Func<T> eventArgs) where T : EventArgs
{
if (eventHander != null)
eventHander(source, eventArgs());
}
public static void Fire<T>(this object source, EventHandler<T> eventHander, params object[] args) where T : EventArgs
{
if (eventHander != null)
eventHander(source, (T)Activator.CreateInstance(typeof(T), args));
}
}
答案 0 :(得分:2)
之前我做过类似的事情,但我采取了使用新的EventArgs / EventHandler包装器的路径。它使用隐式转换和泛型来自动处理与事件args的转换。
public delegate void DataEventHandler<TSender, TEventArgs>(TSender sender, DataEventArgs<TEventArgs> eventArgs);
public delegate void DataEventHandler<TEventArgs>(DataEventArgs<TEventArgs> eventArgs);
public class DataEventArgs<TEventArgs>
{
public TEventArgs Args { get; private set; }
public DataEventArgs(TEventArgs args)
{
this.Args = args;
}
public static implicit operator TEventArgs(DataEventArgs<TEventArgs> args)
{
return args.Args;
}
public static implicit operator DataEventArgs<TEventArgs>(TEventArgs args)
{
return new DataEventArgs<TEventArgs>(args);
}
}
我在发送/不发送过载时,可能不是一个好主意,但你至少可以玩它。
然后扩展方法而不是放置在类型object
上而不是因为所有对象(我认为)使它在智能感知中显示/可用,即使它不是真的适用,我把它绑定到DataEventHandlers本身:
public static class MyExtensions
{
public static void Fire<TSender, TEventArgs>(this DataEventHandler<TSender, TEventArgs> eventHandler, TSender sender, TEventArgs args)
{
if (eventHandler!= null)
eventHandler(sender, args);
}
public static void Fire<TEventArgs>(this DataEventHandler<TEventArgs> eventHandler, TEventArgs args)
{
if (eventHandler != null)
eventHandler(args);
}
}
(注意我把它放在与DataEventHandler
相同的命名空间中,因此它也可以自动使用/导入它们,假设您使用其命名空间作为using语句的事件)
扩展方法已经知道了参数类型,但它还没有作为args对象传入。相反,它会作为原始类型传递,然后仅在最终调用eventHandler(sender, args)
中,如果事件具有注册人,它会隐式转换为事件args 。
您的C
课程可能如下:
public class C
{
public event DataEventHandler<string> E;
public event DataEventHandler<C, string> EWithSender;
public void Go()
{
Console.WriteLine ("Calling event E...");
E.Fire("hello");
EWithSender.Fire(this, "hello");
}
}
请注意,C
中的事件声明未使用DataEventHandler<DataEventArgs<string>>
明确标记自己;这是由委托参数隐式处理的。
您的主叫代码可能如下所示:
C c = new C();
c.E += (args) => PrintOut(args);
c.EWithSender += (sender, args) => Console.WriteLine("Sender Type: " + sender.GetType().Name + " -> Args: " + args.Args);
c.Go();
private void PrintOut(string text)
{
Console.WriteLine(text);
}
同样,事件args 可以(但你不必)在传递给方法时隐式转换回它们的包装数据类型。
现在,这有一些缺点。主要是,在我看来,它有点违反标准的.NET EventHandler关于打字的做法,很难制作你自己的事件args等等。特别是因为我最终没有创建自己的EventArgs子类而只是传递一些数据对象(基本值类型,或我自己的自定义类或数据模型)。它很好地服务于我,但实际上我发现它越来越没用了。我不是在提倡这种风格/实现,但也许它会给你一些想法。