Jason Massey
Jason Massey

Reputation: 1088

ReactiveExtension that was working on old computer is now failing

I'm using the code from Weak Events in .Net, the easy way to handle monitoring changes to an observable collection. The code has worked without any problems for months. I recently updated to a new computer. After getting everything setup and pulling down the code from my repository I encountered a strange problem. The code no longer works!

Here's the relevant portion of my code, it all takes place in the constructor:

 public class PurchaseOrderReceipt : BaseEntity
{
    /// <summary>
    ///     Initializes a new instance of the <see cref="PurchaseOrderReceipt" /> class.
    /// </summary>
    public PurchaseOrderReceipt()
    {
        this.ReceiptItems = new ObservableCollection<PurchaseOrderItemReceipt>();
        this.DateReceived = DateTime.Now;

        this.ReceiptItems.ObserveCollectionChanged()
            .SubscribeWeakly(this, (target, eventArgs) => target.ReceiptItemsChanged());
    }

The exception is thrown on the SubscribeWeakly line with the following error message: ArgumentException: onNext must refer to a static method, or else the subscription will still hold a strong reference to target

I can recreate the problem in LinqPad just by creating an instance of the PurchaseOrderReceipt.

Odder still if I write a simple class in LinqPad that mirrors the setup in the PurchaseOrderReceipt class than it works.

LinqPad code:

void Main()
{
    var x = new Test(); 
    x.ReceiptItems.Add(new PurchaseOrderItemReceipt());

}

public class Test:BaseEntity
{
    public ObservableCollection<PurchaseOrderItemReceipt> ReceiptItems {get; set;}

    public Test()
    {
        this.ReceiptItems = new ObservableCollection<PurchaseOrderItemReceipt>();
        this.ReceiptItems.ObserveCollectionChanged().SubscribeWeakly(this,(target, eventargs) => target.TestChanged());
    }

    private void TestChanged()
    {
        "Changed!".Dump();
    }
}

Changed! is printed out in the results window.

Here's the CustomReactiveExtension class from the link at the top.

public static class CustomReactiveExtension
    {
        public static IObservable<EventPattern<NotifyCollectionChangedEventArgs>> ObserveCollectionChanged(this INotifyCollectionChanged collection)
        {
            return Observable.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
                handler => (sender, e) => handler(sender, e),
                handler => collection.CollectionChanged += handler,
                handler => collection.CollectionChanged -= handler);
        }

        public static IDisposable SubscribeWeakly<T, TTarget>(this IObservable<T> observable, TTarget target, Action<TTarget, T> onNext) where TTarget : class
        {
            var reference = new WeakReference(target);

            if (onNext.Target != null)
            {
                throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
            }

            IDisposable subscription = null;
            subscription = observable.Subscribe(item =>
            {
                var currentTarget = reference.Target as TTarget;
                if (currentTarget != null)
                {
                    onNext(currentTarget, item);
                }
                else
                {
                    subscription.Dispose();
                }
            });

            return subscription;
        }
    }

Any ideas?

Upvotes: 2

Views: 85

Answers (1)

Lukazoid
Lukazoid

Reputation: 19416

I am not 100% certain but my guess is that either different versions of the compiler or different compilation options are resulting in your lambda being compiled to an instance method rather than a static method.

The easiest solution to this would be to explicitly implement a static method to be used as your onNext callback, i.e:

private static void OnReceiptItemsChanged(PurchaseOrderReceipt target, 
    EventPattern<NotifyCollectionChangedEventArgs> eventPattern)
{
    // TODO Do something here
}

And then use SubscribeWeakly like so:

this.ReceiptItems.ObserveCollectionChanged().SubscribeWeakly(this, OnReceiptItemsChanged);

Now regardless of which compiler you use or which compilation options the callback is always a static method.

Upvotes: 1

Related Questions