2

好的,我需要能够跟踪值类型对象,这些对象是另一个对象的属性,如果不让这些属性实现 IObservable 接口或类似接口,就无法做到这一点。然后我想到了闭包和 Jon Skeet 的著名例子,以及它是如何多次打印出 9(或 10)而不是数字的升序。所以我想为什么不这样做:

Class MyClass
{
    ...
    Func<MyValueType> variable;
    ...
    public void DoSomethingThatGetsCalledOften()
    {
        MyValueType blah = variable(); //error checking too not shown for brevity
        //use it
    }
    ...
}
... in some other consuming code ...
MyClass myClass = new MyClass();
myClass.variable = () => myOtherObject.MyOtherProperty;
//then myClass will get the current value of the variable whenever it needs it

显然,这需要对闭包的工作原理有所了解,但我的问题是:这是一个好主意还是一个肮脏的黑客和对闭包系统的滥用?

编辑:由于有些人似乎误解了我想说的话,这里有一个控制台程序来演示它:

using System;
using System.Linq;

namespace Test
{
    class Program
    {
        public static void Main()
        {
            float myFloat = 5;
            Func<float> test = () => myFloat;
            Console.WriteLine(test());
            myFloat = 10;
            Console.WriteLine(test());
            Console.Read();
        }

    }
}

5那将打印出来10

4

3 回答 3

8

您偶然发现了著名的公案:闭包是穷人的对象。您正在使用Action<T>替换类型的属性获取器T。这样的事情在更动态的语言中(稍微)不是一个肮脏的把戏,因为它可以通过注入一个用你的日志记录函数装饰的 getter 来实现,但是在 C# 中没有一种优雅的方式来猴子修补某人的财产时他们没想到。

于 2009-12-26T04:07:24.263 回答
2

作为一种从属性中获取值的机制,它会起作用(但它不会提供任何及时通知更新的机制)。但是,这取决于您打算如何使用它。为了方便地做到这一点,您需要在代码中使用一堆 lambda,或者让一些DynamicMethod/Expression代码在运行时执行此操作。在大多数情况下,更类似于反射的东西会更方便。

我不一定会担心“价值类型”方面;在大多数情况下,这不是瓶颈,尽管有 FUD - 处理此类代码通常object比通过泛型或类似方法容易得多。

我的 IDE 中有一些代码演示了DynamicMethod原始反射(我打算不久之后写博客),展示了基于反射的代码如何不必很慢(或者只是使用HyperDescriptor)。

另一种选择是实现正确的接口/添加正确的事件。也许通过 PostSharp,也许通过动态类型(在运行时继承和覆盖),也许使用常规代码。

于 2009-12-26T07:51:06.720 回答
1

您需要将您的variable成员输入为Func<MyValueType>(或另一个delegate返回MyValueType),但您无法以blah这种方式分配 的值。就像在foreach上面的循环中使用闭包一样,它只会在某个时间点进行评估。这不是使变量的值与其他对象保持同步的方法。事实上,如果没有以下任何一项,就没有办法做到这一点:

  • 在某种循环中持续监控另一个实例的属性值,比如Timer
  • 在另一个实例的类上实现更改通知事件

您将能够实现这样的属性(因为在每次调用时都会评估属性),但是使用自定义委托有什么意义,除了您不必了解其他实例的任何内容。

编辑

我会尽量让这个更清楚一点。使用您发布的此代码:

Class MyClass
{
    ...
    Action<MyValueType> variable;
    ...
    MyValueType blah = variable();
    //use it
    ...
}
...
MyClass myClass = new MyClass();
myClass.variable = () => myOtherObject.MyOtherProperty;

首先,要使其具有功能,variable应该是Func<MyValueType>,而不是Action<MyValueType>Func返回值,Action不;由于您尝试将值分配给变量,因此需要表达式来返回值)。

其次,你的方法的主要问题是——假设我正确地阅读了你的代码——你试图将实例变量的值分配给类声明blah中的评估值。variable()这不起作用有几个原因:

  • 类声明中的赋值不能访问实例成员(即variable
  • 类声明中的变量赋值只发生在对象构造时。即使存在第一个条件,您也只会NullReferenceException在实例化对象时得到 a ,因为它会尝试评估variable,这将是null那个时候
  • 即使不考虑前两个, 的值blah仍然只代表在评估variable()它的任何时间的评估值。它不会“指向”该功能并自动保持同步,就像您正在尝试做的那样。

如果您不是在寻找某种自动同步,那么没有什么能阻止您让Func<MyValueType>代理留在身边进行评估;这种方法没有什么特别好的或坏的,它不是一个闭包,除非委托(在你的情况下是一个 lambda 表达式)涉及使用局部变量。

于 2009-12-26T03:36:17.357 回答