47 votes

Stocker une référence à un type de valeur?

J'écris un objet "Moniteur" pour faciliter le débogage de mon application. Cet objet Monitor est accessible au moment de l'exécution à partir d'un interpréteur IronPython. Ma question est, est-il possible en C # de stocker une référence à un type de valeur? Disons que j'ai la classe suivante:

 class Test
{
    public int a;
}
 

Puis-je en quelque sorte stocker un "pointeur" sur "a" afin de pouvoir vérifier sa valeur à tout moment? Est-il possible d'utiliser du code sécurisé et géré?

Merci.

63voto

Eric Lippert Points 300275

Vous ne pouvez pas stocker une référence à une variable dans un champ ou d'un tableau. Le CLR exige qu'une référence à une variable dans (1) un paramètre formel, (2) un local, ou (3) le type de retour d'une méthode. C# prend en charge (1), mais pas les deux autres.

(APARTÉ: Il est possible en C# pour soutenir les deux autres; en fait, j'ai écrit un prototype de compilateur de ne mettre en œuvre ces fonctionnalités. Il est assez soignée. (Voir http://ericlippert.com/2011/06/23/ref-returns-and-ref-locals/ pour plus de détails.) Bien sûr, on doit écrire un algorithme qui vérifie qu'aucune ref local pourraient éventuellement être référence à un local qui était en maintenant détruite cadre de la pile, qui est un peu délicat, mais sa faisable. Peut-être que nous allons appuyer ce dans un futur hypothétique version de la langue.)

Toutefois, vous pouvez créer une variable avons arbitrairement longue durée de vie, en le mettant dans un champ ou d'un tableau. Si ce que vous avez besoin est une "référence" dans le sens de "j'ai besoin de stocker un alias d'une variable arbitraire", alors, non. Mais si ce que vous avez besoin est une référence dans le sens de "j'ai besoin d'une magie jeton qui me permet de lire et d'écrire dans une variable particulière", alors il suffit d'utiliser un délégué, ou une paire de délégués.

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;
}

Que l'extérieur de la variable locale x va rester en vie au moins jusqu'à ce que le rx est récupérée.

9voto

Reed Copsey Points 315315

Non - vous ne pouvez pas stocker un "pointeur" vers un type de valeur directement en C #.

En règle générale, vous détenez une référence à l'instance de test contenant "a" - cela vous donne accès à a (via testInstance.a ).

3voto

Spencer Rose Points 566

Voici un modèle que je suis venu avec que je trouve moi-même à l'aide un peu. Habituellement, dans le cas du passage propriétés de paramètres pour une utilisation sur n'importe quel objet de type parent, mais cela fonctionne tout aussi bien pour une seule instance. (ne fonctionne pas pour une portée locale types de valeur 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);
        }
    }
}

Cela peut ensuite être utilisé comme

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;
}

Avis de l'interface avec un ensemble limité d'arguments de modèle, ce qui vous permet à passer le pointeur à quelque chose qui n'a aucune connaissance du type parent.

Vous pouvez même mettre en œuvre une autre interface avec pas de modèle arguments qui appelle .ToString sur n'importe quel type de valeur (n'oubliez pas le nul de vérifier d'abord)

1voto

ja72 Points 9417

Vous pouvez littéralement prendre un pointeur vers un type de valeur en utilisant le code usafe

 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
    }
}
 

-4voto

Gorlykio Points 11
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; }
    }
}

Pourquoi mettez-vous à travers tous les tracas que de l'écriture de code compliqué, à déclarer des identifiants partout en la reliant au même endroit? Faire une propriété, ajouter un peu de code XML pour vous aider à l'extérieur de la classe, et utiliser les propriétés dans votre codage.

Je ne sais pas à propos de l'enregistrement de pointeur, ne pense pas que c'est possible, mais si vous êtes juste envie de vérifier sa valeur, le moyen le plus sûr, à ma connaissance, est de créer une propriété de la variable. Au moins de cette façon, vous pouvez vérifier sa propriété, à tout moment, et si la variable est statique, vous ne avez même pas besoin de créer une instance de la classe pour accéder à la variable.

Les propriétés ont beaucoup d'avantages; type de sécurité est l'un, des balises XML autre. Commencez à les utiliser!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X