[EN] „CLR via C#, 3rd edition” – WeakDelegate logical bug

Opublikowany 26 lutego 2011 w C#

While reading the excellent book of Jeffrey Richter"CLR via C#, 3rd edition" I’ve found quite a serious problem in one of the examples. Class WeakDelegate<TDelegate> (on page 562) has a logical bug and it simply will not work as expected. I’ve searched the errata and I’m surprised that nobody has yet noticed that.

The problem that Jeffry wanted to solve is as follows: the reference from the event producer to the event consumer should be weak, so in the case when the event consumer object has no other reason to live, it can be garbage collected. Jeffrey was trying to find a general solution for that and developed the abstract class WeakDelegate<TDelegate> that is holding a weak reference to the original delegate (I’ve removed some unnecessary code to make it clear):

	public abstract class WeakDelegate<TDelegate>
		where TDelegate : class /* MulticastDelegate */ {
		private WeakReference<TDelegate> m_weakDelegate;

		public WeakDelegate( TDelegate @delegate ) {
			var md = (MulticastDelegate) (Object) @delegate;
			if( md.Target == null )
				throw new ArgumentException(
					"Can't do a WeakDelegate to a static method." );

			// Save a WeakReference to the delegate
			m_weakDelegate = new WeakReference<TDelegate>( @delegate );
		}

		protected TDelegate GetRealDelegate() {
			// If the real delegate hasn't been GC'd yet, just return it
			TDelegate realDelegate = m_weakDelegate.Target;
			if( realDelegate != null ) return realDelegate;

			// The real delegate was GC'd,
			// we don't need our WeakReference to it anymore (it can be GC'd)
			m_weakDelegate.Dispose();
			return null;   // The real delegate was GC'd and can't be called
		}

		// All derived classes must return a delegate
		// to a private method matching the TDelegate type
		public abstract TDelegate GetDelegate();

		// Implicit conversion operator to convert
		// a WeakDelegate object to an actual delegate
		public static implicit operator TDelegate(
			WeakDelegate<TDelegate> @delegate ) {
			return @delegate.GetDelegate();
		}
	}

	// This class provides support
	// for the generic EventHandler<TEventArgs> delegate
	public sealed class WeakEventHandler<TEventArgs>
		: WeakDelegate<EventHandler<TEventArgs>>
		where TEventArgs : EventArgs {
		public WeakEventHandler( EventHandler<TEventArgs> @delegate )
			: base( @delegate ) { }

		public override EventHandler<TEventArgs> GetDelegate() {
			return Callback; }

		private void Callback( Object sender, TEventArgs e ) {
			// If the target hasn't been GC'd invoke it
			var eh = base.GetRealDelegate();
			if( eh != null ) eh( sender, e );
		}
	}

So our object diagram looks like below – can you spot the problem?

1. Object diagram.

Object diagram


Suppose that our consumer is still necessary and somehow we are holding a reference to it in our application. What will happen during the first garbage collection? The answer is – the original delegate will be garbage collected because nothing is holding a reference to it (except the WeakDelegate but that reference is weak) and our consumer will stop receiving the events from the producer.

2. Object diagram after first garbage collection


The example from book (page 561) is working only because it is a very special case that will not work in reality – during the first garbage collection both the original delegate and the consumer (DoNotLiveJustForTheEvent object) are collected.

To solve our problem we need something like this:

3. Object diagram with required references


The original delegate is necessary only to extract the target and method from it and use that knowledge to build weak event handler. My solution is explained in my other post (it’s written in Polish but code examples and the diagrams are quite easy to understand). The source code with examples is here. It’s not a general solution – I’ve developed WeakEventHandler<TEventHandler> that is working only with non-generic EventHandler, generic EventHandler<TEventArgs> and custom delegates with signature identical to previous two, but the implementation is quite fast (4-6 times slower than direct delegate invocation). The general solution (that will handle all types of the delegates) is also possible – I will try to show it sometime in the future – I’m thinking about building handlers in runtime using Expressions and using dynamic keyword to invoke open event handlers (but it can be slow).

Podziel się na:
  • RSS
  • Twitter
  • Facebook
  • dotnetomaniak.pl
  • develway.pl
  • Blip
  • Grono.net
  • Spinacz
  • LinkedIn
  • Technorati
  • del.icio.us
  • PDF
  • Drukuj
  • email
komentarze: 0 » tagi: ,

Recenzja książki „CLR via C#, 3rd edition”

Opublikowany 19 lutego 2011 w C#, Recenzje książek

Kilka lat temu Krzysztof Koźmic polecił mi do przeczytania drugie wydanie książki CLR via C# napisanej przez Jeffrey’a Richtera. Pamiętam, że zrobiłem to wtedy z wielką przyjemnością. Książka dotyczyła CLR w wersji 2.0, w międzyczasie pojawił się CLR w wersji 4.0, więc z niecierpliwością czekałem na kolejną jej edycję, która ukazała się nareszcie na początku 2010 roku.

Do tej pory przeczytałem ją już dwa razy od deski do deski i dodatkowo dosyć często wracam do niektórych rozdziałów odkrywając coraz to nowe szczegóły i ciekawostki. Mogę zdecydowanie stwierdzić, że jest to jedna z najlepszych spośród przeczytanych przeze mnie książek dotyczących .NET. Uważam ją za obowiązkową lekturę dla każdego, kto chce być poważnym i świadomym programistą C#, a nie jakimś tam zwykłym kodoklepaczem znającym jedynie składnię języka. Dzięki niej można dowiedzieć się, co tak naprawdę dzieje się pod spodem, jak są w praktyce realizowane przez środowisko .NET pewne koncepcje oraz dlaczego są one realizowane tak, a nie inaczej.

Autor w prosty i bardzo przystępny sposób za pomocą wielu praktycznych przykładów potrafi wyjaśnić nawet skomplikowane idee i problemy. Powszechnie książkę tę uważa się za przeznaczoną dla zaawansowanych programistów, ale ja osobiście nie podzielam tych opinii. Owszem – jeżeli ktoś już posiada mocne podstawy, to jest w stanie wydobyć z niej dużo więcej niż początkujący programista, jednakże każdy może w niej znaleźć coś odpowiedniego dla siebie i swojego poziomu wiedzy. Nie jest to książka do jednorazowego, szybkiego przeczytania – raczej powinna ona być cały czas do naszej dyspozycji gdzieś tam na półce.

Zdecydowanie polecam dla każdego programisty C#.

Podziel się na:
  • RSS
  • Twitter
  • Facebook
  • dotnetomaniak.pl
  • develway.pl
  • Blip
  • Grono.net
  • Spinacz
  • LinkedIn
  • Technorati
  • del.icio.us
  • PDF
  • Drukuj
  • email
komentarze: 0 » tagi: , ,