Search code examples
c#thisobject-initializerscollection-initializer

Set inner object's member as outer object using the object initialization syntax in C#


Context

  • I have a List<T> of type Question.
  • Class Question, in turn, contains a List<Answer>.
  • Class Answer has a member called public Question Question { get; set; } which stores the question of which the answer is for.

I'm using the collection initialization syntax to add a Question item to the list and object initialization to create a new Question. While doing so, I'm also creating new Answer objects using the object initialization syntax(nested).


Problem

How do I set the Question member of the inner Answer class to refer to the enclosing Question object? I know the point at which an Answer is created, the Question is not even fully initialized. But is there any way to obtain the outer Question instance so that I can set it to the inner Answer.


Code

private List<Question> questions = new()
{
    new Question 
    { 
            Id = 1, 
            Text = "Test Question 1", 
            Difficulty = QuestionDifficulty.Easy, 
            Answers = 
            {
                new Answer { Id = 1, Question = [?] },
                new Answer { Id = 2, Question = [?] }   // What should replace [?] here?
            }
    } 
};

Solution

  • You can't do it in the collection/object initialiser, but I'd argue that you shouldn't do it there in the first place. It's quite error-prone to handwrite what the corresponding question for each answer should be. You might forget to do that sometimes too.

    I suggest that you add a custom setter for the Answers property in Question that also sets the answer's Question property:

    private List<Answer> answers;
    public List<Answer> Answers {
        get => answers;
        set {
            // If you feel like it, you can also set the old answers' questions to null
    
            answers = value;
            foreach (var answer in answers) {
                answer.Question = this;
            }
        }
    }
    

    Then in the object initialiser, initialise the answers' list, rather than just adding to it:

    private List<Question> questions = new()
    {
        new Question 
        { 
                Id = 1, 
                Text = "Test Question 1", 
                Difficulty = QuestionDifficulty.Easy, 
                Answers = new List<Answer> // <--- here! 
                {
                    new Answer { Id = 1 },
                    new Answer { Id = 2 }
                }
        } 
    };
    

    This compiles to something like:

    var question = new Question();
    ...
    var list = new List<Answer>();
    var answer1 = new Answer();
    answer1.Id = 1;
    var answer2 = new Answer();
    answer2.Id = 2;
    list.Add(answer1);
    list.Add(answer2);
    question.Answers = list;
    

    As you can see, the setter is called, and the answers' Question property will be set.