Подписка на событие предотвращает сбор мусора в объектах List. - PullRequest
0 голосов
/ 31 октября 2018

Так что я в основном подвергаю сомнению все, что узнал о сборке мусора в C #.

У меня есть класс с именем User, User имеет 2 свойства: (system.Timers)Timer и свойство name каждый раз, когда создается User. Я связываю событие, событие состоит из каждых 0,5 секунд. произошло.

Проблема в том, что каждый раз, когда я связываю событие с User, создается впечатление, что сборщик мусора просто не избавляется от класса, даже если я удаляю его из списка.

User класс

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 Класс

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"); 
            };
        }
    }

Класс звонков

защищенное переопределение async void OnStartup (StartupEventArgs e) { // Настройка DI (использование ninject с постоянной привязкой к классу UserList в качестве теста) 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;

Вот что я пытался сделать до сих пор:

Создание события внутри вызывающего класса, которое будет таким же, как ElapsedEvent, и связывание события при создании объекта User и когда я собираюсь избавиться от объекта User, я отменяю событие

Класс вызова

// 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;

Попытка связать событие внутри класса UserList. Это не сработало, потому что когда я вызывал «CollectionChanged», у меня не было доступа к удаленному пользователю, и не было никакого способа отменить привязку события

UserList класс

    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];
      };
...