На данный момент у меня есть следующий класс.
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. Поэтому, если вы знаете способ получить все объекты, которые реализуют базовый класс и должны быть изменены, сообщите мне об этом.
С помощью Стива Митчама (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();
он будет собирать мои неиспользуемые производные классы (поэтому я думаю, что сборщик мусора будет также собирать мои объекты), потому что сам объект не имеет ссылки в списке.
Спасибо!
С этим шаблоном это тоже просто. Кроме того, производный класс может получить информацию, если значения изменены. Спасибо Алиреза (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)
{
}
}
Не видя больше своей программы, будет сложно определить, можно ли улучшить ваш подход. Однако, без принципиального изменения вашего дизайна, я бы использовал 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);
}
}
Слабые ссылки не будут держать ваши объекты в живых. Когда они получат сбор мусора, они удалят ссылки из списка экземпляров. Обратите внимание, однако, что эта реализация заставит ваши объекты оставаться живыми в сборщике мусора дольше, чем обычно, из-за требования к финализатору, что может снизить производительность приложения с течением времени.
Несмотря на ответ Стива Митчмана, вы можете создать класс вещателя, обеспечивающий событие, которое запускается при каждом изменении файла конфигурации. Производные классы (объекты) могут подписаться на это событие и отказаться от подписки на получение GC или его финализацию. Этот подход следует принципу открытого/закрытого очень хорошо.
Когда ваш экземпляр BaseClass выключен, это происходит, когда вы вызываете Dispose и когда сам экземпляр уничтожается. Эти случаи являются нормальными.