//Rozdział 21.
//Obserwator

public class FallsIllEventArgs : EventArgs
{
   public string Address;
}

public class Person
{
   public void CatchACold()
    {
        FallsIll?.Invoke(this,
            new FallsIllEventArgs { Address = "Mazowiecka 123" });
    }
   public event EventHandler<FallsIllEventArgs> FallsIll;
}

static void Main()
{
    var person = new Person();
    person.FallsIll += CallDoctor;
    person.CatchACold();
}
private static void CallDoctor(object sender, FallsIllEventArgs
eventArgs)
{
    Console.WriteLine($"Wezwano doktora pod adres {eventArgs.Address}");
}

//Słabe zdarzenie
public class Button
{
   public event EventHandler Clicked;
   public void Fire()
    {
        Clicked?.Invoke(this, EventArgs.Empty);
    }
}

public class Window
{
   public Window(Button button)
    {
        button.Clicked += ButtonOnClicked;
    }
    private void ButtonOnClicked(object sender,
    EventArgs eventArgs)
    {
        WriteLine("Klinięto przycisk (handler w obiekcie Window)");
    }
    ~Window()
    {
        WriteLine("Finalizacja obiektu Window");
    }
}
var btn = new Button();
var window = new Window(btn);
var windowRef = new WeakReference(window);
btn.Fire();

window = null;

FireGC();
WriteLine($"Czy okno jest nadal żywe po wywołaniu GC? {windowRef.IsAlive}");
// True
public class Window2
{
   public Window2(Button button)
    {
        WeakEventManager<Button, EventArgs>
            .AddHandler(button, "Kliknięto", ButtonOnClicked);
    }
    //pozostała część klasy taka sama jak poprzednio
}
 Obserwatory właściwości
 class Person
{
   public int Age { get; set; }
}

class Person
{
    private int age;
   public int Age
    {
        get => age;
        set
        {
            //todo: tutaj powinno się coś znaleźć
            age = value;
        }
    }
}

public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged
    ([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this,
        new PropertyChangedEventArgs(propertyName));
}
public int Age
{
    get => age;
    set
    {
        if (value == age) return;
        age = value;
        OnPropertyChanged();
    }
}

//Problemy z zależnościami
public bool CanVote => Age >= 16;
public int Age
{
    get => age;
    set
    {
        if (value == age) return;
        var oldCanVote = CanVote;
        age = value;
        OnPropertyChanged();
        if (oldCanVote != CanVote)
            OnPropertyChanged(nameof(CanVote));
    }
}
class PropertyNotificationSupport : INotifyPropertyChanged
{
    private readonly Dictionary<string, HashSet<String>> affectedBy
        = new Dictionary<string, HashSet<string>>();
   public event PropertyChangedEventHandler PropertyChanged;
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged
    ([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this,
        new PropertyChangedEventArgs(propertyName));
    foreach (var affector in affectedBy.Keys)
        if (affectedBy[affector].Contains(propertyName))
            OnPropertyChanged(propertyName);
}
protected Func<T> property<T>(string name, Expression<Func<T>>
expr)
{
    Console.WriteLine($"Tworzenie obliczonej właściwości dla wyrażenia
    {expr}");
    var visitor = new MemberAccessVisitor(GetType());
    visitor.Visit(expr);
    if (visitor.PropertyNames.Any())
    {
        if (!affectedBy.ContainsKey(name))
            affectedBy.Add(name, new HashSet<string>());
        foreach (var propName in visitor.PropertyNames)
            if (propName != name)
                affectedBy[name].Add(propName);
        }
    return expr.Compile();
}
private class MemberAccessVisitor : ExpressionVisitor
{
    private readonly Type declaringType;
   public IList<string> PropertyNames = new List<string>();
   public MemberAccessVisitor(Type declaringType)
    {
        this.declaringType = declaringType;
    }
   public override Expression Visit(Expression expr)
    {
        if (expr != null && expr.NodeType == ExpressionType.
        MemberAccess)
        {
            var memberExpr = (MemberExpression)expr;
            if (memberExpr.Member.DeclaringType == declaringType)
            {
                PropertyNames.Add(memberExpr.Member.Name);
            }
        }
        return base.Visit(expr);
    }
}

class Person : PropertyNotificationSupport
{
    private int age;
   public int Age { /* tak, jak wcześniej */ }
    private readonly Func<bool> canVote;
   public bool CanVote => canVote();
   public Person()
    {
        canVote = property(nameof(CanVote), () => Age >= 16);
    }
}
// Strumienie zdarzeń

private class Subscription : IDisposable
{
    private Person person;
   public IObserver<Event> Observer;
   public Subscription(Person person, IObserver<Event> observer)
    {
        this.person = person;
        Observer = observer;
    }

   public void Dispose()
    {
        person.subscriptions.Remove(this);
    }
}
public class Event
{
    //tutaj może być cokolwiek
}
public class FallsIllEvent : Event
{
   public string Address;
}
public class Person : IObservable<Event>
{
    private readonly HashSet<Subscription> subscriptions
        = new HashSet<Subscription>();
   public IDisposable Subscribe(IObserver<Event> observer)
    {
        var subscription = new Subscription(this, observer);
        subscriptions.Add(subscription);
        return subscription;
    }

   public void CatchACold()
    {
        foreach (var sub in subscriptions)
            sub.Observer.OnNext(new FallsIllEvent {Address = "Mazowiecka 123"});
    }

    private class Subscription : IDisposable { ... }
}
public class Demo : IObserver<Event>
{
    static void Main(string[] args)
    {
        new Demo();
    }

   public Demo()
    {
        var person = new Person();
        var sub = person.Subscribe(this);
    }

   public void OnNext(Event value)
    {
        if (value is FallsIllEvent args)
            WriteLine($"Wezwano doktora pod adres {args.Address}");
    }

   public void OnError(Exception error){}
   public void OnCompleted(){}
}
 person
    .OfType<FallsIllEvent>()
    .Subscribe(args =>
        WriteLine($"Wezwano doktora pod adres {args.Address}"));
Kolekcje obserwowalne
 Subskrypcje deklaratywne
public class FootballPlayer
{
    [Publishes("score")]
   public event EventHandler PlayerScored;
   public string Name { get; set; }
   public void Score()
    {
        PlayerScored?.Invoke(this, new EventArgs());
    }
}
public class FootballCoach
{
    [SubscribesTo("score")]
   public void PlayerScored(object sender, EventArgs args)
    {
        var p = sender as FootballPlayer;
        Console.WriteLine("Dobra robota, {0}!", p.Name);
    }
}
public class BrokerExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.Container.RegisterInstance(Broker,
            new ExternallyControlledLifetimeManager());
        Context.Strategies.AddNew<BrokerReflectionStrategy>(
            UnityBuildStage.PreCreation);
        Context.Strategies.Add(new BrokerWireupStrategy(Broker),
                               UnityBuildStage.Initialization);
    }
   public EventBroker Broker { get; } = new EventBroker();
}
 
