user1899020
user1899020

Reputation: 13575

Immutable types with object initializer syntax

For example, I have an immutable type

class Contact
{
    // Read-only properties. 
    public string Name { get; }
    public string Address { get; }
}

And I hope I can use object initializer syntax to create a Contact

Contact a = new Contact { Name = "John", Address = "23 Tennis RD" };

But I cannot. Any possible way to make use of the powerful object initializer syntax in this case?

Upvotes: 7

Views: 1387

Answers (4)

Ed Carney
Ed Carney

Reputation: 41

This is dated now, but with the release of C# 9 you can use init to achieve the desired functionality.

So your example would become:

class Contract
{
    // Read-only properties. 
    public string Name { get; init; }
    public string Address { get; init; }
}

And then you could initialize with:

// success!
Contract a = new Contract { Name = "John", Address = "23 Tennis RD" };

But you would still be unable to modify the parameters after setting them (so effectively they are still readonly).

// error!
a.Name = "Uncle Bob";

Under the hood, when you use object initializer syntax prior to C# 9 the compiler would call the default constructor first, and then set the property values you've specified. Obviously if those properties are readonly (i.e. only a get method), it can't set them. The init only setter allows setting the value only on initialization, either via a constructor method or object initializer syntax.

More info is available here: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#init-only-setters

Upvotes: 4

Joe White
Joe White

Reputation: 97778

The closest thing would be a constructor with optional parameters:

class Contact
{
    public string Name { get; }
    public string Address { get; }
    public Contact(string name = null, string address = null) {
        Name = name;
        Address = address;
    }
}

Then you can call it with parameter names:

new Contact(
    name: "John",
    address: "23 Tennis RD"
)

The syntax is slightly different from an object initializer, but it's just as readable; and IMO, the difference is a good thing, because constructor parameters tend to suggest immutable properties. And you can specify the parameters in any order, or leave some out, so it's just as powerful as object initializer syntax.

This does require some extra code (defining the constructor, assigning all the properties), so it's more work than object initializer syntax. But not too terrible, and the value of immutable objects is worth it.

(For what it's worth, C# 7 may get immutable "record types" that have much simpler syntax. These may or may not make it into the final release, but they sound pretty cool.)

Upvotes: 5

Noel Widmer
Noel Widmer

Reputation: 4572

Nope, you cannot use it with readonly properties.
Here are the different property and field types in comparism.

public class sometype {
    public int readonlyProp{
        get;
    }
    public int normalProp {
        get;
        set;
    }

    public const int constField = 3;
    public readonly int readonlyField = 3;
    public int normalField = 3;

    public void test() {
        sometype test = new sometype() { readonlyProp = 3}; //      Doesn't work -> Property or indexer is readonly
        sometype test1 = new sometype() { normalProp = 3 }; //      ok
        sometype test2 = new sometype() { constField = 3 }; //      Doesn't work -> Static field or property
        sometype test3 = new sometype() { readonlyField = 3 }; //   Doesn't work -> readonly field
        sometype test4 = new sometype() { normalField = 3 }; //     ok
    }
}

It is important to understand that const fields are considered static and thus are not instance members. And since the object initializer is used for instance members this doesn't work.

Upvotes: 1

Bruno Garcia
Bruno Garcia

Reputation: 6408

Object initializer will first construct the object, then set property values. It needs setters.

It's short hand for:

Contact a = new Contact();
a.Name = "John"; 
a.Address = "23 Tennis RD";

A readonly field can't have it's values set once the object has been constructed. To have that class immutable, you'll need to create a constructor to take those values:

class Contact // Immutable class
{
    // Read-only properties. 
    public string Name { get; }
    public string Address { get; }
    public Contact(string name, string address)
    {
        this.Name = name;
        this.Address = address;
    }
}

Upvotes: 0

Related Questions