Jak C# Events działa za kulisami?
Używam C#,. Net 3.5. Rozumiem, jak wykorzystać zdarzenia, jak je zadeklarować w mojej klasie, jak podłączyć je z innego miejsca itp. Zmyślony przykład:
public class MyList
{
private List<string> m_Strings = new List<string>();
public EventHandler<EventArgs> ElementAddedEvent;
public void Add(string value)
{
m_Strings.Add(value);
if (ElementAddedEvent != null)
ElementAddedEvent(value, EventArgs.Empty);
}
}
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
MyList tmp = new MyList();
tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired);
tmp.Add("test");
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
Jednak to, co robię nie Rozumiem, jest wtedy, gdy deklaruje się obsługę zdarzenia
public EventHandler<EventArgs> ElementAddedEvent;
Nigdy nie jest inicjowany - więc co dokładnie jest ElementAddedEvent? Na co to wskazuje? Poniżej nie będzie działać, ponieważ program EventHandler nigdy nie jest inicjowany:
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
EventHandler<EventArgs> somethingHappend;
somethingHappend += new EventHandler<EventArgs>(Fired);
somethingHappend(this, EventArgs.Empty);
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
Zauważam, że istnieje EventHandler.CreateDelegate(...), ale wszystkie sygnatury metod sugerują, że jest to używane tylko do dołączania delegatów do już istniejącego programu EventHandler poprzez typowy ElementAddedEvent + = new EventHandler (MyMethod).
Nie wiem, czy to, co próbuję zrobić, pomoże... ale ostatecznie chciałbym wymyślić abstrakcyjny rodzic DataContext w LINQ, którego dzieci mogą zarejestrować typy tabel, które chcą "obserwować", więc mogę mieć zdarzenia takie jak BeforeUpdate i AfterUpdate, ale specyficzne dla typów. Coś takiego:public class BaseDataContext : DataContext
{
private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>();
public static void Observe(Type type)
{
if (m_ObservedTypes.ContainsKey(type) == false)
{
m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>());
EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler);
}
}
public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events
{
get { return m_ObservedTypes; }
}
}
public class MyClass
{
public MyClass()
{
BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate);
}
public void OnUserUpdated(object sender, EventArgs args)
{
// do something
}
}
Myślenie o tym uświadomiło mi, że nie bardzo rozumiem, co dzieje się pod hod z wydarzeniami - i chciałbym to zrozumieć:)
2 answers
Napisałem to w sporej ilości szczegółów w Artykuł , ale oto podsumowanie, zakładając, że jesteś w miarę zadowolony z delegatów siebie:
- zdarzenie jest tylko metodą "Dodaj" i metodą "Usuń", tak samo jak właściwość jest tak naprawdę tylko metodą" get "I metodą" set". (W rzeczywistości CLI pozwala również na metodę "raise / fire", ale C# nigdy tego nie generuje.) Metadane opisują Zdarzenie za pomocą odniesień do metod.
- Kiedy deklarujesz wydarzenie polowe (podobnie jak ElementAddedEvent) kompilator generuje metody i prywatne pole (tego samego typu co delegat). W obrębie klasy, gdy odnosisz się do ElementAddedEvent odnosisz się do pola. Poza klasą, masz na myśli pole.
- gdy ktoś subskrybuje Zdarzenie (z operatorem+=), które wywołuje metodę add. Gdy wypisują się (za pomocą operatora -=), który wywołuje usunąć
-
W przypadku zdarzeń podobnych do pól istnieje pewna synchronizacja, ale poza tym Dodaj/usuń po prostu wywołaj delegata.połączyć/Usuń , aby zmienić wartość automatycznie wygenerowanego pola. Obie te operacje przypisują pole zapasowe-pamiętaj, że delegaci są niezmienni. Innymi słowy, kod autogeneracyjny jest bardzo podobny do tego:
// Backing field // The underscores just make it simpler to see what's going on here. // In the rest of your source code for this class, if you refer to // ElementAddedEvent, you're really referring to this field. private EventHandler<EventArgs> __ElementAddedEvent; // Actual event public EventHandler<EventArgs> ElementAddedEvent { add { lock(this) { // Equivalent to __ElementAddedEvent += value; __ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value); } } remove { lock(this) { // Equivalent to __ElementAddedEvent -= value; __ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value); } } }
Wartość początkowa wygenerowanego pola w Twoim przypadku to
null
- i zawsze będzie tonull
ponownie, jeśli wszyscy subskrybenci zostaną usunięci, ponieważ jest to zachowanie delegata.Usunąć-
Jeśli chcesz, aby obsługa" no-op " subskrybowała Twoje wydarzenie, aby uniknąć sprawdzenia nieważności, możesz to zrobić:
public EventHandler<EventArgs> ElementAddedEvent = delegate {};
delegate {}
jest tylko anonimową metodą, która nie dba o swoje parametry i nic nie robi.
Jeśli jest coś jeszcze niejasnego, proszę zapytać, a ja postaram się pomóc!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-17 20:41:05
Pod maską, wydarzenia są po prostu delegatami ze specjalnymi konwencjami wywołującymi. (Na przykład nie musisz sprawdzać nieważności przed zgłoszeniem zdarzenia.)
W pseudokodzie, Event.Wywołanie() rozkłada się w następujący sposób:
If Event Has Listeners Wywołaj każdego słuchacza synchronicznie w tym wątku w dowolnej kolejności.
Ponieważ zdarzenia są multicastem, będą miały zero lub więcej słuchaczy, trzymanych w kolekcji. CLR zapętli je, wywołując każdy w dowolnym spokój.
Należy pamiętać, że procedury obsługi zdarzeń są wykonywane w tym samym wątku, w którym zdarzenie jest podnoszone. Częstym błędem umysłowym jest myślenie o nich jako o zrodzeniu nowego wątku. Nie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-17 20:06:48