在扩展方法中保留状态

时间:2012-05-18 14:53:22

标签: c# dictionary reference extension-methods state

C#团队之前曾考虑过向C#添加扩展属性,事件等。

Per Eric Lippert:

http://blogs.msdn.com/b/ericlippert/archive/2009/10/05/why-no-extension-properties.aspx

然而,要使这些功能有用,它们必须能够使用对象存储一些新的状态。看起来这样做的唯一方法是使用字典并将对象的每个实例与其他状态相关联。

如果可以通过创建我自己的字典(以及可能的get / set扩展方法)“手动”复制此功能,将会很有用。但是,为了将对象的特定实例与某个状态相关联,您需要将实际的引用散列到该对象。在另一种语言中,您可以通过散列其内存位置来实现此目的,但是在C#中不能保证保持不变,并且使用不安全的代码来完成此功能远非理想。

有没有人知道是否可以获得一些对象的可散列引用,该对象在对象的内部状态发生变化时不会发生变化?显然有一些内部机制可以跟踪单个对象而不管它们的内存位置如何,但我不确定它是否暴露给用户代码。

注意:简单地散列对象本身根本不起作用,因为GetHashCode()依赖于对象的内部状态,而不是对象

感谢您的任何见解。

2 个答案:

答案 0 :(得分:15)

您正在寻找ConditionalWeakTable class

答案 1 :(得分:0)

**全程编辑** 这些属性保存在一个字典中,该字典使用对象的弱引用作为键,以及一个带有字符串 - 对象对的字典来存储属性及其值。

要设置,获取或删除对象的属性,该对象将在字典中的弱引用中进行搜索。

处理未使用的属性可能有两种方法:

  • 检查弱引用的IsAlive,如果为false,则删除字典中的条目
  • 在“可扩展”对象中实现IDisposable,并调用一个扩展方法来删除正在处理的对象的属性。

我在示例代码中包含了一个可选的using块,因此您可以调试并查看Dispose如何调用RemoveProperties扩展方法。这当然是可选的,当对象是GC时,将调用该方法。

使用WeakReference,静态字典和IDisposable来构思该想法。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            using (PropertyLessClass plc = new PropertyLessClass())
            {
                plc.SetProperty("age", 25);
                plc.SetProperty("name", "John");
                Console.WriteLine("Age: {0}", plc.GetProperty("age"));
                Console.WriteLine("Name: {0}", plc.GetProperty("name"));
            }
            Console.ReadLine();
        }
    }
}

public class PropertyLessClass : IDisposable
{
    public void Dispose()
    {
        this.DeleteProperties();
    }
}

public static class PropertyStore
{
    private static Dictionary<WeakReference, Dictionary<string, object>> store
        = new Dictionary<WeakReference, Dictionary<string, object>>();

    public static void SetProperty(this object o, string property, object value)
    {
        var key = store.Keys.FirstOrDefault(wr => wr.IsAlive && wr.Target == o);
        if (key == null)
        {
            key = new WeakReference(o);
            store.Add(key, new Dictionary<string, object>());
        }
        store[key][property] = value;
    }

    public static object GetProperty(this object o, string property)
    {
        var key = store.Keys.FirstOrDefault(wr => wr.IsAlive && wr.Target == o);
        if (key == null)
        {
            return null; // or throw Exception
        }
        if (!store[key].ContainsKey(property))
            return null; // or throw Exception
        return store[key][property];
    }

    public static void DeleteProperties(this object o)
    {
        var key = store.Keys.FirstOrDefault(wr => wr.IsAlive && wr.Target == o);
        if (key != null)
        {
            store.Remove(key);
        }
    }
}