我正在try 创建一个自定义事件,其事件参数包含一个通用参数.我甚至不确定这是否可以做到,或者我只是做得不对.
这里有一个非常简单的例子.它是一个对象类,当其属性之一即将更改时将引发事件.事件参数应包含一个属性,该属性是更改属性的当前值,以便用户可以根据需要取消操作.我知道你可以简单地看一下引发事件的对象,但请忽略这一点,更多地关注这个问题的机制.
此外,我知道这个示例不会编译,但它展示了我正在努力实现的目标.
首先,事件args类:
public class FooPropertyChangingEventArgs<T> : System.EventArgs
{
public FooPropertyChangingEventArgs(string propertyName, T existingValue)
{
this.PropertyName = propertyName;
this.ExistingValue = existingValue;
}
public bool Cancel { get; set; }
public string PropertyName { get; }
public T ExistingValue { get; } // I know that ExistingValue could just be an object type, but I am more interested in finding a way to make it a generic type.
}
代表.这是问题的一部分,因为如果我将类型参数添加到事件参数,我还需要将其添加到事件处理程序:
public delegate void FooPropertyChangingEventHandler<T>(object sender, FooPropertyChangingEventArgs<T> e);
这是我的简单对象类.具有不同数据类型的两个属性:
public class Foo
{
// First problem. Because I added the type argument to the event handler, I need to add it here. However, I can't because at
// this level I have no way of knowing which property will be changed. In fact, the event has to work for all of the properties.
public event FooPropertyChangingEventHandler OnFooPropertyChanging;
private int _myInt = 0;
private string _myString = String.Empty;
public Foo(int myInt, string myString)
{
// Prevent event calls upon object initialization.
this._myInt = myInt;
this._myString = myString;
}
public int MyInt
{
get { return this._myInt; }
set
{
// Here I want to pass an int to any listener.
FooPropertyChangingEventArgs<int> args = new FooPropertyChangingEventArgs<int>("MyInt", this._myInt);
this.OnFooPropertyChanging?.Invoke(this, args);
if(!args.Cancel)
{
this._myInt = value;
}
}
}
public string MyString
{
get { return this._myString; }
set
{
// Here I want to pass a string to any listener.
FooPropertyChangingEventArgs<string> args = new FooPropertyChangingEventArgs<string>("MyString", this._myString);
this.OnFooPropertyChanging?.Invoke(this, args);
if(!args.Cancel)
{
this._myString = value;
}
}
}
}
最后,订阅者:
public class FooSubscriber
{
private Foo _foo;
private int _runningTotal = 0;
private string _runningString = String.Empty;
public FooSubscriber()
{
this._foo = new Foo();
this._foo.FooPropertyChanging += this._foo_PropertyChangingEventArgs;
}
// This is what should be called by a consumer to change the properties and raise the events.
public void ChangeFoo(int newMyInt, string newMyString)
{
this._foo.MyInt = newMyInt;
this._foo.MyString = newMyString;
}
// This is also part of the problem. The compiler expects the event args to have a type argument (e.g., FooPropertyChangingEventArgs<T>), and I understand
// that at this level there is no way to know which property is being raised. Or is there a way I am missing?
private void _foo_PropertyChangingEventArgs(object sender, FooPropertyChangingEventArgs e)
{
// No changes allowed in June. Stupid, but this demonstrates how we want to cancel if "something" happens.
if(DateTime.Now.Month == 6)
{
e.Cancel = true;
}
else
{
// If I could make this work, I would expect to be able to do something like this here:
switch(e.PropertyName)
{
case "MyInt":
// I would expect ExistingValue to be an int.
this._runningTotal += e.ExistingValue + 1;
break;
case "MyString":
// I would expect ExistingValue to be a string.
this._runningString += e.ExistingValue.ToUpper();
break;
default:
this._runningString = 0;
this._runningString = String.Empty;
break;
}
}
}
}
我知道可能有什么问题.也就是说,订阅者不可能知道事件args属性是什么数据类型.对吗?我认为泛型应该通过在运行时确定数据类型来解决这个问题.如何指定希望事件参数具有泛型属性,而不强制事件处理程序执行该行为?
那么,这能做到吗?我错过什么了吗?
提前感谢,