0

I'm practicing c# and gave up on making the Add() method for an implementation of a simple linked list shown below. I looked up at the answer and can't understand one thing. It assigns the variable current_node to "this", goes to the last node and makes a new last node as the "next" of the last node. But then how is current_node related to "this", since I don't see any action to the effect of "this.next = current_node" in the code?

In addition, is my comment correct regarding the GetEnumerator() method, that it is automatically called if the class itself (i.e. not the methods inside the class) is passed into a method that expects an output of IEnumerable<T>, such as XUnit's Assert.Equal()?

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class SimpleLinkedList<T> : IEnumerable<T>
{
    private T value;
    private SimpleLinkedList<T> next;

    // START READING HERE, THESE TWO METHODS DEFINE THE LIST
    // "this" refers to the linked list itself.
    // Two constructors mean that the class can take
    // either a single input of type T or a collection of type T
    // that implements IEnumerable.
    public SimpleLinkedList(T value) => this.value = value;

    public SimpleLinkedList(IEnumerable<T> values)
    {
        this.value = values.First();
        foreach (var i in values.Skip(1))
        {
            this.Add(i);
        }
        
    }

    public T Value 
    { 
        get
        {
            return this.value;
        } 
    }

    public SimpleLinkedList<T> Next
    { 
        get
        {
            return this.next;
        } 
    }

    public SimpleLinkedList<T> Add(T value)
    {
        var current_node = this;
        while (current_node.next != null)
        {
            current_node = current_node.next;
        }
        current_node.next = new SimpleLinkedList<T>(value);
        return this;
    }

    // This method is automatically called if the class itself
    // (i.e. not the methods inside the class) is passed into a
    // method that expects an output of IEnumerable<T>,
    // such as XUnit's Assert.Equal().
    public IEnumerator<T> GetEnumerator()
    {
        yield return value;
        var current_node = this.next;
        while (current_node != null)
        {
            yield return current_node.value;
            current_node = current_node.next;   
        }
    }

    // Since we use Inenumerable<T> interface, need to also implement the
    // non-generic GetEnumerator() method for backwards compatibilty with previous
    // .net versions. 
    // Just make this method return the generic GetEnumerator() method.
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Some of the unit tests for the code are:

using System.Linq;
using Xunit;

public class SimpleLinkedListTests
{
    [Fact]
    public void Single_item_list_value()
    {
        var list = new SimpleLinkedList<int>(1);
        Assert.Equal(1, list.Value);
    }

    [Fact(Skip = "Remove this Skip property to run this test")]
    public void Single_item_list_has_no_next_item()
    {
        var list = new SimpleLinkedList<int>(1);
        Assert.Null(list.Next);
    }

    [Fact]
    public void Two_item_list_first_value()
    {
        var list = new SimpleLinkedList<int>(2).Add(1);
        Assert.Equal(2, list.Value);
    }

    [Fact(Skip = "Remove this Skip property to run this test")]
    public void Two_item_list_second_value()
    {
        var list = new SimpleLinkedList<int>(2).Add(1);
        Assert.Equal(1, list.Next.Value);
    }

    [Fact(Skip = "Remove this Skip property to run this test")]
    public void Two_item_list_second_item_has_no_next()
    {
        var list = new SimpleLinkedList<int>(2).Add(1);
        Assert.Null(list.Next.Next);
    }

    [Fact]
    public void Implements_enumerable()
    {
        var values = new SimpleLinkedList<int>(2).Add(1);
        Assert.Equal(new[] { 2, 1 }, values);
    }

    [Fact]
    public void From_enumerable()
    {
        var list = new SimpleLinkedList<int>(new[] { 11, 7, 5, 3, 2 });
        Assert.Equal(11, list.Value);
        Assert.Equal(7, list.Next.Value);
        Assert.Equal(5, list.Next.Next.Value);
        Assert.Equal(3, list.Next.Next.Next.Value);
        Assert.Equal(2, list.Next.Next.Next.Next.Value);
    }

    [Theory(Skip = "Remove this Skip property to run this test")]
    [InlineData(1)]
    [InlineData(2)]
    [InlineData(10)]
    [InlineData(100)]
    public void Reverse(int length)
    {
        var values = Enumerable.Range(1, length).ToArray();
        var list = new SimpleLinkedList<int>(values);
        var reversed = list.Reverse();
        Assert.Equal(values.Reverse(), reversed);
    }

    [Theory(Skip = "Remove this Skip property to run this test")]
    [InlineData(1)]
    [InlineData(2)]
    [InlineData(10)]
    [InlineData(100)]
    public void Roundtrip(int length)
    {
        var values = Enumerable.Range(1, length);
        var listValues = new SimpleLinkedList<int>(values);
        Assert.Equal(values, listValues);
    }
}
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
aldo
  • 89
  • 6
  • Does this answer your question? [When do you use the "this" keyword?](https://stackoverflow.com/questions/23250/when-do-you-use-the-this-keyword) and [What is the meaning of “this” in C#](https://stackoverflow.com/questions/6270774/what-is-the-meaning-of-this-in-c-sharp) and [What is the purpose of 'this' keyword in C#](https://stackoverflow.com/questions/2013857/what-is-the-purpose-of-this-keyword-in-c-sharp) and [In C#, is “this” keyword required?](https://stackoverflow.com/questions/1517858/in-c-is-this-keyword-required) –  Aug 08 '21 at 16:09
  • 3
    You're reading the code backwards. It "assigns `this` to `current_node`". You cannot assign anything "to `this`" – OneCricketeer Aug 08 '21 at 16:10
  • The keyword `this` is for manipulating the current instance from the point of view of the class object instance code. For example having `SimpleLinkedList instance;` here `this` is `instance` itself from withing the class. That's all. See suggested duplicates for reasons and uses. –  Aug 08 '21 at 16:13
  • Where's the "this.next = current_node" in the code? Or maybe you believe it should be there? – Wiktor Zychla Aug 08 '21 at 16:15

1 Answers1

1

For clarity,

  1. this seems to always refer to the head of your list, so this.next = ...; would never be what you wanted because that would always make you have a list of length 2 (this.value + this.next.value)... That is, unless you created a node with a non-null .next, then set this.next to that, which is almost what the Add method does

  2. these are the same because this and x reference the same object (related - C# difference between == and Equals())

    this.next = <some object>;
    

    var x = this;
    x.next = <some object>;
    

Now, as far as the Add method goes, it sets the starting point as the first node (this), then iterates down to the end of the list, and adds via the last node, which has .next == null. When the method returns, you're returning the current instance of the list, which still references the head node, but through the .next reference chain of objects, has a new node at the end.

The enumerator functions are not called "by themselves". You've implemented IEnumerable<T>, and certain actions over your linked list class will use the enumerator (for loops, maybe reversing the list, etc). Regarding the comment on Assert.Equal, since you're calling .Next.Next.Next, etc, then you're not properly using the enumerator; the test that has Assert.Equal(new[] { 2, 1 }, values); should be enough to test it

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245