Here’s a bit of code i just got through debugging…
public Point Point { get; private set; } public void Offset(Point origin) { Point.Offset(-origin.X, -origin.Y); }
Can you tell what’s wrong here? Let’s just say that the Offset won’t take.
structs are value types, which means anytime you pass one around you get a new copy. So far so good. And that means when you expose a struct value via a property, the accessing party always is looking at a copy. Again, fine, after all when you do a set you change the stored value and if you need to manipulate it, you just manipulate the actual struct.
Enter Automatic properties and you might forget about this last detail and not realize that you never get access to the underlying value, even from within the class. I.e. when I call Point.Offset, i’m calling it on the copy that was passed to me and the resulting value is immediately thrown away. So i just went back to using the property to facade a private Point, which i can now manipulate inside of Offset. Duh.
This is closely related to my last post on deferred execution gotchas and its basically more “if you inline delegated code, you may easily overlook scope side-effects”. This time it’s about dealing with foreach and using the local each item for deferred execution.
public void SpawnActions() { foreach (ActionContext context in contexts) { int id = context.Id; Action<int> callback = (workerNumber) => { Console.WriteLine("{0} Id: {1}/{2}", workerNumber, id, context.Id); }; ThreadPool.QueueUserWorkItem(new WaitCallback(FutureExecute), callback); } } public void FutureExecute(object state) { int id = worker++; Action<int> callback = state as Action<int>; Thread.Sleep(500); callback(id); }
The output looks like this:
0 Id: 0/9 1 Id: 1/9 2 Id: 2/9 3 Id: 3/9 4 Id: 4/9 5 Id: 5/9 6 Id: 6/9 7 Id: 7/9 8 Id: 8/9 9 Id: 9/9
So while the foreach scope variable context is kept alive for the deferred execution, it turns out that foreach re-uses the variable on each pass through the loop and therefore when the Action
foreach (ActionContext context in contexts) { int id = context.Id; // locally scoped variable ActionContext c2 = context; Action<int> callback = (workerNumber) => { Console.WriteLine("{0} Id: {1}/{2}", workerNumber, id, c2.Id); }; ThreadPool.QueueUserWorkItem(new WaitCallback(FutureExecute), callback); }
And now our results are a bit more what we expected:
0 Id: 0/0 1 Id: 1/1 2 Id: 2/2 3 Id: 3/3 4 Id: 4/4 5 Id: 5/5 6 Id: 6/6 7 Id: 7/7 8 Id: 8/8 9 Id: 9/9
Here’s something I tracked down with no help from error messages:
When you copy a user control in Silverlight 1.1 from one project to another the Xaml that the control loads will have it’s Build Action set to SilverlightPage. When you then run your project and try to create an instance of that control you’ll get the ever so informative AG_E_INVALID_ARGUMENT. All you need to do to fix it, is set the Build Action to Embedded Resource again. Tada!
I love declarative definition of UI with behavior wired to static code. But man, at its current state, the debugging support for it just isn’t there. I mean it’s bad enough that having strings in the declarative side to link to actions that won’t be updated by normal refactoring, nor will they show up as references, but at this state Xaml brings the worst part of scripting languages to compile-time checked coding:
Vague runtime errors without a stacktrace
Bah.
My setup until yesterday was Bootcamp partion that was running as a VM using VMWare Fusion. The Bootcamp partition was set up as FAT32, because I NTFS came up as readonly when mounted under Mac OS.
Then I started up another VM (Fedora Core 7, although i have since noticed it’s not what OS you run, but just a second VM). Maybe it’s not enough memory, maybe it’s a VM running from bootcamp plus a VM running from a disk image, but while it had worked previously, this time, it locked up my Mac hard. I finally had to hard boot the Mac. When I got back into th VM, I noticed things were broken. Now, I’ve had to hard boot XP many times and I’ve never seen this. An indeterminate number of files were corrupted. I noticed one XML file that halfway through turned into binary garbage, so I assume that the other systems failing were suffering from similar corruption. Basically it was hosed, because there was no way to determine what had been corrupted. Time to re-install.
Looking at my post from last time, it was clearly written with the frazzled recollection of a day of trying to make things work, since i once again ran into problems. This time I’m making sure I write the resolution down.
Let’s hope this doesn’t turn into a bi-monthly process