Bug 12548 - C# Compiler Differences between Visual Studio and Xamarin Studio wrt Closures
Summary: C# Compiler Differences between Visual Studio and Xamarin Studio wrt Closures
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: 6.3.x
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Future Cycle (TBD)
Assignee: Marek Safar
URL:
Depends on:
Blocks:
 
Reported: 2013-06-04 17:26 UTC by Nathan Furtwangler
Modified: 2016-01-15 08:40 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Nathan Furtwangler 2013-06-04 17:26:51 UTC
I posted this in the Xamarin forums but I'll copy the info here:

I noticed a strange behavior in my app that turned out to be related to the way closures are created. What made it even stranger is that this particular bug appeared after a full test pass that would have easily caught it.

That's when I realized that we used to use Visual Studio to build/deploy using the Mac Server and then later switched to building/deploying with Xamarin Studio.

There is a difference in behavior in the way closure variables are captured when I build with Xamarin Studio compared to building with Visual Studio.

Here's an example:


for (int i = 0; i < letters.Count; i++)
{
    var letter = letters[i];
    var anim = new ColorFromToAnimation(ClockManager)
    {
        From = letter.Color,
        To = Color.LightGreen,
        Duration = 0.5f,
        Apply = (v) => { letter.Color = v; },
    };
    anim.Start();
}

The Apply property on the animation is a lambda that captures the local "letter" variable and will handle updating that letter's color property during the interpolation of the animation.

This works fine when compiled with Visual Studio, but when I compile with Xamarin studio it behaves as if only the last letter's animation matters. In other words, all of the animations are operating on the same letter instead of capturing the letter that was in their local scope when the lambda was created.

I can work around the problem by changing the code to this:


for (int i = 0; i < letters.Count; i++)
{
    var letter = letters[i];
    DoLetterAnim(letter);
}
 
private void DoLetterAnim(Letter letter)
{
    var anim = new ColorFromToAnimation(ClockManager)
    {
        From = letter.Color,
        To = Color.LightGreen,
        Duration = 0.5f,
        Apply = (v) => { letter.Color = v; },
    };
    anim.Start();
}

In other words avoiding the captured variable in the loop and passing it as an argument to a function.

Has anyone else seen this problem? Due to this difference in behavior we are forced to use Visual Studio to compile because our code base relies on these closures quite a bit. It could be that the Xamarin behavior is more "pure C#" behavior but for a cross-platform solution I would expect Xamarin Studio to try to emulate the Visual Studio C# compiler behavior as closely as possible.
Comment 1 Nathan Furtwangler 2013-06-04 19:23:05 UTC
Also note that in the cases I've found so far the bug occurs within IEnumerable functions that use yield return.

I have other animations with closures that seem to behave correctly and the only difference I can see when it repros vs not repro is that it repros only in functions that use yield return somewhere ahead of the loop (or during the loop).
Comment 2 Marek Safar 2013-06-05 04:41:14 UTC
Could you provide complete sample which fails from your snippets I tried to reproduce it but it works for me all the time.

My self contained sample.

using System;
using System.Collections.Generic;

class Letter
{
	public string Color;
}

class ColorFromToAnimation
{
	public Action<string> Apply;

	public void Start ()
	{
		Apply ("x");
	}
}

class Test
{
	public static void Main ()
	{
		foreach (var x in Foo ())
		{

		}
	}

	static IEnumerable<int> Foo ()
	{
		List<Letter> letters = new List<Letter> () {
			new Letter () { Color = "a" },
			new Letter () { Color = "b" },
			new Letter () { Color = "c" },
		};

		yield return 1;

		List<ColorFromToAnimation> anims = new List<ColorFromToAnimation> ();

		for (int i = 0; i < letters.Count; i++)
		{
			var letter = letters[i];
			var anim = new ColorFromToAnimation()
			{
				Apply = (v) => { letter.Color = v; },
			};

			anims.Add (anim);
		}

		foreach (var a in anims)
			a.Start ();

		for (int i = 0; i < letters.Count; i++)
		{
			Console.WriteLine (letters [i].Color);
		}

		yield return 2;
	}
}
Comment 3 Mikayla Hutchinson [MSFT] 2013-06-17 21:08:13 UTC
FYI this is a change in C# 5: http://stackoverflow.com/a/12112959/116899

If you're using VS2012 or XS on Windows you'll be using the MS C# 5 compiler. If you're using XS on Mac with the pre-async builds od XA/XI you'll be using Mono's C# 4 compiler.
Comment 4 Marek Safar 2013-06-18 03:04:01 UTC
Michael comment is not relevant. There has been no change in C# 5 for "for" variables scope.
Comment 6 PJ 2013-11-19 16:44:49 UTC
This bug was targeted for a past milestone, moving to the next non-hotfix active milestone.
Comment 7 GouriKumari 2016-01-11 21:40:06 UTC
Moving this bug to a future milestone.
Comment 8 Marek Safar 2016-01-15 08:40:38 UTC
No response