Is There a Way of Setting a Property Once Only in C#

Is there a way of setting a property once only in C#

There is direct support for this in the TPL in .NET 4.0;

(edit: the above sentence was written in anticipation of System.Threading.WriteOnce<T> which existed in the "preview" bits available at the time, but this seems to have evaporated before the TPL hit RTM/GA)

until then just do the check yourself... it isn't many lines, from what I recall...

something like:

public sealed class WriteOnce<T>
{
private T value;
private bool hasValue;
public override string ToString()
{
return hasValue ? Convert.ToString(value) : "";
}
public T Value
{
get
{
if (!hasValue) throw new InvalidOperationException("Value not set");
return value;
}
set
{
if (hasValue) throw new InvalidOperationException("Value already set");
this.value = value;
this.hasValue = true;
}
}
public T ValueOrDefault { get { return value; } }

public static implicit operator T(WriteOnce<T> value) { return value.Value; }
}

Then use, for example:

readonly WriteOnce<string> name = new WriteOnce<string>();
public WriteOnce<string> Name { get { return name; } }

Is there a way of setting a property only once as a built-in mechanism?

You are confusing quite a few things here.

  1. A readonly field means it can only be assigned inside a constructor or via a field initializer. Now WriteOnce is a reference type, so assigning only means that the value stored in name is a reference to the newly created WriteOnce<string> object.

    Nothing stops you from doing whenever you want name.Value = "Hello"; because you are not changing the value of name. name = whatever outside a constructor or a field initializer on the other hand is disallowed becuase you are changing the value of the variable to a new reference, but nothing else.

  2. Name is a readonly property, which has as the backing field name.

    A read only propery doesn't let you do Name = new WriteOnce<string>(), but Name.Value = "Hello" is perfectly fine.

    Anyhow, nowdays, you'd simply use a readonly autoproperty and let the compiler generate all the plumbing code (backing field):

    public WriteOnce<string> Name { get }

How to make a variable able to be set only once in C#?

You can use a private nullable variable like this, in this way, _myVar just once gets value

   private bool? _myVar = null;
public bool? MyVar
{
set { if (_myVar == null) _myVar = value; }
get { return _myVar; }
}

How to make a property set once and cannot be changed?

Can't you simple create a guard flag?

bool wasSetMessageId = false;
public Guid MesageUniqueId
{
get { return messageUId; }
set
{
if (!wasSetMessageId)
{
messageUId = value;
wasSetMessageId = true;
}
else
{
throw new InvalidOperationException("Message id can be assigned only once");
}
}
}

How to implement properties which can be set only one time

As also commented by @Fildor, the Builder pattern may be appropriate here. You can set the properties on your builder class as often as you want, there is no harm in allowing multiple assignment there. But once you use that to build your class, then it's locked, then no more modifications are allowed.

class MyClass {
internal MyClass(MyClassBuilder builder) {
MyProp1 = builder.MyProp1;
MyProp2 = builder.MyProp2;
MyProp3 = builder.MyProp3;
}
public string MyProp1 { get; }
public string MyProp2 { get; }
public string MyProp3 { get; }
}

class MyClassBuilder {
public string MyProp1 { get; set; }
public string MyProp2 { get; set; }
public string MyProp3 { get; set; }
public MyClass Build() => new MyClass(this);
}

Usage: new MyClassBuilder { MyProp2 = "abc" }.Build(). Different APIs are also possible based on the same general idea.

Best way to only allow the setting of a property while deserializing a JsonProperty?

If you can use init only properties (since C#9.0) (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/init):

[JsonProperty(PropertyName = "id")]
public string Id { get; init; }

If not...

private string _id;
[JsonProperty(PropertyName = "id")]
public string Id
{
get { return _id; }
set { _id ??= value; }
}

Unrelated but helpful link on setting default property values if they are not found in the json: Default value for missing properties with JSON.net

C# 6 auto-properties - read once or every time?

What you show:

private string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");

Is an "Auto-Property Initializer", keyword being "initializer", from MSDN Blogs: C# : The New and Improved C# 6.0:

The auto-property initializer allows assignment of properties directly within their declaration. For read-only properties, it takes care of all the ceremony required to ensure the property is immutable.

Initializers run once per instance (or once per type for static members). See C# Language Specification, 10.4.5 Variable initializers:

For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

So that code compiles to something like this:

public class ContainingClass
{
private readonly string _databaseId;
public string DatabaseId { get { return _databaseId; } }

public ContainingClass()
{
_databaseId = CloudConfigurationManager.GetSetting("database");
}
}

For static variables, this kind of looks the same:

private static string DatabaseId { get; } = CloudConfigurationManager.GetSetting("database");

Compiles to, more or less:

public class ContainingClass
{
private static readonly string _databaseId;
public static string DatabaseId { get { return _databaseId; } }

static ContainingClass()
{
_databaseId = CloudConfigurationManager.GetSetting("database");
}
}

Though not entirely, as when the type doesn't have a static constructor, "static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class".

Is it possible to have fields that are assignable only once?

That would not be a readonly field then. Your only options for initializing real readonly fields are field initializer and constructor.

You could however implement a kind of readonly functionality using properties. Make your field as properties. Implement a "freeze instance" method that flipped a flag stating that no more updates to the readonly parts are allowed. Have your setters check this flag.

Keep in mind that you're giving up a compile time check for a runtime check. The compiler will tell you if you try to assign a value to a readonly field from anywhere but the declaration/constructor. With the code below you'll get an exception (or you could ignore the update - neither of which are optimal IMO).

EDIT: to avoid repeating the check you can encapsulate the readonly feature in a class.

Revised implementation could look something like this:

class ReadOnlyField<T> {
public T Value {
get { return _Value; }
set {
if (Frozen) throw new InvalidOperationException();
_Value = value;
}
}
private T _Value;

private bool Frozen;

public void Freeze() {
Frozen = true;
}
}

class Foo {
public readonly ReadOnlyField<int> FakeReadOnly = new ReadOnlyField<int>();

// forward to allow freeze of multiple fields
public void Freeze() {
FakeReadOnly.Freeze();
}
}

Then your code can do something like

        var f = new Foo();

f.FakeReadOnly.Value = 42;

f.Freeze();

f.FakeReadOnly.Value = 1337;

The last statement will throw an exception.

Set properties of a class only through constructor

Make the properties have readonly backing fields:

public class Thing
{
private readonly string _value;

public Thing(string value)
{
_value = value;
}

public string Value { get { return _value; } }
}


Related Topics



Leave a reply



Submit