Понимание ConditionalWeakTable

Я пытаюсь понять ConditionalWeakTable. В чем разница между

class ClassA
{
    static readonly ConditionalWeakTable<ClassA, OtherClass> OtherClassTable
        = new ConditionalWeakTable<ClassA, OtherClass>();
}

и

class ClassB
{
    OtherClass otherClass;
}

? Каковы были бы плюсы и минусы использования ClassA или ClassB для ссылки на nullable поле?

3 ответов


Я не совсем понимаю, о чем вы спрашиваете, я предполагаю, что вы спрашиваете, следует ли использовать свойство внутри вашего типа или ConditionalWeakTable, к которому можно присоединить такое свойство конкретного экземпляра типа. Если это так, вы можете также спросить, следует ли использовать только свойство или словарь, который может содержать это свойство под определенным ключом (который будет вашим конкретным экземпляром типа). Если вам не нужен такой словарь, это довольно глупо.

понимание ConditionalWeakTable<TKey, TValue>:

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

Итак, вы должны спросить себя, Каковы ваши по необходимости. Предполагая, что ваши типы инстанцированы:

var classA = ClassA(); 
var classB = ClassB(); 
var other = OtherClass();

вы хотите использовать свойство, привязанное к таким экземплярам следующим образом:

/* set */
var other = new OtherClass();        
ClassA.OtherClassTable.Add(classA, other);
/* get */
OtherClass data = null;
var result = ClassA.OtherClassTable.TryGetValue(classA, out data);

вместо этого ниже?

/* set */
classB.OtherClass = other;
/* get */
var result = classB.OtherClass;

если у вас нет особых потребностей, ответ кажется довольно очевидным. Есть конечно еще вот вопросы:

что такое слабая ссылка и почему вы хотите ее использовать?

этой MSDN статьи коротко объясняет тему. Он в основном говорит, что слабые ссылки не продлевают срок службы объекта, позволяя ему собирать мусор, как только такой объект все еще может быть достигнут кодом приложения. Слабые ссылки могут быть полезны для указания на объекты, которые должны быть доступны для GC, если они не используются активно. Однако, если программа использует большое количество небольших объектов, слабые ссылки могут негативно повлиять на использование памяти. Темы как этой и этой следует также прояснить некоторые оставшиеся сомнения.

если вы ищете пример, когда вы могли бы использовать ConditionalWeakTable<TKey, TValue> стандартные Dictionary<TKey, TValue> представьте себе следующий случай. Вы хотите привязать словарь свойств и экземпляр во время выполнения, но в то же время не хотите препятствовать их сбору, если вы перестали активно их использовать. К сожалению, в стандартном подходе это невозможно - GC блокируется, потому что словарь по-прежнему имеет сильный ссылки на них такие:

var x = new object();
x.Props().Y = "hello";

static class ExpandoExtensions 
{
    private static IDictionary<object, dynamic> props = 
        new Dictionary<object, dynamic>();
    public static dynamic Props(this object key)
    { 
        dynamic o;
        if (!props.TryGetValue(key, out o)){
            o = new ExpandoObject();
            props[key] = o;
        }
        return o;       
    } 
}

конечно, вы всегда можете снять их вручную, но разве этот подход не показан ниже проще?

static class ExpandoExtensions
{
    private static readonly ConditionalWeakTable<object, ExpandoObject> props =
        new ConditionalWeakTable<object, ExpandoObject>();

    public static dynamic Props(this object key)
    { 
        return props.GetOrCreateValue(key);       
    } 
}

в то же время (MSDN)

избегайте использования слабых ссылок в качестве автоматического решения для памяти проблемы управления. Вместо этого разработайте эффективную политику кэширования для обработка объектов приложения.

эти методы расширения, показанные выше взяты из этого нить.


самая большая разница между этими двумя -- действительно, основная причина, что и почему это часть CompilerServices--это добавление поля к ClassA требуется возможность добавления поля в ClassA, но строительство ConditionalWeakTable с ключом типа ClassA нет. Эффективно,ConditionalWeakTable существует, чтобы позволить коду эффективно "добавлять поле" к экземплярам любого класса без необходимости изменять сам класс. Хотя это может быть во многих случаях для этой цели можно использовать словарь слабых ключей на основе идентификаторов, который будет работать только до тех пор, пока ни одно значение не содержит прямой или косвенной ссылки на его ключ, и не было циклов ключей, на которые ссылаются напрямую или непосредственно через значения другого ключа. Дизайн ConditionalWeakTable позволяет собирать ключи и значения, даже если такие циклы существуют.


одним из преимуществ первого подхода является то, что вы можете кэшировать экземпляры OtherClass. Например,

class ClassA
{
    static readonly ConditionalWeakTable<ClassA, OtherClass> OtherClassTable
        = new ConditionalWeakTable<ClassA, OtherClass>();

    public void Attach(OtherClass otherClass)
    {
        OtherClassTable.Add(this, otherClass);
    }

    public bool Get(out OtherClass otherClass)
    {
        return OtherClassTable.TryGetValue(this, out otherClass);
    }
}

затем вы можете прикрепить экземпляр OtherClass экземпляр ClassA. Пример OtherClass будет храниться в памяти до тех пор, как экземпляр ClassA, к которому он прикреплен жива.