0

So I am basically questioning everything I've learned so far about garbage collecting in C#.

I have a class called User, User has 2 properties a (system.Timers)Timer and a name property everytime a User is created I bind an event, the event consists of every 0.5 seconds notify log that and event has occurred.

The problem is every time I bind an event to a User it appears that the garbage collector just doesn't seem to dispose of the class even if I remove it from the list.

User class

public class User
{
    /// <summary>
    /// A specific name that I give to created users so I know which one was created/destroyed
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// A timer that fires an event every so often
    /// </summary>
    public Timer _timer;


    public User(string name)
    {

        Name = name;

        // Initializing the Timer
        // Here is where it get interesting when I initialize the Timer class this user class just doesn't get disposed
        // even if I set "Enabled" to false
        _timer = new Timer()
        {
            AutoReset = true,
            Enabled = true,
            Interval = TimeSpan.FromSeconds(0.5).TotalMilliseconds,
        };


        // Notifying console that user has been created
        IoC.Log().WriteLine($"\n{Name} was created\n");


        // Bind the event
        // I've attempted to bind the event as lambada function and as a 
        // "normal" function both seem to be causing the "error"
        //_timer.Elapsed += (sender, e) => IoC.Log().WriteLine($"{Name} elapsed");
    }

    /*
    private void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        // Notify that an event has been called
        IoC.Log().WriteLine($"{Name} elapsed");
    }*/



    ~User()
    {
        // Notify that user has been destroyed/disposed
        IoC.Log().WriteLine($"\n{Name} destroyed <---------- \n");
    }
}

UserList Class

public class UserList
    {

        private ObservableCollection<User> _user = new ObservableCollection<User>();

        /// <summary>
        /// Stores a list of users 
        /// </summary>
        public ObservableCollection<User> Users
        {
            get => _user;
            set => _user = value;
        }

        public UserList()
        {
            Users.CollectionChanged += Users_CollectionChanged;
        }


        private void Users_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // When a user is added to the list
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                // Get the new user
                User addedUser = Users[e.NewStartingIndex];

                // Bind an Elapsed event to the user's timer
                addedUser._timer.Elapsed += (_sender, _e) => IoC.Log().WriteLine($"{addedUser.Name} elapsed"); 
            };
        }
    }

Calling class

protected override async void OnStartup(StartupEventArgs e) { // Setup DI (Using ninject with a constant binding to UserList class just as a test) IoC.Setup();

// Initialize a window
MainWindow = new MainWindow();
MainWindow.Show();


// Create a scoped user
// Should be destoryed as soon as we get out of scope
// Does actaully get disposed (because I am not binding an event ?)
{
    User user = new User("user");
};


// Adding user as a new instance 
IoC.Get<UserList>().Users.Add(new User("New User1"));
IoC.Get<UserList>().Users.Add(new User("New user2"));


// Creating user as a reference
User user1 = new User("user1");
User user2 = new User("user2");


// Adding the "reference" userlist
IoC.Get<UserList>().Users.Add(user1);
IoC.Get<UserList>().Users.Add(user2);



// Notify that we are deleting users
IoC.Log().WriteLine("\nWaiting 5 seconds\n");

await Task.Delay(3000);

IoC.Log().WriteLine("\nDeleting users..\n");


// Removing created users
IoC.Get<UserList>().Users.RemoveAt(3);
IoC.Get<UserList>().Users.RemoveAt(2);
IoC.Get<UserList>().Users.RemoveAt(1);
IoC.Get<UserList>().Users.RemoveAt(0);


// NOtify that we finished 
IoC.Log().WriteLine("\nFinised deleting\n");

//  And after a while the Garbage collector should dispose of the references

return;

Here is what I've attempted so far:

Creating an event inside the calling class that will be the same as the ElapsedEvent and binding the event when I am creating a User object and when I am about to dispose of a User object I unbind the event

Calling class

// Not binding any event to the user's that will be added to the list
// This works fine they are disposed like intended

//But this scoped user appears to not be disposed 
User user;
{
     user = new User("user");
     user._timer.Elapsed += _timer_Elapsed;
 };

 // Logic that takes a while
 // So we can see the event in action
 await Task.Delay(3000)


 // Unbind event.
 // But 'user' is still not being disposed even after existing scope
 user._timer.Elapsed -= _timer_Elapsed;

Trying to bind the event inside the UserList class. This didn't work because when I called the 'CollectionChanged' I didn't have access to the removed user And there was no way to unbind the event

UserList class

    private void Users_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
     // When a user is added to the list
     if (e.Action == NotifyCollectionChangedAction.Add)
     {
          // Get the new user
          User addedUser = Users[e.NewStartingIndex];

          / Bind an Elapsed event to the user's timer
          // This works because I have access to the newly created/added user 
          //addedUser._timer.Elapsed += (_sender, _e) => IoC.Log().WriteLine($"{addedUser.Name} elapsed"); 
                };
      }
      // If a user is removed from the list
      else
      {
           // After all user's have been removed this throws an OutOfRangeException, Furthermore I never have acces to the CURRENT user that was removed
           User addedUser = Users[e.OldStartingIndex];
      };
Josef Lintz
  • 32
  • 1
  • 3

0 Answers0