Подпись сбивает с толку. Позвольте мне разъясниться немного:
Я хотел бы обеспечить события, которые зависят от параметра, таким образом, наблюдатель может решить получить события, если что-то происходит с определенным "идентификатором". Это могло быть похожим на это:
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) => {};
Объяснение
Так как Вы, вероятно, задаетесь вопросом, почему я пытаюсь сделать это, я объясню свою ситуацию. У меня есть класс, который обеспечивает положения определенных объектов (каждый из них определенных некоторой Строкой идентификатора).
Вместо того, чтобы обеспечить event EventHandler<PositionChangedEventArgs>
это повышено для ЛЮБЫХ позиционных изменений, я хотел бы иметь событие для каждого объекта (полученный доступ индексом), таким образом, наблюдатели могут слушать события для определенного идентификатора только.
Вы можете сделать что-то вроде этого:
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;
Ваша проблема звучит как проблема построения графика . Имена считаются узлами, а членство в наборе - краями. С этой точки зрения требуется структура данных, которая хорошо обрабатывает разреженные графики, например список смежности . Это, конечно, похоже на то, что вы уже делаете с Dictionary < последовательность, IEnumerable < string > >
, но размышление об этом может привести к некоторым полезным реализациям и алгоритмам.
Либо используйте TRIGGER _ NESTLEVEL () для ограничения рекурсии триггера, либо
проверьте в целевой таблице, необходимо ли ОБНОВЛЕНИЕ вообще:
IF (SELECT COUNT (1)
ОТ users_V1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ вставлено ВКЛ. users_V1.ID = вставлено. Я бы
WHERE users_V1.field1 < > вставления.field1
OR users_V1.field2 < > inserted.field2) > 0 BEGIN
ОБНОВИТЬ НАБОР users_V1...
Необходимо использовать класс, производный от 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
, а затем
Я только начал использовать Rx Framework и он великолепен. Я думаю, это может быть то, что ты ищешь.
http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx
Подписка и отказ от подписки обрабатываются во фреймворке. Он называется LINQ к событиям. Это "математический двойник" IEnumerable.
Ваше здоровье, -jc
Ты имеешь в виду что-то вроде
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;
?
Я нашел в основном один более или менее элегантный способ решения этой проблемы:
Используйте словарь идентификаторов событий. Доступ для добавления / удаления слушателей с помощью методов.
// 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 ();
}
Я не знаю, использовал бы я события в этом случае, но я не уверен, что это действительно самая большая проблема.
Если вы пытаетесь контролировать подписчиков, я думаю, вам лучше позволить подписчикам самим заниматься фильтрацией. Только они знают, что они действительно хотят фильтровать, поэтому размещение кода фильтрации в классе, который испускает события, кажется неоптимальным.
Позвольте мне попытаться немного прояснить, если смогу... Код для определения того, интересует ли получателя A
событие от эмиттера B
, где-то живет. Может показаться, что имеет смысл поместить его в B
. Однако проблема возникает, когда вы понимаете, что вам нужно учитывать также получателей C
, D
и E
. У них может быть сложная логика для определения того, что их волнует (и она может даже меняться с течением времени). Если поместить всю эту логику в наш эмиттер (B
), то получится большой, неуклюжий класс, который трудно использовать.
Другой вариант заключается в том, чтобы A
имел логику о том, хочет ли он получить событие внутренне. Это локализует логику в A
, сохраняет B
чистым и легким для использования всеми остальными. Однако обратной стороной этого является то, что логика подписки не может быть использована в C
, если она окажется такой же.
Но если мы действительно подумаем об этом, то здесь происходят три вещи: испускание события, фильтрация событий для получателей и получение/реакция на события. Принцип единой ответственности говорит нам, что у класса должна быть только одна ответственность - одна причина для изменения. Включив логику фильтрации либо в A
, либо в B
, кто бы из них ни получил ее, теперь у него две ответственности и две причины для изменения.
Итак, в этом случае я бы поступил так: создал еще один класс, Q
, который содержит логику для фильтрации событий. Теперь ни A
, ни B
не получают дополнительной логики в своем коде. C
не нужно ее повторно реализовывать. И, в качестве бонуса, теперь мы можем легко связать несколько фильтров вместе, чтобы получить сложное поведение фильтра на основе очень простых компонентов.
Реализован как единый класс с простым 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 );
}
}
}
}
Как насчет реализации 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:
}
}
Я думаю, что Reactive Extensions для .NET - это именно то, что вы ищете.
Идея * такова:
Во-первых, определите класс, производный от EventArgs
и включающий в себя нужную вам информацию (в частности, любой «индекс», который вы имели в виду). Примерно так:
public class IndexedEventArgs : EventArgs {
public string Index { get; private set; }
public IndexedEventArgs(string index) {
Index = index;
}
// ...
}
Затем для класса, который будет вызывать события, реализуйте одно событие, используя EventHandler
и этот класс, который вы только что определили. В этом определении класса создайте объект, реализующий 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.
Я подготовил полный пример. Вы можете использовать его следующим образом:
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)
{ }
}
}
Класс 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();
}
}
}
Подписки на события класса
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());
}
}
}
}