Сохранить ссылку на тип значения? - PullRequest
36 голосов
/ 13 февраля 2010

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

class Test
{
    public int a;
}

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

Спасибо.

Ответы [ 5 ]

52 голосов
/ 13 февраля 2010

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

(ВНИМАНИЕ: возможно для C # для поддержки двух других; на самом деле я написал прототип компилятора, который реализует эти функции. Он довольно изящен. детали.) Конечно, нужно написать алгоритм, который проверяет, что никакая ссылка локальная не может ссылаться на локальную, которая была в разрушенном кадре стека, что немного сложно, но выполнимо. Возможно, мы поддержим это гипотетическая будущая версия языка. (ОБНОВЛЕНИЕ: оно было добавлено в C # 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.

9 голосов
/ 13 февраля 2010

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

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

2 голосов
/ 16 апреля 2014

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

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 для любого типа значения (сначала не забудьте проверить нулевую проверку).)

0 голосов
/ 18 апреля 2014

Вы можете буквально взять указатель на тип значения, используя 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
    }
}
0 голосов
/ 30 ноября 2012
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-теги - другое. Начните использовать их!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...