Click here to Skip to main content
15,879,535 members
Please Sign up or sign in to vote.
4.00/5 (2 votes)
See more:
hi, i just want to ask if my thinking is right, basically for example i have class named 'Person' and then in the class i have events,
- OnCashChanged(Person x);
- OnCashAdded(Person x);
- OnCashRemoved(Person x); // meaning spent money :D

and then i would like to access these events and here is my questions, for example if you have multiple objects of persons, and you need to listen on whatever one of them changes their cash or cash added or removed, so as you can see from my code, i have 2 persons and i declared the all events for the two of them, but is this actually correct?! because what if you have 50 persons and you need to listen for events on all of them, do you need to subscribe to the event with every one of them like i did or these is a some other way to do this, like i said im learning about events and i dont know what is correct and what not,
link to pastebin code
thanks in advance.
Posted
Comments
Sergey Alexandrovich Kryukov 16-Jan-15 17:39pm    
What these event? Those are not events. I think I understand what you want but you don't quite understand it. This is not how events work.
Also, there are no custom and non-custom events. All .NET BCL and FCL events are written on the same platform you can use.
—SA
SrgjanX 16-Jan-15 20:20pm    
by custom event i meant event that you write, event that is not written by .net.
BillWoodruff 16-Jan-15 22:37pm    
Have you ever looked at implementing INotifyPropertyChanged ?

Are you familiar with Action and Func delegates ?
Sergey Alexandrovich Kryukov 17-Jan-15 1:54am    
There is nothing as bad in .NET libraries as its System.ComponentModel. It solved its problems, but in very poorly supportable ways, in particular, in approaches using hard-coded strings representing method names. Anyway, the OP's problems is solved in much better way using events (and not just "regular" delegates).
—SA
BillWoodruff 17-Jan-15 5:33am    
I personally don't like the implementation of INotifyPropertyChanged, and INotifyPropertyChanging in System.ComponentModel. The example I give in my solution does not use INotifyPropertyChanged/Changing.

I simply asked the OP what they are familiar with in order to try and get a "baseline" on how it might be appropriate to respond.

This comment of yours is off-topic, and is an inappropriate venting.

Further, as of .NET 4.5 "hard-coded strings" are no longer necessary in implementing INotifyPropertyChanged.

Try to get a grip.

The way you have it coded, yes you would need to register to each person's event. What you could do is create a collection class of type Person, that way it's all centralized on one class. Like so:

C#
class PersonCollection:List<person>{
     public new void Add(Person p){
          if(p!=null){
               // Register here to events
               p.OnCashChanged += OnCashChangedHandler;
               base.Add(p);
          }
     }

     public new bool Remove(Person p){
          bool removed=false;
          if(p!=null){
               // Remove events
               p.OnCashChanged -= OnCashChangedHandler;
               removed = base.Remove(p);
          }
          return removed;
     }
     
     protected void OnCashChangedHandler(Person p){
          // Do something here with someone
     }
}
</person>


This way, all your code is centralized to this class then you can just add or remove people from the collection
 
Share this answer
 
Comments
SrgjanX 16-Jan-15 17:50pm    
thank you man :)
BillWoodruff 17-Jan-15 6:07am    
+4 This is a good start, if you would show how the OnCashChanged Event is implemented, you'd get my #5.
Here's an example of using the Action delegate Type for event notification:
C#
using System;
using System.Collections.Generic;

namespace PersonCollectionDemo
{
    public enum TransactionType
    {
        ClearAndReset,
        Deposit,
        Withdraw
    }

    // Person
    public class Person
    {
        // expose only ability to 'read
        public string Name { private set; get; }
        public decimal Cash { private set; get; }

        public Person
        (
            string name, decimal initialCash
        )
        {
            Name = name;
            Cash = initialCash;
        }

        public void SetCash(decimal amount)
        {
            // consider throwing an error if amount is negative ?
            if (amount == Cash || amount < 0) return;

            Cash = amount;

            Persons.CashChangedMethod(this, TransactionType.ClearAndReset, amount, Cash);
        }

        public void AddCash(decimal amount)
        {
            // amount must be positive
            // consider throwing an error if negative ?
            if (amount <= 0) return;

            Cash += amount;

            Persons.CashChangedMethod(this, TransactionType.Deposit, amount, Cash);
        }

        public void WithDrawCash(decimal amount)
        {
            // amount must be negative
            // consider throwing an error if positive ?
            if (amount >= 0) return;

            // detect and handle overdraft
            if (Cash + amount < 0)
            {
                Persons.OverDraftAlertMethod(this, TransactionType.Withdraw, amount, Cash);
                return;
            }

            Cash += amount;

            Persons.CashChangedMethod(this, TransactionType.Withdraw, amount, Cash);        
        }
    }

    // Collection of Person
    public class Persons : List<Person>
    {
        public static Action<Person,> CashChangedMethod { private set; get; }

        public static Action<Person,> OverDraftAlertMethod { private set; get; }

        public Persons
        (
            Action<Person,> cashChangedMethod,
            Action<Person,> overDraftAlertMethod
        )
        {
            if(CashChangedMethod == null) CashChangedMethod = cashChangedMethod;
            if(OverDraftAlertMethod == null) OverDraftAlertMethod = overDraftAlertMethod;
        }

        public void Add(string name, decimal initialcash)
        {
            Person newPerson = new Person(name, initialcash);
            this.Add(newPerson);
        }
    }
}
Sample test:
C#
using PersonCollectionDemo;

private void TestPersonDemo
{
    Persons ThePersons = new Persons(CashChangedMethod, OverDraftAlertMethod);
    
    ThePersons.Add("Person1", 100.23M);
    ThePersons[0].SetCash(245.35M);
    ThePersons.Add("Person2", 499.00M);
    
    ThePersons[0].AddCash(23.35M);
    ThePersons[1].WithDrawCash(-500.00M);
}

// EventHandlers invoked from Person class
private void OverDraftAlertMethod(Person person, TransactionType transactionType, decimal amount, decimal balance)
{
    Console.WriteLine("Account Action: Withdrawal Rejected: Account Name: {0} | Transaction Type: {1} | Withdraw Amount: {2} | Balance: {3}", person.Name, transactionType, amount, balance);
}

private void CashChangedMethod(Person person, TransactionType transactionType, decimal amount, decimal balance)
{
    Console.WriteLine("Account Action: Account Name: {0} | Transaction Type: {1} | Amount: {2} | Balance: {3}", person.Name, transactionType, amount, balance);
}
Test output to Console:
Account Action: Account Name: Person1 | Transaction Type: ClearAndReset | Amount: 245.35 | Balance: 245.35

Account Action: Account Name: Person1 | Transaction Type: Deposit | Amount: 23.35 | Balance: 268.70

Account Action: Withdrawal Rejected: Account Name: Person2 | Transaction Type: Withdraw | Withdraw Amount: -500.00 | Balance: 499.00
Notes:

1. some care is taken in this example to make the instances of the Action Delegates used (they are declared as 'static Properties) only accessible (can be set) the first time the constructor of the Persons class is called.

The reason for this is to prevent any code outside the Class from adding additional EventHandlers to those Action Delegates.
 
Share this answer
 
v6
Comments
Sergey Alexandrovich Kryukov 17-Jan-15 1:49am    
Bill, I am sorry, but this is awfully misleading. You are talking "events" but showing only the delegates.
Come one, do you really understand events at all? Before, I thought you basically do.
Anyway, if you use the word "event" in figurative or "semantic", not "technical" meaning of this word, at least you should write about it, to avoid confusion.

Besides, your code won't compile, due to Action<person,>. I decisively cannot understand that you allow yourself to down-vote answers for a little typo (which is itself can be quite fair), while you write long code fragments without passing it through your compiler? A while ago, you claimed pretty high ethical standard in your answers; for example, you claimed that you never answer on techniques you did not try (which I think would be acceptable), and, at the same time, allow yourself to put such code? You disappoint me a lot. Sometime I even get to thinking that you don't feel well at times. Wish you the very best.

—SA
BillWoodruff 17-Jan-15 3:17am    
Every piece of code I post is tested; it compiles, it runs. Where do you think the sample Console output I show came from ?

Do go ahead and engage in ad hominem raving and "revenge down-voting" all you like.
Sergey Alexandrovich Kryukov 17-Jan-15 3:22am    
You said this word which crossed the line: "revenge". I never do it. At this point, any discussion is impossible.
—SA
Sergey Alexandrovich Kryukov 17-Jan-15 3:27am    
You could make a mistake during posting. I've shown which like doesn't compile, just checked it up.
—SA
To start with, those are not events. And this is not how events and work and not how event data is passed. Please see my comments to the question.

I would quickly send you to the reading of standard documentation on events. You need to learn this topic anyway. But you also need to do something which is not properly explained in MSDN, because this "something" is not formally required, be is a part of some conventions for the cultured code. Perhaps it would be better to show you some code sample.

First of all, event data should be passed using the class derived from System.EventArgs. This is not required formally but is required as the good style convention; in return, .NET give you valuable support by its .NET BCL types. Do this:
C#
class Person { /* ... */ }

public class MyEventArgs : System.EventArgs {
     internal MyEventArgs(Person person) { this.Person = person; } // not public!
     public Person Person { get; private set; } // private is important
}

Now, let's add the class declaring an event instance. It can be your Person, but it's better to show more general case when this is a different class. First of all, if you used a "regular" delegate instance, not event instance, your delegate type matching the event delegate type could be based on the System.Action declaration. It could be
C#
System.Action<object, MyEventArgs> myDelegateInstance;
But! This convention is not applied to "regular delegate instances", but only to delegate instances. But for delegate instances, .NET BCL gives you more , different support, so you don't even need this Action declaration. Instead, you should use
C#
public class EventImplementor {
    public event System.EventHandler<MyEventArgs> PersonChanged;
}

Now, the problem is: where to invoke the event? The trick is: it is only allowed to be done in the same class; any other code won't compile. This is a very important .NET fool-proof feature.
So, you would need to add:
C#
public class EventImplementor {
    
    public event System.EventHandler<MyEventArgs> PersonChanged;
    
    // private, by default, but could be, say, protected, to grant access
    // to derived classes:
    void InvokePersonChanged() {
        if (PersonChanged != null) // important!
            PersonChanged.Invoke(this, new MyEventArgs(this.person));
    }

    // now you can called it whenever something changes the person;
    // for example, it could be done by public/internal setter (!)
    // of the property:
    Person Person {
        get { return this.person; }
        set {
            if (value == this.person) return;
            this.person = value;
            InvokePersonChanged();
        }
    }
    
    Person person;

}

Not it's time to address your OnSomethingChanged. The convention is: if you have the property Person, the method named like OnPersonChanged 1) should be virtual, 2) only protected, 3) should invoke PersonChanged:
C#
public class EventImplementor {
    
    public event System.EventHandler<MyEventArgs> PersonChanged;
    
    // private, by default, but could be, say, protected, to grant access
    // to derived classes:
    protected virtual void OnPersonChanged() {
        if (PersonChanged != null) // important!
            PersonChanged.Invoke(this, new MyEventArgs(this.person));
    }

    // ...

}
Why? By a very serious reason. It give some extra flexibility for handling the change of the person. First way won't use the event, but will not allow for event multi-casting. It's faster:
C#
class MyDerivedClass : EventImplementor {
    protected override void OnPersonChanged() {
        // do something when person changed
        // note that you can ignore event, then it won't multi-cast
    } //protected override void OnPersonChanged()
}
Another way is handling the event; as I made event public (could be internal), it can be done in any place of code, even in a different assembly:
C#
EventImplementor implementor = new EventImplementor();
implementor.PersonChanged += (sender, eventArgs) => {
    // this is also based on convention:
    // the declaring class member send "this" as sender:
    EventImplementor implementorSender = (EventImplementor)sender;
    // but for static event instances, it should be null
    Person person = eventArgs.Person; // this was the whole purpose
    // of the event argument class
};
Are you getting the idea?

[EDIT #1]

I apparently pretty much ignore the fact that you are changing some hash, not person. This if, first of all, because I wanted to make my tutorial as simple as possible. The difference is very little. Instead of having MyEventArgs.Person, you could have two: one property for person, another for hash object. Also, you should rather add more events, such as PersonAdding, PersonAdded, PersonRemoving, PersonRemoved. If this is not clear, please ask me the question.

[EDIT #2]

Some weird members of this site don't accept anonymous methods. I think this is a big mistake, but for completeness:
C#
static void PersonHandler(object sender, MyEventArgs eventArg) { /* ... */ }

//...

implementor.PersonChanged += PersonHandler; // no need in "new" 

For C#.v2, type inference cannot be used even for the anonymous. You would need:
C#
implementor.PersonChanged += delegate(object sender, MyEventArgs) => {
    // ...
}
Several handlers can be added to the invocation list of each event handler; they all will be invoked through one invocation call.

—SA
 
Share this answer
 
v5
Comments
SrgjanX 16-Jan-15 20:25pm    
man thanks for this code but i definitely need a time to review this, its too much for me, i just started learning custom written events, im going to rate this after i review it and test it.
Sergey Alexandrovich Kryukov 16-Jan-15 20:32pm    
Of course. Just don't think about time; it just doesn't matter. But you can hardly find so compressed overview of all relevant aspect in one single place. And I'll answer your follow-up questions if you want. But would I advise to write it all in some separate project and see how it works under the debugger. You will instantly understand nearly everything.
Good luck.
—SA
BillWoodruff 16-Jan-15 22:53pm    
+3 The content is a very obscure and non-standard way to implement the equivalent of an enhanced INotifyPropertyChanged facility.
Sergey Alexandrovich Kryukov 17-Jan-15 1:40am    
I afraid am coming to the conclusion that your understanding of event is pure. I always suspected it looking at numbers of your code samples. But I want to talk to you about "pedagogical" which you tend to emphasize again and again (obscure).

You cannot deny that I listened you with attention and hope you can listen yourself. There is the thing: you need to see the difference between some very different issues: 1) historically earliest or later; 2) well-established in public opinion; 2) standard or non-standard; 3) easy or difficult. You should finally understand that "well-established" does not mean easy or difficult. It also does not mean "standard or non-standard".

Do you understand that geocentric picture of the world was not correct or wrong, it was, first of all, awfully difficult in calculations? It was very well established for a long time, nevertheless. And your understanding of what I just describe as "non-standard" is nothing but your poor knowledge. This is perfectly fine and can be fixed, the problem is not that but your approach. I really appreciate your criticism, as criticism in general, but would like you to address to me only with some essential and rationally explained facts. It's possible that I am wrong right now, but your will do me a great favor if you explain it me rationally. Also, aren't we scientific people or not? Then you should understand that I cannot expect any referenced to any authorities. I hope you understand it. I Otherwise our arguments, honestly, would waste to much time.

Thank you.
—SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900