You're right, there were missing tokens; sorry about that.
Here is the equivalent C# 6 version of the code (i.e. something very similar to the pattern that I currently use):
IEnumerable GetStudents(SqlCommand cmd)
{
var rdr = cmd.ExecuteReader();
var memo = new Dictionary();
while(rdr.Read())
{
Student.Completion completion; //this declaration will be unnecessary in C# 7
if(!memo.TryGetValue(rdr.GetInt32("student_id"), out completion))
memo.Add(new Student(rdr, out completion).ID, completion);
completion.AddCourse(rdr); //completion is *guaranteed* to be non-null
}
return from kv in memo select kv.Value.Student;
}
As you can see, it only differs from the C# 7 version by one line.
The first thing to note is that, per the C# spec, the `out` parameters of a method must be definitely assigned before the method returns[1]. It just so happens that the constructor of the `Student` class always creates a new `Completion` object and assigns it to the `out` parameter. Now, theoretically, an `out` parameter could be assigned a null reference (as in the case of TryGetValue), but in practice it's trivial to guarantee that it will be non-null (as in the case of our Student cstor).
In the line,
memo.Add(new Student(rdr, out completion).ID, completion);
we first call the Student cstor, which assigns a non-null value to completion, so that by the time `memo.Add` is called, the completion variable is guaranteed to be non-null.
Also, `Student.Completion` is a class that is defined inside of the `Student` class. As such, it has access to all of Student's members (private and public). But, in order to do anything to an instance of Student, a Completion instance must have field which references that Student instance (unlike the case of Java's inner classes, which are a bit more powerful I think). That is why this line is possible:
return from kv in memo select kv.Value.Student //`Value` is an instance of Student.Completion
Here is the basic definition of the Student class:
sealed class Student
{
public int ID { get; } //this is a readonly property, meaning it can only be modified in a cstor
public FullName { get; } //readonly property
private List courses = new List();
public IEnumerable Courses => from c in courses select c;
public Student(IDataReader rdr, out Completion completion)
{
ID = rdr.GetInt32("student_id");
Fullname = rdr.GetString("fullname");
completion = new Completion(this);
}
public sealed class Completion
{
public Student Student { get; } //readonly property.
public Completion(Student student)
{
this.Student = student;
}
public void AddCourse(IDataReader rdr)
{
if(rdr.GetInt32("student_id) == Student.ID)
student.courses.Add(new Course(rdr));
}
}
}
Like I said in my earlier comment, my immediate goal was to be able to create a set of
immutable objects with arbitrary nestings from the result of a
single SQL query that may have an arbitrary number of joins (which is how we represent nesting relationally). The above example just has just one nested property, but I have production code in which objects have many more nested properties. For example, the `Course` class in the aforementioned example might have its own `Completion` class for adding `CourseAssignment` instances (e.g. select from Student left join Course on ... left join CourseAssignment on ...).
But, the really big idea is that I wanted an object-capability system[2][3]. Getting objects be immutable "almost everywhere"[4] is a nice side benefit.
[1] http://www.ecma-international.org/publications/files/ECMA-ST... (section 12.1.6)
[2] https://en.wikipedia.org/wiki/Object-capability_model
[3] https://www.youtube.com/watch?v=EGX2I31OhBE
[4] https://en.wikipedia.org/wiki/Almost_everywhere (in this case "almost everywhere" is with respect to the set of all possible execution paths).