Display Name
Display Name

Reputation: 15101

How to add items to a property of type List when instantiating an object with object initializer?

I want the Books not to be null so I made it an initialized readonly property as follows.

class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<string> Books { get; } = new List<string>();
}

Because of this constraint, I have to instantiate an object a first with object initializer and then populate its Books property via AddRange as follows.

class Project
{
    void Main()
    {
        Author a = new Author
        {
            Id = 100,
            Name = "Bill Gates"
        };
        a.Books.AddRange(new string[] { "Asp.net", "C++", "C#" });
    }
}

Question

Is there any trick to make the instantiation of a (as well as populating its Books) simpler just with object initializer?

Upvotes: 0

Views: 1278

Answers (3)

radarbob
radarbob

Reputation: 5101

Because of this constraint, I have to instantiate an object a first [emphasis mine]with object initializer and then populate its Books property via AddRange

But the client can set Books to null which is what I don't want.

These alarm bells are all answered by using a constructor. The constructor's purpose is creating valid objects. The constructor guarantees a valid object because it forces the user to provide the arguments required and then applies its internal state rules. Without a constructor the onus is on the user, and every user every time, to enforce that class' rules - and do it correctly.

It should strike the reader as ironic and incongruous that an object was instantiated in order to ensure partial state and then allowing external code to screw it up at will.

Edit

Re: the "am forced to" comment

You're focused on fixing only half the problem. For example, is it really ok for Name to be null or empty string? What's an Author without a name? Calling a method on a null Name string throws an exception. Either deal with everything it in the constructor or you'll be writing error trapping all over the place, including the client code. Client code should not be fixing Authors shortcomings.

But, if you must you can do this. And this "lazy initialization" does not eliminate the need for a decent constructor.

public List<string> Books { 
   get{ return value; }
   set { if (Books == null) {
            Books = new List<string>(); 
       } else { Books = value; }
   }
}

But that's buggy. An existing list can be wiped out.

public Author (string name, List<string> books = null) {
   if(String.isNullOrWhiteSpace(name)) 
      throw new ArgumentException("name is missing");

   // P.S. no public setter properties to undo construction
   Name = name;
   Books = books;  
}

// suggestion: rewrite to use params keyword to add 1 - many at once
public void AddBook( title) {
   if (String.isNullORWhitespace(title)) return;
   Books.Add(title);  // allows duplicates, fyi.

If anyone feels the urge to up vote, up-vote @HugoJose answer instead. He effectively illustrated this earlier. I'm just trying to explain the goodness of a robust constructor.

end Edit

Upvotes: 1

Hugo Jose
Hugo Jose

Reputation: 369

you can use private set

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<string> Books { get; private set; }

   public Author(params string[] books)
    {
        Books = new List<string>(books);
    }
}

or use just get (readonly option)

  public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<string> Books { get; }

    public Author(params string[] books)
    {
        Books = new List<string>(books);
    }
}

The private option I can change the instance of the list any time inside Author, but any other class outside Author just read it.

The read-only option I can't change any time. I initialize it in the constructor after that it will never change.

You can also use readonly collection, no body will add or remove books after the initialization.

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ReadOnlyCollection<string> Books { get; }

    public Author(params string[] books)
    {
        Books = new ReadOnlyCollection<string>(books);
    }
}

Upvotes: 1

Display Name
Display Name

Reputation: 15101

class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<string> Books { get; } = new List<string>();
    public Author(params string[] books)
    {
        Books.AddRange(books);
    }
}

class Project
{
    void Main()
    {
        Author a = new Author("Asp.net", "C++", "C#")
        {
            Id = 100,
            Name = "Bill Gates"
        };
    }
}

Upvotes: 1

Related Questions