DataContractSerializer может сериализовать поля только для чтения, если вы инициализируете его следующим образом
var serializer = new DataContractSerializer(
typeof(ToSerialize),
new DataContractSerializerSettings()
{
SerializeReadOnlyTypes = true
});
Но это будет работать только в одну сторону, если вы хотите десериализовать класс со свойством, которое имеет только геттер. Таким образом, вы можете сериализовать, но не десериализовать что-то вроде этого:
[DataContract]
public struct ToSerialize
{
public ToSerialize(string a)
{
PropertyToSerialize = "a";
}
[DataMember]
public string PropertyToSerialize { get; }
}
Таким образом, вы либо добавляете закрытый набор в свое свойство, либо добавляете вспомогательное поле и помечаете его атрибутом [DataMember].
[DataContract]
public struct ToSerialize
{
public ToSerialize(string a)
{
backingField = "a";
}
public string PropertyToSerialize => backingField;
[DataMember]
string backingField;
}
Используя частные сеттеры, вы затрудняете изменение состояния объекта (вы, вероятно, можете сделать это с помощью отражения). Поэтому, если нет методов, изменяющих поля в вашей структуре, и все свойства имеют частные установщики, ваша структура технически неизменна. Я бы выбрал частного сеттера в вашем случае, он более читабелен и требует меньше усилий, чем игра с отражением.