Olá, José. Tudo bom?
Considere o exemplo de código a seguir:
Conta c = new Conta();
Console.WriteLine(c.Numero);
Quando executarmos este código, sabemos que a saída será 0. Afinal, não mudamos o valor deste atributo em nosso objeto.
Aliás, aproveitando para ajudar com a confusão de instância e referência, poderia dizer que não mudamos o atributo em nossa instância de Conta, criada com new Conta() e referenciada por c, quando fizemos a atribuição c = new Conta();
Sabemos que a saída será 0, porque, além de não alterarmos o seu valor, sabemos que Numero é do tipo int e seu valor padrão, default, é 0.
Contudo, criamos a classe Cliente e, diferente dos tipos int, double, bool, etc. que são tipos de valor o tipo Cliente é um tipo de referência.
Então, ao executar o código a seguir:
Conta c = new Conta();
c.Titular.Nome = "Pedro";
Seu programa buscará o campo Nome do objeto (podemos falar tbm "da instância") referenciada no campo Titular.
Mas, peraí, a referência em Titular nunca foi inicializada! Então ela possui o valor padrão de todo tipo de referência: null. Por isso, para não enfrentarmos este erro, é necessário atribuir a referência para algum objeto no campo Titular, seja com o código abaixo:
c.Titular = new Cliente();
Ou, algo assim:
c.Titular = pedro;
Onde pedro é uma variável do tipo Cliente
Outro benefício disso é destacado com o código abaixo:
Cliente pedro = new Cliente();
pedro.Nome = "Pedro";
ContaCorrente primeiraConta = new ContaCorrente();
primeiraConta.Titular = pedro;
ContaCorrente segundaConta = new ContaCorrente();
segundaConta.Titular = pedro;
pedro.Nome = "Pedro da Silva";
Console.WriteLine(primeiraConta.Titular.Nome); //Imprime "Pedro da Silva"
Console.WriteLine(segundaConta.Titular.Nome); //Imprime "Pedro da Silva"
Ficou mais claro? O que você acha?
Abs e bons estudos.