【问题标题】:"this" in c# simple linked listC#简单链表中的“this”
【发布时间】:2021-08-08 16:06:56
【问题描述】:

我正在练习 C#,并放弃了使用 Add() 方法来实现如下所示的简单链表。我抬头看了看答案,一件事也听不懂。它将变量 current_node 分配给“this”,转到最后一个节点并创建一个新的最后一个节点作为最后一个节点的“下一个”。但是,current_node 与“this”有什么关系,因为我在代码中没有看到任何影响“this.next = current_node”效果的操作?

此外,我对 GetEnumerator() 方法的评论是否正确,如果将类本身(即不是类内的方法)传递给期望输出 IEnumerable 的方法,则会自动调用它,比如XUnit的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();
    }
}

代码的一些单元测试是:

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);
    }
}

【问题讨论】:

标签: c# class this


【解决方案1】:

为了清楚起见,

  1. this 似乎总是指您列表的头部,所以 this.next = ...; 永远不会是您想要的,因为这总是会让您拥有一个长度为 2 的列表 (this.value + this.next.value).. . 也就是说,除非你创建了一个非空的.next 的节点,然后将this.next 设置为那个,这几乎是Add 方法所做的

  2. 这些是相同的,因为 thisx 引用同一个对象(相关 - C# difference between == and Equals()

    this.next = <some object>;
    

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

现在,就 Add 方法而言,它将起点设置为第一个节点 (this),然后向下迭代到列表的末尾,并通过最后一个节点添加 .next == null .当方法返回时,您将返回列表的当前实例,它仍然引用头节点,但通过对象的.next 引用链,在末尾有一个新节点。

枚举器函数不是“自己”调用的。您已经实现了IEnumerable&lt;T&gt;,并且您的链表类上的某些操作将使用枚举器(for 循环,可能反转列表等)。关于Assert.Equal的评论,既然你打电话给.Next.Next.Next等,那么你没有正确使用枚举器;具有Assert.Equal(new[] { 2, 1 }, values); 的测试 应该足以测试它

【讨论】:

    猜你喜欢
    • 2014-04-04
    • 1970-01-01
    • 2010-11-08
    • 2011-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-29
    • 2011-09-17
    相关资源
    最近更新 更多