【问题标题】:Saving instructions for later evaluation, possible?保存说明以供以后评估,可能吗?
【发布时间】:2018-03-05 16:07:56
【问题描述】:

我想保存关于以后如何确定值的“说明”,而不是保存当前时间的实际值。 这甚至可能吗?

一个简单的 C# 示例:

int[] myArray = new int[2];
Dictionary<string, int> myDictionary = new Dictionary<string, int>();
//dictionary type can be changed if required

myArray[0] = 1;
myArray[1] = 2;

myDictionary.Add("total", (myArray[0] + myArray[1]) ); // Don't evaluate the value now

myArray[0] = 3;
myArray[1] = 4;

Console.WriteLine("total 2 = " + myDictionary["total"]); // Evaluate the value now
//Desired output: 7 (3+4), actual output = 3 (1+2)

【问题讨论】:

  • 好奇怪的字典,除了total还有别的吗?
  • * Tim,这只是一个例子,在这本词典中只有“total”,但在真正的词典中会有更多的条目。 * 费尔南多,我去看看。
  • * vc 74,我不知道怎么读,它看起来像一个返回摘要的匿名函数?
  • 一组按需计算结果的指令在 C# 中称为“方法”。将您的代码放入方法中。当您希望它被激活时,调用方法

标签: c# lazy-evaluation evaluation delayed-execution


【解决方案1】:

你可以使用(expression bodied read-only) properties:

public int[] MyArray { get; set; }

public string CurrentResult => $"total: {MyArray.Sum()} ({string.Join("+", MyArray)})";

如果需要局部变量,可以使用local functions

string GetCurrentResult() => $"total: {MyArray.Sum()} ({string.Join("+", MyArray)})";

MyArray[0] = 1;
MyArray[1] = 2;
Console.WriteLine(GetCurrentResult()); // total: 3 (1+2)
MyArray[0] = 3;
MyArray[1] = 4;
Console.WriteLine(GetCurrentResult()); // total: 7 (3+4)

如果您不使用 C#7,您可以使用 Func&lt;string&gt; 委托:

Func<string> GetCurrentResult = () => $"total: {MyArray.Sum()} ({string.Join("+", MyArray)})";

【讨论】:

    【解决方案2】:

    您正在寻找Lazy&lt;T&gt;。它需要一个Func&lt;T&gt;,直到通过Value 属性访问它才会评估。一旦评估,结果将被存储以供进一步访问。所以你的代码可能看起来像:

    int[] myArray = new int[2];
    
    var total = new Lazy<int>(() => myArray.Sum());
    myArray[0] = 1;
    myArray[1] = 2;
    myArray[0] = 3;
    myArray[1] = 4;
    
    Console.WriteLine("total = " + total);
    Console.WriteLine("total = " + total.Value);
    Console.WriteLine("total = " + total);
    

    这段代码的输出是:

    total = Value is not created.
    total = 7
    total = 7
    

    请注意,如果不调用total.Value,则结果不是int,而是一条消息,告诉我们表达式尚未计算。一旦调用了total.Value,随后对total 的访问就会产生该值(由于Console.WriteLine() 中隐含的ToString() 调用)。

    使用Lazy&lt;T&gt; 的好处是该值是持久的,而不是每次访问时都重新计算。这非常适合类中的属性/字段,这些属性/字段可能不会在每次使用类时都被访问但需要很长时间才能生成值。

    编辑:根据 Op 的反馈,Lazy&lt;T&gt; 并不是他们想要的。

    如果您总是希望每次访问表达式时都对其求值,您需要一个方法或Func&lt;T&gt;。所以想象一下你有一个这样的类:

    public class MyClass
    {
        public int[] Vals {get;set;}
    }
    

    如果您想定义一种自定义方式来获取(例如)Vals 的总和,您有几个简单的选择。

    类方法

    public class MyClass
    {
        public int[] Vals {get;set;}
        public int SumOfVals()
        {
            return Vals.Sum();
        }
    }
    

    如果您选择类方法,您可以(可以想象)使类通用 (MyClass&lt;T&gt;) 并使用虚拟/抽象方法来实现具体的 SumOfVals 方法。

    类中实现的Func&lt;T&gt;

    public class MyClass
    {
        public int[] Vals {get;set;}
        public Func<int[], int> SumOfVals { get;set; }
    }
    

    现在您可以在每次实例化类时将SumOfVals 设置为某个自定义函数。如果您没有将其设置为任何内容,那么如果您尝试对其进行任何操作,您将收到 NullReferenceException。

    一个Func&lt;T&gt; 内联实现

    var vals = new int[2];
    var sumVals = new Func<int[], int>((arr) => arr.Sum());
    Console.WriteLine(sumVals(vals));
    

    这可能是最灵活的,但这可能会导致一些意大利面条代码。我建议只在调用MyClass 的类中创建一个方法,或者在MyClass 中创建一个方法来处理这个逻辑。

    【讨论】:

    • 这个解决方案的问题是它只评估了第一次,如果我更改数组然后再次运行 total.Value,我仍然得到旧值。它基本上第一次按要求工作,然后它和我的第一个示例代码有同样的问题。
    • 如果 Lazy 有某种形式的重新评估功能,那将是一个完美的解决方案,但我在文档中找不到。
    猜你喜欢
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    • 2011-08-08
    • 2010-11-27
    • 2018-03-05
    相关资源
    最近更新 更多