Время жизни объекта в статическом списке - слабая ссылка на объекты

1

На данный момент у меня есть следующий класс.

class BaseClass : IDisposable
{
    private static List<BaseClass> instances = new List<BaseClass>();

    protected BaseClass()
    {
        instances.Add(this);
    }
    ~BaseClass()
    {
        Dispose();
    }
    public void Dispose()
    {
        instances.Remove(this);
    }
}

Таким образом, время жизни каждого класса, наследующего BaseClass, бесконечно, пока я не BaseClass программу, иначе я вызову Dispose.

Могу ли я предотвратить такое поведение, чтобы срок службы вернулся к норме? (конечно, не удаляя возможности доступа к производным объектам, иначе мой вопрос не имеет смысла)

Я добавляю статический список, чтобы обрабатывать различные действия для всех классов, которые наследуют BaseClass.


редактировать

class DerivedClass : BaseClass
{
}


//This case works
using (DerivedClass _dc = new DerivedClass())
{
      //Do something with object
}
//This object will live forever, because it is internally in the static list
//That behaviour is not desired
DerivedClass dc = new DerivedClass();

Как я могу получить bahaviour, который DerivedClass DerivedClass dc называет своим деструктором после обычного времени жизни (как его бы не было в списке)?


Задний план

Я хочу заполнить свойства производных классов значениями из файла конфигурации и сделать это в базовом классе. Но если файл конфигурации изменился, я должен изменить все свойства классов alll. Поэтому, если вы знаете способ получить все объекты, которые реализуют базовый класс и должны быть изменены, сообщите мне об этом.


Обновить

Решение 1. Основываясь на моем вопросе

С помощью Стива Митчама (Go to post) я узнал, как я могу сделать небольшую ссылку с несколькими строками кода:

class BaseClass : IDisposable
{
    private static List<GCHandle> handles = new List<GCHandle>();
    protected BaseClass()
    {
        this.handle = GCHandle.Alloc(this, GCHandleType.Weak);
        handles.Add(this.handle);
    }
    ~BaseClass()
    {
        Dispose();
    }
    public void Dispose()
    {
        if (handle.IsAllocated)
        {
            //Do Something more to Dispose the Object
            //...
            handle.Free();
            handles.Remove(handle);
        }
    }
    public void DoSomethingWithTheList()
    {
        foreach (GCHandle handle in handles)
        {
            BaseClass bc = (BaseClass)handle.Target;
            //Do something
        }
    }
}

Теперь, если я вызову GC.Collect(); он будет собирать мои неиспользуемые производные классы (поэтому я думаю, что сборщик мусора будет также собирать мои объекты), потому что сам объект не имеет ссылки в списке.

Спасибо!

Решение 2: шаблон Subsrciber/Broadcaster

С этим шаблоном это тоже просто. Кроме того, производный класс может получить информацию, если значения изменены. Спасибо Алиреза (Goto post) и Тангадурай.

class ConfigurationBroadcaster
{
    string path = "";
    public string Path
    {
        get { return path; }
        set 
        {
            bool changed = path != value;
            path = value;
            if(changed)
            if (ChangedConfigurationValues != null)
            {
                Delegate[] invocationList = ChangedConfigurationValues.GetInvocationList();
                foreach (var item in invocationList)
                {
                    Type t = item.Target.GetType();
                    PropertyInfo[] pInfos = t.GetProperties();
                    foreach (PropertyInfo pInfo in pInfos)
                    {
                        //new object() have to be the value from config file
                        //5 is used to set Width and Height from BroadcastSubscriber for this example
                        pInfo.SetValue(item.Target, 5/* new object()*/, null);
                    }
                }
               ChangedConfigurationValues(this, new EventArgs());
            }
        }
    }
    public event EventHandler ChangedConfigurationValues;
}
class BaseBroadcastSubscriber
{
    ConfigurationBroadcaster broadcaster;
    protected BaseBroadcastSubscriber(ConfigurationBroadcaster broadcaster)
    {
        this.broadcaster = broadcaster;
        this.broadcaster.ChangedConfigurationValues += new EventHandler(broadcaster_ChangedConfigurationValues);
    }

    void broadcaster_ChangedConfigurationValues(object sender, EventArgs e)
    {
        Console.WriteLine("Configuration values changed");
    }
}
class BroadcastSubscriber : BaseBroadcastSubscriber
{
    int width,height;

    public int Width
    {
        get { return width; }
        set { width = value; }
    }

    public int Height
    {
        get { return height; }
        set { height = value; }
    }
    public BroadcastSubscriber(ConfigurationBroadcaster broadcaster)
        : base(broadcaster)
    {

    }
}
  • 0
    Зачем вам вручную держать экземпляры объектов, которые вы создаете?
  • 0
    Пожалуйста, прочитайте последнее предложение. Я должен обрабатывать различные действия для всех объектов. Если вы знаете другой способ сделать это, не стесняйтесь объяснить это.
Показать ещё 9 комментариев
Теги:

3 ответа

3
Лучший ответ

Не видя больше своей программы, будет сложно определить, можно ли улучшить ваш подход. Однако, без принципиального изменения вашего дизайна, я бы использовал WeakReferences для отслеживания ваших объектов. Метод UpdateClasses предназначен для имитации вашего действия по перенастройке.

class BaseClass : IDisposable
{
    private WeakReference<BaseClass> myReference;
    private static List<WeakReference<BaseClass>> instances = new List<WeakReference>();

    public static UpdateClasses(MyData stuff)
    {
        foreach(var ref in instances)
        {
           BaseClass target;
           if (ref.TryGetTarget(out target))
           {
               // code to update target here
           }
        }
    }
    protected BaseClass()
    {
       myReference = new WeakReference<BaseClass>(this,true);
       instances.Add(myReference);
    }

    ~BaseClass()
    {
       Dispose();
    }

    public void Dispose()
    {
       instances.Remove(myReference);
    }
}

Слабые ссылки не будут держать ваши объекты в живых. Когда они получат сбор мусора, они удалят ссылки из списка экземпляров. Обратите внимание, однако, что эта реализация заставит ваши объекты оставаться живыми в сборщике мусора дольше, чем обычно, из-за требования к финализатору, что может снизить производительность приложения с течением времени.

1

Несмотря на ответ Стива Митчмана, вы можете создать класс вещателя, обеспечивающий событие, которое запускается при каждом изменении файла конфигурации. Производные классы (объекты) могут подписаться на это событие и отказаться от подписки на получение GC или его финализацию. Этот подход следует принципу открытого/закрытого очень хорошо.

  • 0
    Я добавлю пример к моему вопросу. Не так сложно, как я думал. Спасибо.
  • 1
    Подписка классов на событие приведет к появлению ссылки, которая предотвратит их сборку мусора, поэтому вам придется встроить слабые ссылки в класс вещателя или принудительно утилизировать с помощью классов, полученных с помощью dervied. Кроме того, я пытался не менять дизайн, но я бы согласился, что другой дизайн, вероятно, является лучшим решением.
Показать ещё 2 комментария
0

Когда ваш экземпляр BaseClass выключен, это происходит, когда вы вызываете Dispose и когда сам экземпляр уничтожается. Эти случаи являются нормальными.

  • 0
    Я знаю о нормальных случаях. Мне нужен твик, чтобы получить доступ ко всем производным объектам без изменения их времени жизни.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню