How attach an event to a method in a common class?
Say i have the following two classes:
public class User
{
public int ID { get; }
public string Name { get; set; }
public void ChangeName(string newName)
{
Name = newName;
}
}
public class Mail
{
public void SendUserInfoChangeEmail()
{
throw new NotImplementedException();
}
}
and what i want to do is: when the someone edit user object name using the method ChangeName
the SendUserInfoChangeEmail
get called automatically.
And i know that i can use events to handle this开发者_运维百科 issue, but what i want is to make a 3rd static class to build in it this events, and will say in the class: attach ChangeName
to SendUserInfoChangeEmail
How can i do this?
NOTE:
I don't want to put any event handling or delegates in both user and email classes i want everything to manage through this third new static class.You can create an event on the user class and subscribe to it in a 3rd class.
You may want to look at the INotifyPropertyChanged interface to see how things like this are done by others.
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(VS.96).aspx
I think what you are talking about is sometimes called an event proxy class. It is a class that serves as an intermediary when you don't want to have a hard dependency between two classes.
In general, I try to avoid using it whenever I can get a reference to the firing object, but there are some legitimate scenarios where the consumer of an event may not be aware of all of the instances that may fire it. For example, where I've used this before is to provide a global data refresh event when there are multiple views of the data and the data is not in a single domain object that can be directly referenced.
public class User
{
public int ID { get; }
public string Name { get; set; }
public void ChangeName(string newName)
{
Name = newName;
UserEventProxy.FireUserNameChanged(this);
}
}
public class UserEventArgs : EventArgs
{
public User User{get; set;}
}
/// <summary>
///
/// </summary>
public static class UserEventProxy
{
/// <summary>
/// Indicates that the associated user's name has changed.
/// </summary>
public static event EventHandler<UserEventArgs> UserNameChanged;
/// <summary>
/// Fires the UserNameChanged event.
/// </summary>
/// <param name="user">The user reporting the name change.</param>
public static void FireUserNameChanged(User user)
{
EventHandler<UserEventArgs> handler = UserNameChanged;
if (handler != null)
{
UserEventArgs args = new UserEventArgs()
{
User = user
};
//Fire the event.
UserNameChanged(user, args);
}
}
}
public class Mail
{
public Mail()
{
UserEventProxy.UserNameChanged += new EventHandler<UserEventArgs>(UserEventProxy_UserNameChanged);
}
private void UserEventProxy_UserNameChanged(object sender, UserEventArgs e)
{
User user = e.User;
//
//Presumably do something with the User instance or pass it to
//the SendUserInfoChangedEmail method. to do something there.
//
SendUserInfoChangeEmail();
}
public void SendUserInfoChangeEmail()
{
throw new NotImplementedException();
}
}
The pattern that you want seems to be the Mediator. This simple example gives you the idea:
public class User {
public event EventHandler NameChanged = delegate { };
public int ID { get; }
public string Name { get; private set; }
public void ChangeName(string newName) {
if (newName != Name) {
NameChanged(this, EventArgs.Empty);
}
Name = newName;
}
}
public class UserMediator {
User _user;
EventHandler _eh;
public UserMediator(User user, Action onNameChanged) {
_user = user;
_eh = (src, args) => onNameChanged();
_user.NameChanged += _eh;
}
public void Detach() {
_user.NameChanged -= _eh;
}
}
Somewhere else:
var user = new User();
var mail = new Mail();
new UserMediator(user, mail.SendUserInfoChangeEmail);
Your user class needs to have an event that is fired with the username changes. Whichever object keeps track of the users will need to have a method that is subscribed to username change event and can create a new Mail object and send the necessary email.
To create the event in the user class, do this:
public class User {
public event EventHandler UserNameChanged;
private string m_Name;
public string Name {
get { return m_Name; }
set {
if(m_Name != value) {
m_Name = value;
// assuming single-threaded application
if(UserNameChanged != null)
UserNameChanged(this, EventArgs.Empty);
}
}
}
// everything else is the same...
}
In the managing code, you will have a method that handles the event:
private void Handle_UserNameChanged(object sender, EventArgs e) {
User user = (User)sender;
// create the mail object and send it
}
I don't think your Mail class as it stands now will work. If Mail is meant to represent a mail message, then it should provide all the methods and properties necessary to set up a mail message and to send it. But if all you need to do is use the .NET framework's mail system to send the email, then Mail could be a static class with methods that send various types of predefined email (not the best design, but workable as a start), including a method SendUserInfoChangeEmail(). That can be called by your event handler.
In terms of design patterns, take a look at the Observer Pattern
- Why would you want a
ChangeUser
method in addition to theName
property which serves exactly the same purpose just much cleaner? - What does
SendUserInfoChangeEmail
mean? What is its purpose? Why is it in the Mail class? Wouldn't it be better to have that in theUser
class or something similar? - Why a static third class?
Anyways, I would change the User
class to something like this:
public class User : INotifyPropertyChanged
{
public int ID { get; }
private string name;
public string Name
{
get { return name; }
set
{
if(value != name)
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Then in wherever you feel like it, your static class or whatever, subscribe to that event and send the email when the name has changed.
精彩评论