Вызывать событие из неизвестного элемента управления [дубликат]

Краткий ответ: ваш метод foo() возвращается немедленно, а вызов $ajax() выполняется асинхронно после возврата функции . Проблема заключается в том, как и где сохранить результаты, полученные при вызове async, после его возврата.

В этом потоке было задано несколько решений. Возможно, самый простой способ - передать объект методу foo() и сохранить результаты в члене этого объекта после завершения асинхронного вызова.

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

Обратите внимание, что вызов foo() ] все равно не вернут ничего полезного. Однако результат асинхронного вызова теперь будет сохранен в result.response.

21
задан Jonathan Nixon 19 September 2013 в 21:37
поделиться

11 ответов

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

obj.AfterSearch += handler; (где обработчик - это метод по сигнатуре AfterSearch) , Один может подписаться на событие извне просто отлично, но он может быть поднят только внутри класса, определяющего его.

30
ответ дан Femaref 31 August 2018 в 23:29
поделиться

Я тоже наткнулся на эту проблему, потому что я экспериментировал с вызовом событий PropertyChanged извне. Поэтому вам не нужно выполнять все в каждом классе. Решение из halorty не будет работать с использованием интерфейсов.

Я нашел решение, использующее тяжелое отражение. Это, безусловно, медленно и нарушает принцип, согласно которому события следует вызывать только внутри класса. Но интересно найти общее решение этой проблемы ....

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

Здесь вы идете ....

class Program
{
  static void Main(string[] args)
  {
    var instance = new TestPropertyChanged();
    instance.PropertyChanged += PropertyChanged;

    instance.RaiseEvent(nameof(INotifyPropertyChanged.PropertyChanged), new PropertyChangedEventArgs("Hi There from anywhere"));
    Console.ReadLine();
  }

  private static void PropertyChanged(object sender, PropertyChangedEventArgs e)
  {
    Console.WriteLine(e.PropertyName);
  }
}

public static class PropertyRaiser
{
  private static readonly BindingFlags staticFlags = BindingFlags.Instance | BindingFlags.NonPublic;

  public static void RaiseEvent(this object instance, string eventName, EventArgs e)
  {
    var type = instance.GetType();
    var eventField = type.GetField(eventName, staticFlags);
    if (eventField == null)
      throw new Exception($"Event with name {eventName} could not be found.");
    var multicastDelegate = eventField.GetValue(instance) as MulticastDelegate;
    if (multicastDelegate == null)
      return;

    var invocationList = multicastDelegate.GetInvocationList();

    foreach (var invocationMethod in invocationList)
      invocationMethod.DynamicInvoke(new[] {instance, e});
  }
}

public class TestPropertyChanged : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
}
1
ответ дан feal 31 August 2018 в 23:29
поделиться

ВОЗМОЖНО, но используя умный взлом.

Вдохновленный http://netpl.blogspot.com/2010/10/is-net-type-safe.html

Если вы не верите, попробуйте этот код.

using System;
using System.Runtime.InteropServices;

namespace Overlapping
{
    [StructLayout(LayoutKind.Explicit)]
    public class OverlapEvents
    {
        [FieldOffset(0)]
        public Foo Source;

        [FieldOffset(0)]
        public OtherFoo Target;
    }

    public class Foo
    {
        public event EventHandler Clicked;

        public override string ToString()
        {
            return "Hello Foo";
        }

        public void Click()
        {
            InvokeClicked(EventArgs.Empty);
        }

        private void InvokeClicked(EventArgs e)
        {
            var handler = Clicked;
            if (handler != null)
                handler(this, e);
        }
    }

    public class OtherFoo
    {
        public event EventHandler Clicked;

        public override string ToString()
        {
            return "Hello OtherFoo";
        }

        public void Click2()
        {
            InvokeClicked(EventArgs.Empty);
        }

        private void InvokeClicked(EventArgs e)
        {
            var handler = Clicked;
            if (handler != null)
                handler(this, e);
        }

        public void Clean()
        {
            Clicked = null;
        }
    }

    class Test
    {
        public static void Test3()
        {
            var a = new Foo();
            a.Clicked += AClicked;
            a.Click();
            var o = new OverlapEvents { Source = a };
            o.Target.Click2();
            o.Target.Clean();

            o.Target.Click2();
            a.Click();
        }

        static void AClicked(object sender, EventArgs e)
        {
            Console.WriteLine(sender.ToString());
        }
    }
}
18
ответ дан halorty 31 August 2018 в 23:29
поделиться

Не очень хорошее программирование, но если вы хотите сделать это любым способом, вы можете сделать что-то вроде этого

class Program
{
    static void Main(string[] args)
    {

        Extension ext = new Extension();
        ext.MyEvent += ext_MyEvent;
        ext.Dosomething();
    }

    static void ext_MyEvent(int num)
    {
        Console.WriteLine(num);
    }
}


public class Extension
{
    public delegate void MyEventHandler(int num);
    public event MyEventHandler MyEvent;

    public void Dosomething()
    {
        int no = 0;
        while(true){
            if(MyEvent!=null){
                MyEvent(++no);
            }
        }
    }
}
3
ответ дан Jagdish 31 August 2018 в 23:29
поделиться

Класс повышения должен получить новую копию EventHandler. Одно из возможных решений ниже.

using System;

namespace ConsoleApplication1
{
    class Program
    {
        class HasEvent
        {
            public event EventHandler OnEnvent;
            EventInvoker myInvoker;

            public HasEvent()
            {
                myInvoker = new EventInvoker(this, () => OnEnvent);
            }

            public void MyInvokerRaising() {
                myInvoker.Raise();
            }

        }

        class EventInvoker
        {
            private Func<EventHandler> GetEventHandler;
            private object sender;

            public EventInvoker(object sender, Func<EventHandler> GetEventHandler)
            {
                this.sender = sender;
                this.GetEventHandler = GetEventHandler;
            }

            public void Raise()
            {
                if(null != GetEventHandler())
                {
                    GetEventHandler()(sender, new EventArgs());
                }
            }
        }

        static void Main(string[] args)
        {
            HasEvent h = new HasEvent();
            h.OnEnvent += H_OnEnvent;
            h.MyInvokerRaising();
        }

        private static void H_OnEnvent(object sender, EventArgs e)
        {
            Console.WriteLine("FIRED");
        }
    }
}
0
ответ дан Johannes 31 August 2018 в 23:29
поделиться

Похоже, вы используете шаблон Delegate . В этом случае событие AfterSearch должно быть определено в классе EventRaiser, а класс EventContainer должен потреблять событие:

В EventRaiser.cs

public event EventHandler BeforeSearch;
public event EventHandler AfterSearch;

public void ExecuteSearch(...)
{
    if (this.BeforeSearch != null)
      this.BeforeSearch();

    // Do search

    if (this.AfterSearch != null)
      this.AfterSearch();
}

В EventContainer.cs

public EventContainer(...)
{
    EventRaiser er = new EventRaiser();

    er.AfterSearch += this.OnAfterSearch;
}

public void OnAfterSearch()
{
   // Handle AfterSearch event
}
7
ответ дан Mark Pim 31 August 2018 в 23:29
поделиться

Есть хороший способ сделать это. Каждое событие в C # имеет делегат, который указывает знак методов для этого события. Определите поле в своем внешнем классе с типом делегата события. получите ссылку этого поля в конструкторе внешнего класса и сохраните его. В основном классе вашего мероприятия отправьте ссылку события для делегата внешнего класса. Теперь вы можете легко вызвать делегата во внешнем классе.

public delegate void MyEventHandler(object Sender, EventArgs Args);

public class MyMain
{
     public event MyEventHandler MyEvent;
     ...
     new MyExternal(this.MyEvent);
     ...
}

public MyExternal
{
     private MyEventHandler MyEvent;
     public MyExternal(MyEventHandler MyEvent)
     {
           this.MyEvent = MyEvent;
     }
     ...
     this.MyEvent(..., ...);
     ...
}
4
ответ дан Milad Irannejad 31 August 2018 в 23:29
поделиться

Очень простой пример. Мне нравится делать это с помощью EventHandler.

    class Program
    {
        static void Main(string[] args)
        {
            MyExtension ext = new MyExtension();
            ext.MyEvent += ext_MyEvent;
            ext.Dosomething();
            Console.ReadLine();
        }

        static void ext_MyEvent(object sender, int num)
        {
            Console.WriteLine("Event fired.... "+num);
        }
    }

    public class MyExtension
    {
        public event EventHandler<int> MyEvent;

        public void Dosomething()
        {
            int no = 1;

            if (MyEvent != null)
                MyEvent(this, ++no);
        }
    }
}
0
ответ дан Mist 31 August 2018 в 23:29
поделиться

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

Конечно, это разрушает инкапсуляцию и плохой дизайн.

6
ответ дан Peter Mortensen 31 August 2018 в 23:29
поделиться

Согласитесь с Femaref - и обратите внимание, что это важное различие между делегатами и событиями (см., например, эту запись в блоге для хорошего обсуждения этого и других различий).

В зависимости от того, чего вы хотите достичь, вам может быть лучше с делегатом.

3
ответ дан Tim Barrass 31 August 2018 в 23:29
поделиться

У меня была такая путаница и я честно нашел ответы здесь, чтобы запутать. Хотя пара намекала на решения, которые я позже найду, будет работать.

Мое решение заключалось в том, чтобы попасть в книги и стать более знакомыми с делегатами и обработчиками событий. Хотя я использовал оба в течение многих лет, я никогда не был знаком с ними. http://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices дает лучшее объяснение как делегатов, так и обработчиков событий, которые я когда-либо читал и четко объясняет, что класс может быть издателем событий и использовать другие классы. Эта статья: http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single обсуждает, как однократные события только для одного обработчика поскольку делегаты являются многоадресными по определению. Делегат наследует system.MulticastDelegate большинство, включая делегатов системы, являются многоадресными. Я обнаружил, что многоадресная рассылка означает, что любой обработчик событий с той же подписью получит поднятое событие. Многоадресное поведение вызвало у меня несколько бессонных ночей, когда я шагнул через код и увидел, что мое событие, казалось бы, ошибочно отправлено обработчикам, что я не собирался получать это событие. Обе статьи объясняют это поведение. Вторая статья показывает вам один путь, и первая статья показывает вам другую, сделав делегата и подпись жестко напечатанной. Я лично считаю, что сильная типизация предотвращает глупые ошибки, которые могут быть причиной боли. Поэтому я проголосовал за первую статью, хотя я получил второй код статьи. Мне было просто любопытно. :-)

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

Я начал играть со классом eventRaiser второй статьи. Я сделал простой проект в виде окон. Я добавил в свой проект второй класс статей EventRaiser.cs. В коде основной формы я определил ссылку на этот класс EventRaiser вверху как

private EventRaiser eventRaiser = new EventRaiser();

. Я добавил метод в основной код формы, который я хотел вызвать, когда событие было запущено

protected void MainResponse( object sender, EventArgs eArgs )
{            
    MessageBox.Show("got to MainResponse");
}

, то в конструкторе основной формы я добавил назначение события:

eventRaiser.OnRaiseEvent += new EventHandler(MainResponse);`

Затем я создал класс, который будет создан в моей основной форме под названием «SimpleClass» из-за отсутствия творческая изобретательность на данный момент.

Затем я добавил кнопку, и в событии клика кнопки я создал экземпляр кода SimpleClass, я хотел поднять событие из:

    private void button1_Click( object sender, EventArgs e )
   {            
       SimpleClass sc = new SimpleClass(eventRaiser);
   }

Обратите внимание на экземпляр «eventRaiser», который я передал SimpleClass.cs. Это было определено и инстанцировано ранее в коде основной формы.

В SimpleClass:

using System.Windows.Forms;
using SinglecastEvent; // see SingleCastEvent Project for info or http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single

    namespace GenericTest
    {

        public class SimpleClass
        {

            private EventRaiser eventRaiser = new EventRaiser();

            public SimpleClass( EventRaiser ev )
            {
                eventRaiser = ev;
                simpleMethod();

            }
            private void simpleMethod()
            {

                MessageBox.Show("in FileWatcher.simple() about to raise the event");
                eventRaiser.RaiseEvent();
            }
        }
    }

Единственным пунктом для частного метода, который я назвал SimpleMethod, было проверить, что частная область метод все равно мог поднять событие, а не то, что я его сомневался, но мне нравится быть позитивным.

Я запустил проект, и это привело к тому, что событие было создано с помощью «simpleMethod» из «SimpleClass» до основная форма и переход к ожидаемому правильному методу MainResponse, доказывающему, что один класс действительно может поднять событие, которое потребляется другим классом. Да, событие должно быть поднято из класса, который нуждается в его трансляции для других классов, которые заботятся. Прием классов может быть одним классом или многими многими классами в зависимости от того, насколько сильно вы набрали их, или сделав их одиночными, как в 2-й статье.

Надеюсь, что это поможет, а не грянет вода. Лично у меня есть много делегатов и событий для очистки! Многоадресные демоны бегут!

0
ответ дан user3866622 31 August 2018 в 23:29
поделиться
Другие вопросы по тегам:

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