views:

400

answers:

7

The following program will fail because Contracts is not instantiated.

Of course I can instantiate it in my constructor but if I have dozens of properties and/or multiple constructors I have to keep track of which are instantiated, etc.

And of course I could create large blocks for these properties with full gets and sets and private field variables, etc. But this gets messy as well.

Isn't there a way to automatically instantiate these collections with the nice C# property syntax?

using System;
using System.Collections.Generic;

namespace TestProperty232
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            customer.FirstName = "Jim";
            customer.LastName = "Smith";

            Contract contract = new Contract();
            contract.Title = "First Contract";

            customer.Contracts.Add(contract);

            Console.ReadLine();
        }
    }

    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public List<Contract> Contracts { get; set; }

        public Customer()
        {
            //Contracts = new List<Contract>();
        }
    }

    public class Contract
    {
        public string Title { get; set; }
    }
}
+5  A: 

No, there is no sugar. Instantiate them in your parameterless constructor and redirect all your other constructors there, so that it will always execute.

class MyClass
{
    //Many properties

    public MyClass()
    {
        //Initialize here
    }

    public MyClass(string str) : this()
    {
        //do other stuff here
    }
}
Maximilian Mayerl
A: 

The short answer is no.

klausbyskov
the long answer is Nooooooooooooooooooooooooooooooo
Pondidum
Long answer is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong ;)
Maximilian Mayerl
+5  A: 

There is no such syntactic sugar, but I'd like to point out a few things:

  • Collection properties should be read-only in any case.
  • If you have a lot of such properties in a single type, it's a strong symptom that you are violating the Single Responsibility Principle
  • If you have multiple constructors, you should always take great care that there's only one constructor that does the real work, and all other constructors delegate to that constructor. Alternatively you can delegate all constructor work to a private Initialize method.
Mark Seemann
Which version of C# are you using?
Joe
Why the downvote?
Mark Seemann
Re collection properties - except for the annoying niggle of `XmlSerializer`, but agree in theory.
Marc Gravell
In the current version of C#, if I understand the question, this answer is wrong. There is syntactic sugar (as per my answer).
Joe
@Joe: If I understand the OP correctly, he's not asking about Type Initializers, but rather if there's some sort of shorthand like Auto-Properties that would allow you to declaratively specify that a List property exists. This should then ensure that the List instance is available (i.e. not null) when the object is created. At least in C# 3 there is no such thing, and I don't believe there is in C# 4 either.
Mark Seemann
In which case, apologies for the downvote. I read the question different first time round, drawing inferences from the intent of the code.
Joe
@Joe: Thanks for taking the time to explain :)
Mark Seemann
@Joe and @Mark Enjoyed your answers and discussion. I wonder if there are certain scenarios where it is "best practice" to put a test for 'Null and an initialization call in a 'Get on a Property : my preference right now would be that all such initialization be done in the instantiation of the enclosing class, but I do not, yet, feel on "solid technical ground" for this preference.
BillW
@BillW: It sounds like you just described Lazy Initialization. There are certainly scenarios where this would be appropriate, but listing them all is not possible in this comments field. It's never my default approach though - I'd rather check the invariants as soon as possible... However, your question sounds like a good candidate for a 'proper' Stack Overflow question :)
Mark Seemann
+1  A: 

Long answer: how's this?

using System.Collections.Generic;

namespace proto
{
    public class Customer
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

    public class Contract
    {
        public List<Customer> Customers { get; set; }

        public string Title { get; set; }
    }

    public class ContractDemo
    {
        public Contract CreateDemoContract()
        {
            Contract newContract = new Contract
            {
                Title = "Contract Title", 
                Customers = new List<Customer>
                {
                    new Customer
                    {
                        FirstName = "First Name",
                        LastName = "Last Name"
                    },
                    new Customer
                    {
                        FirstName = "Other",
                        LastName = "Customer"
                    }
                }
            };

            return newContract;
        }

    }
}
Joe
A: 

Not sure what you are actually looking for, but you could tidy it up a bit like this...

using System;
using System.Collections.Generic;

namespace TestProperty232
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer() { 
                FirstName = "Jim",
                LastName = "Smith"
            };

            Contract contract = new Contract() { 
                Title = "First Contract"
            };

            customer.Contracts = new List<Contract>() { contract };

            Console.ReadLine();
        }
    }

    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public List<Contract> Contracts { get; set; }

        public Customer()
        {
            //Contracts = new List<Contract>();
        }
    }

    public class Contract
    {
        public string Title { get; set; }
    }
}
Dan Hedges
A: 

You could make Contracts not be an autoproperty:

private List<Contract> _contracts;
public List<Contract> Contracts
{
    get
    {
        if (_contracts == null)
        {
            _contracts = new List<Contract>();
        }
        return _contracts;
    }
    set
    {
        if (!_contracts.Equals(value))
        {
            _contracts = value;
        }
    }
}

That will allow you not to have to explicitly instantiate Contracts.

jomtois
You can have some syntactic sugar to make this shorter: `get { return _contracts ?? (_contracts = new List<Contract>()); }`
Joel Mueller
@Joel Thanks ! I was just trying this technique (using the null coalescing operator) fifteen minutes ago, and going nuts as to why I was getting an error message that said : "The left-hand side of an assignment must be a variable, property or indexer" : I was failing to enclose the initialization of the private backing-field (on the right side of the ?? operator) _contracts in parentheses :)
BillW
+1  A: 

Sugary or lemony : you decide : (VS 2010 beta 2, FrameWork 4)

    Customer customer = new Customer
    {
        FirstName = "Jim", 
        LastName = "Smith", 
        Contracts = new List<Contract> { new Contract { Title ="First Contract" } }
    };

Works fine with your existing class definitions, but feels awkward to read ?

best,

BillW