To take the solution a little further to allow deserialization to work as well...
public class A
{
private int _id = -1;
public int Id
{
get { return _id; }
set
{
if (_id < 0)
throw new InvalidOperationException("...");
if (value < 0)
throw new ArgumentException("...");
_id = value;
}
}
}
This will allow Id to be set exactly one time to a value greater than or equal to 0. Any attempts to set it after will result in InvalidOperationException. This means that XmlSerializer will be able to set Id during deserialization, but it will never be able to be changed after. Note that if the property is a reference type then you can just check for null.
This may not be the best solution if you have a lot of read-only properties to serialize/deserialize as it would require a lot of boilerplate code. However, I've found this to be acceptable for classes with 1-2 read-only properties.
Still a hack, but this is at least a little more robust.