简体   繁体   中英

Using a double by reference in C#

Let's say I have a class that monitors a double value. I do not want to pass that double value every time I call the monitor method in the monitoring class. Therefore, I would like to pass a reference to the double in the constructor, and store it, so I can compare values directly in the monitor method.

My try was:

class Monitor {
    double v;
    public Monitor(ref double x) { v = x; }
    public watch() { Console.WriteLine("Value is: " + v); }
}

And use this with:

double d = 10;
Monitor m = new Monitor(ref d);
while (d > 0) {
  m.watch();
  d--;
}

Unfortunately this doesn't work, as the assignment in the Monitor constructor is storing the value in the v variable. I tried to declare the variable as ref double v but ref can only be used in methods parameters.

Is there any solution near this proposal that doesn't include boxing/unboxing or passing the d value in every call to watch ?

Thank you

NOTE: the concrete situation in which I want to use this is not related with the code above. It is just a way of showing easily what I want to achieve.

The short answer is you can't store a reference to a double (or any other value type for that matter). References can only be passed to methods (not stored between calls).

The long answer is, you can wrap it in a class like this and pass the class around:

class DoubleWrap
{
    protected double val;

    public DoubleWrap(double _val) { val = _val; }
    public double Value { get { return val } set { val = value; } }
}

Then when you use it:

class Monitor {
    DoubleWrap v;
    public Monitor(DoubleWrap x) { v = x; }
    public watch() { Console.WriteLine("Value is: " + v.Value); }
}

Please note, the compiler has to do some special lifting, but I am not sure what/how for a local variable declared as a structure type! (That is, I make no claim as to overhead of lack thereof.)

However, I do like closures and find this is "less intrusive", so:

class Monitor {
    Func<double> v;
    public Monitor(Func<double> x) { v = x; }
    public watch() { Console.WriteLine("Value is: " + v()); }
}

double d = 10;
Monitor m = new Monitor(() => d);
while (d > 0) {
  m.watch();
  d--;
}

A ref/out parameter (it's not a general type attribute) must always refer to a valid variable (let's ignore VB.NET tricks for properties) and thus these attributes cannot be applied to an external (member) variable:

The only way this "refers to a valid variable" semantic can be ensured at compile-time is to limit the use of the ref/out semantics in the current call frame - read: in the method body - as the calling frame with the "referenced variables" is guaranteed to exist. ref/out cannot even be used inside nested closures/delegates/lambdas for this very reason; they might be lifted/invoked out of the current call frame.

Since you're trying to track whether a variable changes, a better thing to do instead of polling it for changes IMHO would be to wrap your variables in an observable. Then you can receive events when your variable changes. Of course, you'll have to change the way you read and write from your variables.

public class ObservableObject<T>
        : INotifyPropertyChanging, INotifyPropertyChanged
{
    public ObservableObject(T defaultValue = default(T),
                            IEqualityComparer<T> comparer = null)
    {
        this.value = defaultValue;
        this.comparer = comparer ?? EqualityComparer<T>.Default;
    }

    private T value;
    private IEqualityComparer<T> comparer;
    public T Value
    {
        get { return value; }
        set
        {
            if (!comparer.Equals(this.value, value))
            {
                OnValueChanging();
                this.value = value;
                OnValueChanged();
            }
        }
    }

    public event PropertyChangingEventHandler PropertyChanging;
    protected virtual void OnValueChanging()
    {
        var propertyChanging = PropertyChanging;
        if (propertyChanging != null)
            propertyChanging(this, new PropertyChangingEventArgs("Value"));
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnValueChanged()
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs("Value"));
    }
}

Then you can just do this:

var d = new ObservableObject<double>(10);
d.PropertyChanged += (o, e) =>
{
    var obs = (ObservableObject<double>)o;
    Console.WriteLine("Value changed to: {0}", obs.Value);
};
Console.WriteLine("Value is: {0}", d.Value);
d.Value = 20;
Console.WriteLine("Value is: {0}", d.Value);

If you want to pass a reference to an intrinsic value type around make an array of length one. All arrays are reference types.



    class A {
        private double[] sharedDbl;
        public A(double[] d) {
            sharedDbl = d;
        }
    }

    class Program {
        public static void Main() {
            // other code
            double[] shared = new double[1];
            A a1, a2;

            shared[0] = 34.55d;
            a1 = new A(shared);
            a2 = new A(shared);
        // Now both a1 and a2 have a reference to the same double value.
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM