标题令人困惑。让我澄清一下:
我想提供依赖于参数的事件,以便观察者可以决定在特定“id”发生某些事件时接收事件。它看起来像这样:
public event EventHandler Foo (string id);
我知道这种语法在.NET 3.5中是错误的,我也知道这个想法引入了额外的问题(例如,我们如何管理取消订阅?)。
我该如何规避这个问题?我想过使用类似的东西:
public EventHandler Foo (string id);
这至少是合法的语法并且可以工作,但它对我来说仍然不是很好。
编辑:我不询问是否将参数传递给回调函数。我的想法更像是这样:
class Bleh
{
public event EventHandler Foo (string index);
private void RaiseEvents() // this is called by a timer or whatever
{
Foo["asdf"] (this, EventArgs.Empty); // raises event for all subscribers of Foo with a parameter of "asdf"
Foo["97"] (this, EventArgs.Empty); // same for all "97"-subscribers
// above syntax is pure fiction, obviously
}
}
// subscribe for asdf events via:
Bleh x = new Bleh ();
x.Foo["asdf"] += (s, e) => {};
说明的
既然你可能想知道我为什么要这样做,我会解释我的情况。我有一个提供某些对象位置的类(每个对象都由一些ID字符串标识)。
我没有提供为任何位置更改而引发的event EventHandler<PositionChangedEventArgs>
,而是希望每个对象都有一个事件(由索引访问),因此观察者只能侦听特定ID的事件
答案 0 :(得分:16)
你可以这样做:
public class Foo
{
public class Bar
{
public event EventHandler PositionChanged;
internal void RaisePositionChanged()
{
var handler = PositionChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
private Dictionary<string, Bar> m_objects;
public Bar this[string id]
{
get
{
if (!m_objects.ContainsKey(id))
m_objects.Add(id, new Bar());
return m_objects[id];
}
}
private void RaisePositionChanged(string id)
{
Bar bar;
if (m_objects.TryGetValue(id, out bar))
bar.RaisePositionChanged();
}
}
然后订阅一个事件,就像这样简单:
Foo foo = new Foo();
foo["anId"].PositionChanged += YourHandler;
答案 1 :(得分:8)
您需要使用包含ID的EventArgs派生类,然后使用EventHandler<IdEventArgs>
或其他:
public class IdEventArgs : EventArgs
{
private readonly string id;
public string Id { get { return id; } }
public IdEventArgs(string id)
{
this.id = id;
}
}
public event Eventhandler<IdEventArgs> Foo;
当你举起活动时,你需要创建一个IdEventArgs
的实例,然后订阅者可以检查它并决定如何处理它。
答案 2 :(得分:5)
我刚刚开始使用Rx框架,它很棒。我想这可能就是你要找的东西。
http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx
订阅和取消订阅在框架中处理。它被称为LINQ to events。它是IEnumerable的“数学对偶”。
干杯, -jc
答案 3 :(得分:5)
我认为Reactive Extensions for .NET 正是您正在寻找的内容。
这个想法是这样的:
首先,定义一个派生自EventArgs
的类,并包含您想要的信息(特别是您想到的“索引”)。像这样:
public class IndexedEventArgs : EventArgs {
public string Index { get; private set; }
public IndexedEventArgs(string index) {
Index = index;
}
// ...
}
接下来,对于将要引发事件的类,使用EventHandler<TEventArgs>
和您刚定义的此类实现单个事件。在此类定义中,创建一个实现IObservable
的对象,如下所示:
public class ClassWithIndexedEvents {
public event EventHandler<IndexedEventArgs> IndexedEvent;
public IObservable Events { get; private set; }
public ClassWithIndexedEvents() {
// yeah, this feels a little weird, but it works
Events = Observable.FromEvent<IndexedEventArgs>(this, "IndexedEvent");
}
// ...
}
现在,在您只想订阅与特定索引匹配的事件的代码中,您可以像Events
一样过滤IEnumerable
属性:
// code mangled to fit without scrolling
public IDisposable CreateSubscription(
string eventIndex,
Action<IEvent<IndexedEventArgs>> handler) {
return Events.Where(e => e.Index == eventIndex).Subscribe(handler);
}
请注意Subscribe
方法返回IDisposable
个对象;这是您刚刚订阅的过滤事件中取消订阅的关键。代码非常明显:
var fooSubscription = objectWithIndexedEvents.CreateSubscription(
"foo",
e => DoSomething(e)
);
// elsewhere in your code
fooSubscription.Dispose();
* 免责声明:我或多或少地记录了Rx的工作原理;我没有测试过,因为我没有在我正在使用的机器上安装Rx。我明天可以在另一台机器上检查,以确保它写得正确;现在它应该至少可以作为一个例子,让你了解Rx如何工作。要了解更多信息,您可以随时在线查找Rx教程。
答案 4 :(得分:2)
我不知道在这种情况下我是否会使用事件,但我不确定这是否是最大的问题。
如果您正在尝试控制订阅者,我认为您最好让订阅者处理过滤。只有他们知道他们真正想要过滤的内容,所以将过滤代码放在发布事件的类中似乎是次优的。
让我尝试澄清一下,如果可以的话...确定收件人A
是否关心来自发射器B
的事件的代码。将它放在B
中似乎是有意义的。但是,当您意识到必须考虑收件人C
,D
和E
时,问题才出现。他们可能有复杂的逻辑来确定他们关心的东西(甚至可能会改变它)。将所有这些逻辑放在我们的发射器(B
)中将会产生一个难以使用的大型,笨拙的类。
另一种选择是让A
具有关于它是否想要内部事件的逻辑。这将逻辑本地化为A
,使B
保持清洁,并且易于被其他人使用。但是,这样做的缺点是如果C
恰好相同,则订阅逻辑不可用。
但是如果我们真的想到它,我们在这里发生了三件事......发出的事件,向接收者过滤事件,以及接收/反应事件。单一责任原则告诉我们,一个班级应该只有责任 - 一个改变的理由。通过在A
或B
中包含过滤逻辑,无论哪个获取它现在都有两个职责,并且两个原因需要更改。
因此,在这种情况下,我想要做的是创建另一个类Q
,它包含事件过滤的逻辑。现在,A
或B
都没有在代码中获得额外的逻辑。 C
无需重新实施。而且,作为奖励,我们现在可以轻松地将多个过滤器绑定在一起,以便根据非常简单的组件获得复杂的过滤器行为。
答案 5 :(得分:2)
我准备了一个完整的例子。您可以这样使用它:
eventsSubscriptions["1"].EventHandler = new EventHandler(this.Method1);
eventsSubscriptions["2"].EventHandler = new EventHandler(this.Method2);
eventsSubscriptions["3"].EventHandler = new EventHandler(this.Method3);
Boss Boss1 = new Boss("John Smith");
Boss Boss2 = new Boss("Cynthia Jameson");
Employed Employed1 = new Employed("David Ryle");
Employed Employed2 = new Employed("Samantha Sun");
Employed Employed3 = new Employed("Dick Banshee");
// Subscribe objects to Method 1
eventsSubscriptions["1"].Subscribe(Boss1);
eventsSubscriptions["1"].Subscribe(Employed1);
// Subscribe objects to Method 2
eventsSubscriptions["2"].Subscribe(Boss2);
eventsSubscriptions["2"].Subscribe(Employed2);
// Subscribe objects to Method 3
eventsSubscriptions["3"].Subscribe(Employed3);
然后,您可以调用RaiseAllEvents()方法,这是控制台输出:
在以下几行中,我将粘贴所涉及的所有类的代码。有一点耐心和复制/粘贴,你就可以测试它= P希望它可以帮助你。
---守则---
主要强>
namespace MyExample
{
public class Program
{
static void Main(string[] args)
{
SomeExampleClass someExampleInstance = new SomeExampleClass();
someExampleInstance.SuscribeObjects();
someExampleInstance.RaiseAllEvents();
Console.ReadLine();
}
}
}
班级人员
namespace MyExample
{
public abstract class Person
{
protected string name;
public Person(string name)
{
this.name = name;
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public override string ToString()
{
return (this.GetType().Name + " " + name);
}
}
}
班级老板
namespace MyExample
{
public class Boss : Person
{
public Boss(string name)
: base(name)
{ }
}
}
<强>员工强>
namespace MyExample
{
public class Employee : Person
{
public Employee(string name)
: base(name)
{ }
}
}
Class SomeExampleClass
namespace MyExample
{
public class SomeExampleClass
{
private EventsSubscriptions eventsSubscriptions = new EventsSubscriptions();
private void Method1(object sender, System.EventArgs e)
{
Console.WriteLine("Method 1 raised with " + sender.ToString());
}
private void Method2(object sender, System.EventArgs e)
{
Console.WriteLine("Method 2 raised with " + sender.ToString());
}
private void Method3(object sender, System.EventArgs e)
{
Console.WriteLine("Method 3 raised with " + sender.ToString());
}
public void SuscribeObjects()
{
eventsSubscriptions["1"].EventHandler = new EventHandler(this.Method1);
eventsSubscriptions["2"].EventHandler = new EventHandler(this.Method2);
eventsSubscriptions["3"].EventHandler = new EventHandler(this.Method3);
Boss Boss1 = new Boss("John Smith");
Boss Boss2 = new Boss("Cynthia Jameson");
Employee Employee1 = new Employee("David Ryle");
Employee Employee2 = new Employee("Samantha Sun");
Employee Employee3 = new Employee("Dick Banshee");
// Method 1
eventsSubscriptions["1"].Subscribe(Boss1);
eventsSubscriptions["1"].Subscribe(Employee1);
//// Method 2
eventsSubscriptions["2"].Subscribe(Boss2);
eventsSubscriptions["2"].Subscribe(Employee2);
//// Method 3
eventsSubscriptions["3"].Subscribe(Employee3);
}
public void RaiseAllEvents()
{
eventsSubscriptions.RaiseAllEvents();
}
}
}
类EventsSubscriptions
namespace MyExample
{
public class EventsSubscriptions
{
private Dictionary<string, Subscription> subscriptions = new Dictionary<string, Subscription>();
public Subscription this[string id]
{
get
{
Subscription subscription = null;
subscriptions.TryGetValue(id, out subscription);
if (subscription == null)
{
subscription = new Subscription();
subscriptions.Add(id, subscription);
}
return subscription;
}
}
public void RaiseAllEvents()
{
foreach (Subscription subscription in subscriptions.Values)
{
Subscription iterator = subscription;
while (iterator != null)
{
iterator.RaiseEvent();
iterator = iterator.NextSubscription;
}
}
}
}
}
类订阅
namespace MyExample
{
public class Subscription
{
private object suscribedObject;
private EventHandler eventHandler;
private Subscription nextSubscription;
public object SuscribedObject
{
set
{
suscribedObject = value;
}
}
public EventHandler EventHandler
{
set
{
eventHandler = value;
}
}
public Subscription NextSubscription
{
get
{
return nextSubscription;
}
set
{
nextSubscription = value;
}
}
public void Subscribe(object obj)
{
if (suscribedObject == null)
{
suscribedObject = obj;
}
else
{
if (nextSubscription != null)
{
nextSubscription.Subscribe(obj);
}
else
{
Subscription newSubscription = new Subscription();
newSubscription.eventHandler = this.eventHandler;
nextSubscription = newSubscription;
newSubscription.Subscribe(obj);
}
}
}
public void RaiseEvent()
{
if (eventHandler != null)
{
eventHandler(suscribedObject, new System.EventArgs());
}
}
}
}
答案 6 :(得分:1)
我基本上找到了解决这个问题的优雅方法:
使用ids字典来表示事件。访问通过方法添加/删除侦听器。
// ignore threadsafety and performance issues for now.
private Dictionary<string, EventHandler> _Events = new Dictionary<string, EventHandler> ();
private void AddId (string id)
{
_Events[id] = delegate {
};
}
public void Subscribe (string id, EventHandler handler)
{
_Events[id] += handler;
}
public void Unsubscribe (string id, EventHandler handler)
{
_Events[id] -= handler;
}
private void Raise (string id)
{
_Events[id] (this, new EventArgs ());
}
static void Main (string[] args)
{
var p = new Program ();
p.AddId ("foo");
p.Subscribe ("foo", (s, e) => Console.WriteLine ("foo"));
p.Raise ("foo");
p.AddId ("bar");
p.Subscribe ("bar", (s, e) => Console.WriteLine ("bar 1"));
p.Subscribe ("bar", (s, e) => Console.WriteLine ("bar 2"));
p.Raise ("bar");
Console.ReadKey ();
}
答案 7 :(得分:0)
你的意思是
public class EventArgs<T> : EventArgs
{
private T _value;
public T Value
{
get { return this._value; }
protected set { this._value = value; }
}
public EventArgs(T value)
{
this.Value = value;
}
}
// ...
public event EventHandler<EventArgs<string>> Foo;
答案 8 :(得分:0)
使用简单的API实现为单个类。
// subscribe to an event
eventsource.AddHandler( "foo", MyEventHandler );
// unsubscribe
eventsource.RemoveHandler( "foo", MyEventHandler );
// raise event for id
eventsource.RaiseEvent( "foo" );
public class EventSource
{
Dictionary<string,List<EventHandler>> handlers = new Dictionary<string,List<EventHandler>>();
public void AddHandler( string id, EventHandler handler )
{
if (!handlers.ContainsKey( id )) {
handlers[id] = new List<EventHandler>();
}
handlers[id].Add( handler );
}
public void RemoveHandler( string id, EventHandler handler )
{
if (handlers.ContainsKey( id )) {
handlers[id].Remove( handler );
}
}
public void RaiseEvent( string id )
{
if (handlers.ContainsKey( id )) {
foreach( var h in handlers[id] ) {
h( this, EventArgs.Empty );
}
}
}
}
答案 9 :(得分:0)
如何实施INotifyPropertyChanged呢?
然后......
protected void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void OnSourcePropertyChanged(Object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName == "InterestingName")
{
// TODO:
}
}