2

假设我有一个监控双精度值的类。我不想在每次调用监控类中的监控方法时都传递那个双精度值。因此,我想在构造函数中传递一个对double的引用,并将其存储起来,这样我就可以直接在monitor方法中比较值。

我的尝试是:

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

并将其用于:

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

不幸的是,这不起作用,因为 Monitor 构造函数中的赋值将值存储在 v 变量中。我试图将变量声明为,ref double v但 ref 只能在方法参数中使用。

该提案附近是否有任何解决方案不包括装箱/拆箱或d在每次调用时传递值watch

谢谢

注意:我想使用它的具体情况与上面的代码无关。这只是一种轻松展示我想要实现的目标的方式。

4

4 回答 4

4

简短的回答是您不能存储对双精度(或任何其他值类型)的引用。引用只能传递给方法(不存储在调用之间)。

长答案是,您可以将它包装在这样的类中并传递该类:

class DoubleWrap
{
    protected double val;

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

然后当你使用它时:

class Monitor {
    DoubleWrap v;
    public Monitor(DoubleWrap x) { v = x; }
    public watch() { Console.WriteLine("Value is: " + v.Value); }
}
于 2012-11-04T19:33:41.380 回答
1

请注意,编译器必须做一些特殊的提升,但我不确定声明为结构类型的局部变量是什么/如何!(也就是说,我没有声称缺少开销。)

但是,我确实喜欢闭包并且发现这是“不那么侵入性”,所以:

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

ref/out 参数(它不是通用类型属性)必须始终引用有效变量(让我们忽略 VB.NET 的属性技巧),因此这些属性不能应用于外部(成员)变量:

在编译时可以确保这种“引用有效变量”语义的唯一方法是限制ref/out在当前调用框架中使用语义 - 读取:在方法主体中 - 作为具有“引用变量”的调用框架保证存在。ref/out由于这个原因,甚至不能在嵌套的闭包/委托/lambdas中使用;它们可能会从当前调用框架中提升/调用。

于 2012-11-04T19:42:14.470 回答
1

如果要传递对内在值类型的引用,请创建一个长度为 1 的数组。所有数组都是引用类型。



    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.
    }
}
于 2014-12-09T21:08:39.277 回答
0

由于您正在尝试跟踪变量是否更改,因此更好的做法是在恕我直言时将您的变量包装在一个可观察的变量中,而不是轮询它。然后,您可以在变量更改时接收事件。当然,您必须更改从变量中读取和写入的方式。

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

然后你可以这样做:

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);
于 2012-11-04T20:20:23.943 回答