1

I'm relatively new to C#, so if the answer to this question is obvious I apologise.

There's a portion of a program I am writing that stores an array of structs, and one of the elements of the struct is a curried function.

The following is the portion of code causing the problem (minimised as far as I am able)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CurryingProblem
{
    class Program
    {
        public struct Value
        {
            public Func<decimal> Output;
        }
        public static decimal AddOne(decimal value)
        {
            return value + 1;
        }

        static void Main(string[] args)
        {
            Dictionary<string, Decimal> ThingsToAdd = new Dictionary<string, decimal>();
            Dictionary<string, Value> ThingsToPrint = new Dictionary<string, Value>();

            ThingsToAdd.Add("One", 1.0m);
            ThingsToAdd.Add("Two", 2.0m);
            foreach (KeyValuePair<string, Decimal> thing in ThingsToAdd)
            {
                Value value = new Value();
                value.Output = () => AddOne(thing.Value);
                ThingsToPrint.Add(thing.Key, value);
            }
            Console.WriteLine(ThingsToPrint["One"].Output());
            Console.WriteLine(ThingsToPrint["Two"].Output());

            Console.ReadKey();
        }
    }
}

The expected output of this program is

2.0
3.0

But the actual output is

3.0
3.0

Any direction as to where I've gone wrong would be wonderful.

JamesL
  • 157
  • 8

1 Answers1

3

Your question is similar to access to modified closure. This article by Eric Lippert explains it very well. You need to pass variable of local scope instead of passing loop thing.Value. Create a new variable and assign thing.Value to it so that local copy is passed.

    static void Main(string[] args)
    {
        Dictionary<string, Decimal> ThingsToAdd = new Dictionary<string, decimal>();
        Dictionary<string, Value> ThingsToPrint = new Dictionary<string, Value>();

        ThingsToAdd.Add("One", 1.0m);
        ThingsToAdd.Add("Two", 2.0m);
        foreach (KeyValuePair<string, Decimal> thing in ThingsToAdd)
        {
            Value value = new Value();
            Decimal d = thing.Value;
            value.Output = () => AddOne(d);
            ThingsToPrint.Add(thing.Key, value);
        }
        Console.WriteLine(ThingsToPrint["One"].Output());
        Console.WriteLine(ThingsToPrint["Two"].Output());

        Console.ReadKey();
    }
Community
  • 1
  • 1
Adil
  • 146,340
  • 25
  • 209
  • 204
  • This definitely works, thanks. I think this boils down to me not understanding the subtleties of scope. – JamesL Nov 23 '12 at 05:31
  • I found this article very good for understanding http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx – Adil Nov 23 '12 at 05:41