Подтвердить что ты не робот

Понимание ConditionalWeakTable

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

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

и

class ClassB
{
    OtherClass otherClass;
}

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

4b9b3361

Ответ 1

Я не совсем понимаю, о чем вы спрашиваете, но я предполагаю, что вы спрашиваете, следует ли использовать свойство внутри вашего типа или 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)

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

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

Ответ 2

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

Ответ 3

Одним из преимуществ первого подхода является то, что вы можете кэшировать экземпляры 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, к которому он присоединен, остается в живых.