0

First, let's define a node:

LinkedListNode<int> node = new LinkedListNode<int>(43);

You'll notice that the properties node.List, node.Next and node.Previous are all get only. Now let's add the node to a LinkedList:

LinkedList<int> linkedlist = new LinkedList<int>();
linkedlist.AddFirst(node);

At this point, the property node.List will have change to contain a reference to linkedlist. Similarly, if other nodes are added to the LinkedList, the Next and Previous properties will be updated to reflect the structure of the linked list, even though these properties do not expose a public set accessor.

How is this behavior implemented?

I know how to do it using internal, but is there a better way? For example, suppose I have a single assembly that contains many types, not just LinkedList and LinkedListNode. By making the setters for the node properties List, Previous and Next, I am exposing these setters to the whole assembly, which is undesired.

a06e
  • 18,594
  • 33
  • 93
  • 169

3 Answers3

3

How is this behavior implemented?

Without looking at the source, I would guess the setters or backing fields for those properties are marked internal and therefore accessible from LinkedList<T> since they both sit in the same assembly.

but is there a better way?

Not really. Let me elaborate. There are other options.

  1. You could make LinkedListNode<T> an inner class defined in LinkedList<T>. That's an option, but it's burdensome on callers.

  2. You could carve LinkedList<T> and LinkedListNode<T> into their own assembly. That's obviously a burden on users and could quickly degrade into a maintenance debacle.

I characterize both of these as not being better solutions.

jason
  • 236,483
  • 35
  • 423
  • 525
  • 1
    Some implementations make the node class an inner class of the linked list; that's the other way of solving the problem. – Servy Jun 12 '13 at 21:17
  • Well, that adds other problems. I wouldn't characterize that as a *better* way. – jason Jun 12 '13 at 21:18
  • 1
    Well, generally the nodes in a linked list are an implementation detail that the user doesn't need to know about at all, which is usually how such implementations are designed. Personally I do think that's better. There isn't any reason for the node to be public. – Servy Jun 12 '13 at 21:22
  • @Servy: Oh, you mean as a *`private`* inner class. Yes, that I would completely agree with, it's always puzzled me that `LinkedListNote` is `public`. My answer is in the context of `LinkedListNode` being public given the question was about the mechanism for how `LinkedListNode`'s properties are modified given they aren't `public`. – jason Jun 12 '13 at 21:23
  • @Servy If `LinkedListNode` is private, how do you expose `Next` and `Previous`? – a06e Jun 12 '13 at 21:59
  • 2
    @becko You do so through an iterator. If it was a forward only linked list the `IEnumerable` would be fine. If your iterator needs additional functionality then you'd create the appropriate interface with the appropriate functionality and have the list itself have a method/property that creates an iterator. – Servy Jun 12 '13 at 22:12
1

I checked with ILSpy, the read-only properties are backed by internal fields.

internal LinkedListNode<T> next;

public LinkedListNode<T> Next {
    get {
        if (this.next != null && this.next != this.list.head) {
            return this.next;
        }
        return null;
    }
}

As for how to do it differently, and a discussion of whether you'd want to, see this question and its answers.

Community
  • 1
  • 1
Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
1

Jason is correct. I had a look at the source and LinkedListNode<T> has internal backing fields.

One way to do this is with nested classes. I don't know if this qualifies as a "better" way, but it does avoid internal setters/fields. I've left out some of the implementation, so you can see an outline of the structure:

public class Node<T>
{
  private List list;
  private Node<T> prev, next;

  public List List { get { return list; } }
  // other accessors.

  public abstract class List
  {
    Node<T> head;

    internal List() { }

    public AddFirst(Node<T> node)
    {
        // node adding logic.
        node.list = this;       
    }

    // implementation elided for brevity
  }

}

public class MyLinkedList<T> : Node<T>.List { }

Now you can declare a MyLinkedList and add nodes to it, but there are no internal accessors on Node<T>.

Mike Zboray
  • 39,828
  • 3
  • 90
  • 122