C# 引用计数技术

6
我正在寻找在C#中实现引用计数的好模式。我有一个…
Dictionary<string, MyObject> ObjList;

我想做的是,如果存在MyObject实例,则分发对其的引用,如果不存在,则创建新的实例。我的代码中可能有多个地方引用MyObject实例,但当所有引用都被释放时,我希望将其从字典中删除。我已经研究了WeakReference,但不确定是否适用于此情况?
编辑1.) 具体来说,我正在使用一个OPC服务器,因此每当引用一个项目时,我希望使用字典查找现有项目的引用。当我不再需要该项目时,我想要取消订阅该项目。很难知道或者有多少地方在我的代码中当前正在使用该项目。

你试图做什么根本不清楚... - Thomas Levesque
我正在使用OPC服务器,因此当对象没有引用时,我希望取消订阅它。我认为我的字典始终会有一个引用,因此它永远不会被处理。 - MDK
WeakReference 可以工作,但如果您有能力显式地处理实例,我建议您直接使用它。 - Ry-
1
重要的是需要注意,MyObject必须正确实现IDisposable带有调用.Dispose()的类析构函数,否则对于MyObjectWeakReference将无法被处理。 - Enigmativity
2个回答

6
在这些情况下,我所做的是创建一个WeakReferences字典。 WeakReference 的作用是允许您的字典引用 MyObject 实例而不将该实例保留在内存中。 因此,一旦该对象的所有其他引用已被释放,该项的条目仍将存在于字典中。 但是,该条目将引用一个IsAlivefalseTargetnullWeakReference。 如果需要,您可以定期清理字典,删除所有WeakReference未激活的条目。

4
这是您需要的东西:
public class WeakReferences<T>
{
    private Func<string, T> _factory;

    public WeakReferences(Func<string, T> factory)
    {
        _factory = factory;
    }

    private Dictionary<string, WeakReference> _references =
        new Dictionary<string, WeakReference>();

    public T this[string index]
    {
        get
        {
            T target = default(T);
            if (_references.ContainsKey(index))
            {
                var wr = _references[index];
                target = (T)wr.Target;
                if (wr.IsAlive)
                {
                    Console.WriteLine("Reused: " + index);
                    return target;
                }
            }
            target = _factory(index);
            _references[index] = new WeakReference(target);
            return target;
        }
    }
}

您可以像这样使用它:

    Func<string, object> f = k =>
    {
        Console.WriteLine("Created: " + k);
        return new object();
    };

    var wrs = new WeakReferences<object>(f);

    var a = wrs["a"];
    var b = wrs["b"];
    a = wrs["a"];
    b = wrs["b"];
    a = null;
    GC.Collect();
    a = wrs["a"];
    b = wrs["b"];

运行此命令后我得到的输出结果是:
Created: a
Created: b
Reused: a
Reused: b
Created: a
Reused: b

棘手的部分是如何编写清理代码。在上面的示例中,您将如何获取。已删除:a.我需要放置代码告诉Opc服务器我完成了。 - MDK
@MDK - 你不需要告诉程序清理这一切。这都是弱引用 - 一旦你的代码中再也没有其他引用,所有这些都将被 GC 清除。 - Enigmativity
@MDK - 如果您添加的对象需要被处理,则可以轻松地删除 Func<string, T> factory 的需求,并允许外部分配实例,以便您可以自行管理处理。 - Enigmativity
所以我猜你的意思是没有自动的方法来判断对象何时被取消引用。我不想依赖于我的代码的其他用户明确地处理对象。 - MDK
1
@MDK - 你必须显式地处理你的对象。垃圾回收器不会自动处理它。所以,能够检查对象是否被引用并没有帮助。尽管如此,你可以检查WeakReferenceIsAlive属性来查看它是否已经被垃圾回收。但是,这仍然不能告诉你它是否已经被处理了。我认为你可能需要让我们知道你需要如何管理对象的生命周期,以便我们可以为你提供最佳建议。 - Enigmativity
@MDK - 在 MyObject.Dispose 中清除 MyObjects 对象,而不是在你的字典中。让 MyObject 实现 IDisposable 接口,这样就可以把需要的逻辑放到那里。将 MyObject 包含一个指向字典的指针,并在 MyObject.Dispose 方法中通过该指针调用字典的方法。然后,该字典方法可以告诉 OPC 服务器并删除 MyObject 弱引用。 - ToolmakerSteve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接