Wednesday, January 28, 2009
« DSLs – fun, cool, but maybe a bad ... | Main | Life changing tip for XAML editing in Vi... »

I just ran into an odd “feature” of Silverlight data binding, that I assume must actually be a bug.

I have an object with a string property that has a public get, but private set:

public string Name
{
  get { return GetProperty(NameProperty); }
  private set { SetProperty(NameProperty, value); }
}

You would think that no code outside the business class could call the set block, because it is private. Certainly in .NET this would appear as a read-only property to any code outside the class.

But in Silverlight, data binding is perfectly capable of calling this code. Worse, the reflection PropertyInfo object for this property returns true for CanWrite, so this appears as a read-write property to any code.

I don’t think the problem is in the C# compiler, because if I write code that tries to set the property I get a compile-time error saying that’s not allowed.

Also, it isn’t a problem with reflection, because trying to set the value using the SetValue() method of a PropertyInfo object fails with the expected MethodAccessException (Silverlight reflection doesn’t allow you to manipulate private members). This, in particular, is weird, because if CanWrite returns true it should be safe to write to a property…

Update: I just checked .NET (being the suspicious sort) and it turns out that CanWrite returns true for a read-only set block in .NET too. And of course reflection won't set the property due to the scope issue, just like SL. But WPF data binding also doesn't call the private set block, where Silverlight somehow cheats and does call it - so at least the scope of the issue is narrowed a bit.

Which begs the question then: how is data binding bypassing the normal property scope protections so it can manipulate a private member? With the next question being how can I do it too? :) 

Seriously, this is the first time I’ve found where Microsoft has code in Silverlight that totally bypasses the otherwise strict rules, and it is a bit worrisome (and a pain in the @$$).

Anyway, the workaround at the moment is to use the old-fashioned approach and create a separate mutator method:

public string Name
{
  get { return GetProperty(NameProperty); }
}

private void SetName(string value)
{
  SetProperty(NameProperty, value);
}

This works, because when there’s no set block at all the property is actually read-only to everyone, inside and outside the class. Kind of inconvenient for the other code inside the business class that wants to set the property, because that code must use this non-standard mutator method – but at least it works…

Saturday, January 31, 2009 12:20:55 AM (Central Standard Time, UTC-06:00)
I think one can use OneWay in the binding expression to avoid letting user access setter.
"{Binding Value, Mode=OneWay}"
Does it solve your problem?

Michael
michael
Saturday, January 31, 2009 1:08:29 AM (Central Standard Time, UTC-06:00)
Yes, that does work. But my point is that SL data binding is blatantly breaking a scoping rule by accessing a private member. In SL this isn't allowed, even by reflection, so it really makes me wonder how they are bypassing one of the core elements of the runtime that protects your and my code from being exploited.

(and it is just a pain that it doesn't follow the rules!)
Saturday, January 31, 2009 1:57:37 PM (Central Standard Time, UTC-06:00)
I found this discussion that may shed some light:

http://social.msdn.microsoft.com/Forums/en-US/clr/thread/d8850967-5081-4d2b-b130-a5ebe2f0d056/
Josh Painter
Saturday, January 31, 2009 3:15:36 PM (Central Standard Time, UTC-06:00)
Thanks Josh, that directly explains the reflection part of the issue.

It also means you could use private scoping on a setter to easily create "bypassable" read-only property implementations. That's an interesting idea all by itself.


The other (bigger imo) part of my post is clearly a Silverlight implementation issue. WPF honors the scope of the setter, while Silverlight does not. This means you get different behavior from the same XAML on one platform from the other. I think that's a Silverlight bug, pure and simple.
Comments are closed.