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

Хранить ссылку на тип значения?

Я пишу объект "Монитор", чтобы облегчить отладку моего приложения. Этот объект Monitor можно получить во время выполнения от интерпретатора IronPython. Мой вопрос: возможно ли в С# хранить ссылку на тип значения? Скажем, у меня есть следующий класс:

class Test
{
    public int a;
}

Можно ли каким-либо образом сохранить "указатель" на "а", чтобы иметь возможность проверить его значение в любое время? Возможно ли использование безопасного и управляемого кода?

Спасибо.

4b9b3361

Ответ 1

Вы не можете сохранить ссылку на переменную в поле или массиве. CLR требует, чтобы ссылка на переменную была в (1) формальным параметром, (2) местным или (3) возвращаемым типом метода. С# поддерживает (1), но не два других.

(ASIDE: возможно, С# поддерживает два других, на самом деле я написал прототип-компилятор, который реализует эти функции. Он довольно опрятен. (См. Http://ericlippert.com/2011/06/23/ref- return-and-ref-locals/ для подробностей.) Конечно, нужно написать алгоритм, который проверяет, что ни один локатор ссылок не может ссылаться на локальный, который был на теперь разрушенном стеке стека, что немного сложно, но возможно, мы будем поддерживать это в гипотетической будущей версии языка. (ОБНОВЛЕНИЕ: он был добавлен в С# 7!))

Однако вы можете сделать переменную, имеющую сколь угодно долгое время жизни, помещая ее в поле или массив. Если вам нужна "ссылка" в смысле "Мне нужно сохранить псевдоним произвольной переменной", то нет. Но если вам нужна эта ссылка в смысле "Мне нужен волшебный токен, который позволяет мне читать и писать конкретную переменную", то просто используйте делегат или пару делегатов.

sealed class Ref<T> 
{
    private Func<T> getter;
    private Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter)
    {
        this.getter = getter;
        this.setter = setter;
    }
    public T Value
    {
        get { return getter(); }
        set { setter(value); }
    }
}
...
Ref<string> M() 
{
    string x = "hello";
    Ref<string> rx = new Ref<string>(()=>x, v=>{x=v;});
    rx.Value = "goodbye";
    Console.WriteLine(x); // goodbye
    return rx;
}

Внешняя локальная переменная x останется активной, по крайней мере, до тех пор, пока rx не будет восстановлен.

Ответ 2

Нет - вы не можете сохранить "указатель" на тип значения непосредственно на С#.

Как правило, вы должны иметь ссылку на экземпляр Test, содержащий "a" - это дает вам доступ к a (через testInstance.a).

Ответ 3

Вот образец, с которым я столкнулся, что я нахожу себя довольно немного. Обычно в случае передачи свойств в качестве параметров для использования на любом объекте родительского типа, но он работает точно также для одного экземпляра. (не работает для локальных значений значений типа tho)

public interface IValuePointer<T>
{
    T Value { get; set; }
}
public class ValuePointer<TParent, TType> : IValuePointer<TType>
{
    private readonly TParent _instance;
    private readonly Func<TParent, TType> _propertyExpression;
    private readonly PropertyInfo _propInfo;
    private readonly FieldInfo _fieldInfo;

    public ValuePointer(TParent instance, 
                        Expression<Func<TParent, TType>> propertyExpression)
    {
        _instance = instance;
        _propertyExpression = propertyExpression.Compile();
        _propInfo = ((MemberExpression)(propertyExpression).Body).Member as PropertyInfo;
        _fieldInfo = ((MemberExpression)(propertyExpression).Body).Member as FieldInfo;
    }

    public TType Value
    {
        get { return _propertyExpression.Invoke(_instance); }
        set
        {
            if (_fieldInfo != null)
            {
                _fieldInfo.SetValue(_instance, value);
                return;
            }
            _propInfo.SetValue(_instance, value, null);
        }
    }
}

Затем это можно использовать так

class Test
{
    public int a;
}
void Main()
{
    Test testInstance = new Test();
    var pointer = new ValuePointer(testInstance,x=> x.a);
    testInstance.a = 5;
    int copyOfValue = pointer.Value;
    pointer.Value = 6;
}

Обратите внимание на интерфейс с более ограниченным набором аргументов шаблона, это позволяет передать указатель на то, что не знает тип родителя.

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

Ответ 4

Вы можете буквально взять указатель на тип значения, используя usafe code

public class Foo
{
    public int a;
}

unsafe static class Program
{
    static void Main(string[] args)
    {
        var f=new Foo() { a=1 };
        // f.a = 1

        fixed(int* ptr=&f.a)
        {
            *ptr=2;
        }
        // f.a = 2
    }
}

Ответ 5

class Test
{
    private int a;

    /// <summary>
    ///  points to my variable type interger,
    ///  where the identifier is named 'a'.
    /// </summary>
    public int A
    {
        get { return a; }
        set { a = value; }
    }
}

Зачем ставить себя на все трудности в написании сложного кода, объявляя идентификаторы повсюду, ссылающиеся на одно и то же место? Создайте свойство, добавьте XML-код, который поможет вам выйти за пределы класса, и используйте свойства в вашей кодировке.

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

Свойства имеют много преимуществ; тип безопасности - один, XML-теги - другой. Начните использовать их!