静态列表中的对象生存期 - 对象的弱引用

时间:2015-01-16 05:44:49

标签: c#

目前,我有以下课程。

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的每个类的生命周期都是无限的,否则我将调用Dispose explicity。

我可以阻止此行为,以便终身恢复正常吗? (当然没有删除访问派生对象的可能性,否则我的问题毫无意义)

我添加静态List,以处理继承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();

我怎样才能得到DerivedClass dc在正常生命之后调用他的析构函数的行为(因为它不在列表中)?


背景

我想用配置文件中的值填充派生类的属性,并在基类中执行。但是如果配置文件发生了变化,我必须更改alll派生类的所有属性。因此,如果您知道获取实现基类并需要更改的所有对象的方法,请告诉我。


更新

解决方案1:根据我的问题

Steve MitchamGo 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 Pattern

使用这种模式也很简单。此外,如果值已更改,派生类可以获取信息。 感谢您AlirezaGoto post)和Thangadurai

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)
    {

    }
}

3 个答案:

答案 0 :(得分:4)

如果没有看到更多的程序,就很难确定您的方法是否可以改进。但是,如果不从根本上改变您的设计,我会使用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 :(得分:1)

尽管史蒂夫·米奇曼的答案,你可以设计一个广播者类,提供一个事件,只要检测到配置文件发生变化就会被触发。派生类(对象)可以订阅此事件,并在获取或最终确定GC时取消订阅。这种方法很好地遵循开放/封闭原则。

答案 2 :(得分:0)

当您的BaseClass实例关闭时,既可以调用Dispose,也可以在实例本身被销毁时使用。这些情况很正常。