So, I've got a use-case similar to this one, but with some additional specifics that I feel warrant a new question. (related questions, for reference)
I'm writing a data structure to that implements a cycle. The basic design is something like this:
public class Cycle<T>
{
public Node<T> Origin { get; private set; }
public int Count { get; private set; }
}
public class Node<T>
{
public Cycle<T> Cycle { get; private set; }
public Node<T> Next { get; private set; }
public Node<T> Previous { get; private set; }
public T Value { get; set; }
}
However, I want to implement all of the following behaviors:
- Update Cycle.Count as nodes are inserted/deleted
- Allow an "empty"
Cycle
(i.e.Origin = null
) to create a newNode
for theOrigin
- Prevent the construction of new
Node
objects outside of cases 1 & 2 - Avoid exposing methods that don't need to be called by classes other than these two
In C++, I'd simply make each class a friend
of the other. But in C#, I don't see a way to make it work.
I know I can can fulfill all but #3 if I nest Node
inside of Cycle
and only expose a single constructor for Node
, like so:
public class Cycle<T>
{
public Node Origin { get; private set; }
public int Count { get; private set; }
public class Node
{
public Cycle<T> Cycle { get; private set; }
public Node Next { get; private set; }
public Node Previous { get; private set; }
public T Value { get; set; }
internal Node<T>(Cycle<T> cycle)
{
if (cycle.Origin != null)
throw new InvalidOperationException();
else
{
Cycle = cycle;
Next = this;
Previous = this;
cycle.Origin = this;
cycle.Count = 1;
}
}
}
}
But as you can see, I can only go as far as internal
, so I still have to verify data integrity or break encapsulation.
I do have one "clever" idea, but it's kind of a black magic answer:
public abstract class Cycle<T>
{
public Node Origin { get; private set; }
public int Count { get; private set; }
public sealed class Node
{
private CycleInternal _cycle;
public Cycle<T> Cycle { get { return _cycle; } }
public Node Next { get; private set; }
public Node Previous { get; private set; }
public T Value { get; set; }
// this constructor can be called by CycleInternal, but not other classes!
private Node(CycleInternal cycle)
{
Cycle = cycle;
Next = this;
Previous = this;
cycle.Origin = this;
cycle.Count = 1;
}
private sealed class CycleInternal : Cycle<T>
{
// this constructor can be called by Node, but not other classes!
public CycleInternal() {}
}
}
}
In this case, my worry is that something else could inherit from Cycle; could that be prevented by making the constructor to Cycle private? Or am I just being paranoid here?