Там какой-либо путь состоит в том, чтобы создать индексированные события в C# (или некоторое обходное решение)?

Подпись сбивает с толку. Позвольте мне разъясниться немного:

Я хотел бы обеспечить события, которые зависят от параметра, таким образом, наблюдатель может решить получить события, если что-то происходит с определенным "идентификатором". Это могло быть похожим на это:

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> это повышено для ЛЮБЫХ позиционных изменений, я хотел бы иметь событие для каждого объекта (полученный доступ индексом), таким образом, наблюдатели могут слушать события для определенного идентификатора только.

17
задан mafu 6 March 2010 в 22:40
поделиться

10 ответов

Вы можете сделать что-то вроде этого:

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;
16
ответ дан 30 November 2019 в 11:37
поделиться

Ваша проблема звучит как проблема построения графика . Имена считаются узлами, а членство в наборе - краями. С этой точки зрения требуется структура данных, которая хорошо обрабатывает разреженные графики, например список смежности . Это, конечно, похоже на то, что вы уже делаете с Dictionary < последовательность, IEnumerable < string > > , но размышление об этом может привести к некоторым полезным реализациям и алгоритмам.

-121--3753588-
  • Либо используйте 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...
    
-121--3579188-

Необходимо использовать класс, производный от 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 , а затем

7
ответ дан 30 November 2019 в 11:37
поделиться

Я только начал использовать Rx Framework и он великолепен. Я думаю, это может быть то, что ты ищешь.

http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx

Подписка и отказ от подписки обрабатываются во фреймворке. Он называется LINQ к событиям. Это "математический двойник" IEnumerable.

Ваше здоровье, -jc

5
ответ дан 30 November 2019 в 11:37
поделиться

Ты имеешь в виду что-то вроде

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;

?

0
ответ дан 30 November 2019 в 11:37
поделиться

Я нашел в основном один более или менее элегантный способ решения этой проблемы:

Используйте словарь идентификаторов событий. Доступ для добавления / удаления слушателей с помощью методов.

// 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 ();
}
1
ответ дан 30 November 2019 в 11:37
поделиться

Я не знаю, использовал бы я события в этом случае, но я не уверен, что это действительно самая большая проблема.

Если вы пытаетесь контролировать подписчиков, я думаю, вам лучше позволить подписчикам самим заниматься фильтрацией. Только они знают, что они действительно хотят фильтровать, поэтому размещение кода фильтрации в классе, который испускает события, кажется неоптимальным.

Позвольте мне попытаться немного прояснить, если смогу... Код для определения того, интересует ли получателя A событие от эмиттера B, где-то живет. Может показаться, что имеет смысл поместить его в B. Однако проблема возникает, когда вы понимаете, что вам нужно учитывать также получателей C, D и E. У них может быть сложная логика для определения того, что их волнует (и она может даже меняться с течением времени). Если поместить всю эту логику в наш эмиттер (B), то получится большой, неуклюжий класс, который трудно использовать.

Другой вариант заключается в том, чтобы A имел логику о том, хочет ли он получить событие внутренне. Это локализует логику в A, сохраняет B чистым и легким для использования всеми остальными. Однако обратной стороной этого является то, что логика подписки не может быть использована в C, если она окажется такой же.

Но если мы действительно подумаем об этом, то здесь происходят три вещи: испускание события, фильтрация событий для получателей и получение/реакция на события. Принцип единой ответственности говорит нам, что у класса должна быть только одна ответственность - одна причина для изменения. Включив логику фильтрации либо в A, либо в B, кто бы из них ни получил ее, теперь у него две ответственности и две причины для изменения.

Итак, в этом случае я бы поступил так: создал еще один класс, Q, который содержит логику для фильтрации событий. Теперь ни A, ни B не получают дополнительной логики в своем коде. C не нужно ее повторно реализовывать. И, в качестве бонуса, теперь мы можем легко связать несколько фильтров вместе, чтобы получить сложное поведение фильтра на основе очень простых компонентов.

2
ответ дан 30 November 2019 в 11:37
поделиться

Реализован как единый класс с простым 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 );
            }
        }       
    }
}
0
ответ дан 30 November 2019 в 11:37
поделиться

Как насчет реализации 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:
    }
}
0
ответ дан 30 November 2019 в 11:37
поделиться

Я думаю, что 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.

5
ответ дан 30 November 2019 в 11:37
поделиться

Я подготовил полный пример. Вы можете использовать его следующим образом:

        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 (), и это будет вывод консоли:

  • Метод 1, созданный боссом Джоном Смитом
  • Метод 1, созданный сотрудником Дэвидом Райлом
  • Метод 2, созданный боссом Синтией Джеймсон
  • Метод 2, созданный сотрудником Самантой Сан
  • Метод 3, созданный сотрудником Диком Банши

В следующие строки я вставлю код всех задействованных классов. Приложив немного терпения и скопировав / вставив, вы сможете проверить это = 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());
            }
        }
    }
}
2
ответ дан 30 November 2019 в 11:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: