1

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?
            }
    } 
};
SomeBody
  • 7,515
  • 2
  • 17
  • 33
Amal K
  • 4,359
  • 2
  • 22
  • 44
  • 1
    Why do you need to do this? It creates a redundant data structure that can lead to bugs in the future. It's better to compute the parent than to store it. – Enigmativity Apr 08 '21 at 06:44

2 Answers2

2

It is not possible. But you can set the question to all answers after you created them. Do not assign a value to the question property during your use of the collection initialization. In your constructor, you do the following:

foreach(var question in questions)
{
    foreach(var answer in question.Answers)
    {
         answer.Question = question;
    }
}
SomeBody
  • 7,515
  • 2
  • 17
  • 33
2

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.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • 1
    Good approach! Maybe it is even possible to use a custom implementation of `IList` to set the `Question` property when a new question is added to the list. – SomeBody Apr 08 '21 at 06:28
  • 2
    @SomeBody Yeah, if you do that, you don't need `new List` anymore (assuming `Answers` is initialised in the constructor), but then IMO making a custom list implementation just for this is a bit overkill :-) – Sweeper Apr 08 '21 at 06:32
  • This is exactly what I needed! Thanks a lot : ) – Amal K Apr 08 '21 at 06:43
  • @SomeBody That sounds good too. I actually have subclasses for both `Question` and `Answer` so I think a custom `class AnswerList : IList` can be reasonable enough to create too. But for now, I'll go with @Sweeper's approach. Thanks :) – Amal K Apr 08 '21 at 06:48