使用IComparable比较数组

时间:2017-05-12 23:54:12

标签: c# arrays inheritance

我的学校任务有问题。

"让我们说类List是生成随机数组的构造函数。

类List1继承自List类,并使用IComparable来比较第一个元素,然后是第二个元素等等(空白是0){1,1,1}< {1,2}< {2}

类List2同样继承并按大小比较数组的大小。如果尺寸相同,请查看List1 {1,2,3}< {1,2,3,4}< {2,2,3,4}"

我制作了班级名单。

  public List(int b)
      {
          tabb = new int[b];
          for (int i = 0; i < b; i++)
              tabb[i] = r1.Next(0, 100);
      }

我注意到从List继承到List1的构造函数不是继承的,所以我不知道在哪里以及如何使用IComparable

class List1 : List,IComparable<List1>
  {
    public int CompareTo(List1 x){
      return this.CompareTo(x);
    }
  }

2 个答案:

答案 0 :(得分:0)

  

我注意到从List继承到List1的构造函数不是继承的,所以我不知道在哪里以及如何使用IComparable

我无法理解这句话是一个单一的问题,因为缺乏构造函数的继承似乎完全独立于我如何使用IComparable<T>

所以,让我们分别解决这两个问题:

  1. 你是正确的,构造函数不是继承的。也就是说,虽然它们仍然存在,但它们只能由派生类型调用,而不能由外部代码直接使用。如果基类没有无参数构造函数,那么作为派生类的实现者,您有责任提供构造函数,然后调用相应的基类构造函数(每个类都有一些构造函数) )。例如:
  2. class List1 : List
    {
        public List1(int count) : base(count) { }
    }
    

    (我假设List是.NET List<T>类型以外的某种类型,即它不是通用的,并且是在您自己的上下文中定义的。)

    通过这种方式,您可以提供必要的构造函数,并通过使用: base(count)将参数值传递给构造函数来确保调用正确的基本构造函数。

    1. 如何使用IComparable<T>。在我看来,这是你的任务的关键。我会帮你为你编写代码。但是,我会尝试详细说明帮助您了解他们的要求(当然,您的老师是您接受此建议的最佳人选)。

      你在这里有两个不同的任务。两者都要求您实现接口IComparable<T>,因此您需要做的第一件事是确保您了解实现接口的含义。你呢?界面是一种“契约”。在任何接口声明中,都描述了需要提供该接口的实现的成员。如果您查看the definition of IComparable<T>,您会发现它只需要实现一个成员:CompareTo()方法。

      因此,您的任务归结为如何为两个必需的子类List1List2中的每一个实现该方法。两者的实施基本思路相同,但具体情况不同。

      在第一种情况下,比较将根据集合的内容对List1的两个实例进行排序,其中这些实例的顺序由数组的相同位置中的第一个非相等元素的排序确定。在每个实例中。在给出的示例中,{1, 1, 1}被视为“小于”{1, 2},因为当您比较List1的每个实例的每个位置的元素时,元素不同的第一个对应位置是第二个位置(索引1),其中{1, 1, 1}的值为1{1, 2}的值为2。值1小于值2,因此{1, 1, 1}的整个实例“小于”{1, 2}的整个实例。 (不幸的是,你的帖子中描述的分配不清楚如何命令一个实例,其中一个的基础列表比另一个的基础列表短,但在这些元素位置具有完全相同的值。我会使更短的列表被视为“小于”,但这并非毫无疑问是唯一有效的方法。)

      在分配的第二部分,实现List2,唯一被比较的是列表的长度。这应该比分配的第一部分更容易实现,因为在每个List2实例中只有一个值可供比较,即tabb.Length

      请注意,在这两种情况下,您都可以利用int也实现IComparable<T>这一事实,因此您可以使用其CompareTo()方法来确定类的每个实例中的相应值是否为小于,等于或大于彼此。
    2. 我希望这足以让你指出正确的方向。如果您需要更多帮助,您可能应该咨询您的教练。他们会确切地知道他们想要给你多少帮助,同时还没有真正为你做任务。

答案 1 :(得分:0)

对不起,我不是故意要全力以赴。但后来它刚刚发生了。晚了。它让我感兴趣,特别是需要能够测试它,即使随机数的要求使其难以测试。

我所做的解决方案是将列表与其填充方式分开。为了测试目的,我可以替换一个“populator”,它可以完全插入我想要的值。这样我就可以创建测试场景。

// This is the base list - it just requires something to populate it.
public class IntegerList : List<int>
{
    public IntegerList(IIntegerListPopulator populator, int size)
    {
        populator.PopulateList(this, size);
    }
}

// Interface and implementation to populate a list with random numbers.
public interface IIntegerListPopulator
{
    void PopulateList(List<int> target, int size);
}

public class RandomIntegerListPopulator : IIntegerListPopulator
{
    public void PopulateList(List<int> target, int size)
    {
        var random = new Random();
        for (var i = 0; i < size; i++)
        {
            target.Add(random.Next(0, 100));
        }
    }
}

// Compares by values, but the populator is injected - needed so that
// the class can be tested.
public class IntegerListThatComparesByValues : IntegerList, IComparable<IntegerListThatComparesByValues>
{
    public IntegerListThatComparesByValues(IIntegerListPopulator populator, int size)
        : base(populator, size)
    { }

    public int CompareTo(IntegerListThatComparesByValues other)
    {
        return new IntegerListValueComparer().Compare(this, other);
    }
}

// Class to perform comparisons by value. There's no real point 
// in implementing IComparer since I'm not using it that way,
// but it doesn't hurt.
public class IntegerListValueComparer : IComparer<IntegerList>
{
    public int Compare(IntegerList x, IntegerList y)
    {
        // I made this part up. I don't actually know how
        // you want to handle nulls. 
        if (x == null && y == null) return 0;
        if (x == null) return 1;
        if (y == null) return -1;

        // Always compare the longer one to the shorter.
        // if this one is shorter, do the reverse comparison
        // and reverse the result.
        if (y.Count < x.Count) return -Compare(y, x);
        if (x.SequenceEqual(y)) return 0;
        for (var index = 0; index < x.Count; index++)
        {
            var comparison = x[index].CompareTo(y[index]);
            if (comparison != 0) return comparison;
        }

        // If the other list is longer than this one, then assume
        // that the next element of this list is 0.  
        return -y[x.Count];
    }
}

public class IntegerListThatComparesByLength : IntegerList, IComparable<IntegerListThatComparesByLength>
{
    public IntegerListThatComparesByLength(IIntegerListPopulator populator, int size)
        : base(populator, size)
    {
    }

    public int CompareTo(IntegerListThatComparesByLength other)
    {
        var comparisonByCount = Count.CompareTo(other?.Count ?? 0);
        return comparisonByCount != 0
            ? comparisonByCount
            : new IntegerListValueComparer().Compare(this, other);
    }
}

// *************************************************************
// These are the concrete classes specified in the requirements.
// *************************************************************

public class RandomIntegerListThatComparesByValues : 
    IntegerListThatComparesByValues
{
    public RandomIntegerListThatComparesByValues(int size)
        : base(new RandomIntegerListPopulator(), size)
    { }
}

public class RandomIntegerListThatComparesByLength : 
    IntegerListThatComparesByLength
{
    public RandomIntegerListThatComparesByLength(int size)
        : base(new RandomIntegerListPopulator(), size)
    { }
}

// *************************************************************
// The rest is all testing.
// *************************************************************

// Allows me to create class instances that contain the numbers
// I specify instead of random numbers so that I can create 
// test cases.
public class IntegerListPopulatorTestDouble : IIntegerListPopulator
{
    private readonly int[] _values;

    public IntegerListPopulatorTestDouble(params int[] values)
    {
        _values = values;
    }

    public void PopulateList(List<int> target, int size)
    {
        target.AddRange(_values.Take(size));
    }
}

[TestClass]
public class IntegerListThatComparesByValuesTests
{
    [TestMethod]
    public void EmptyListsAreEqual()
    {
        var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(), 0 );
        var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(), 0);
        Assert.AreEqual(0, list1.CompareTo(list2));
    }

    [TestMethod]
    public void ListsWithSameValuesAreEqual()
    {
        var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1,2,3), 3);
        var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1,2,3), 3);
        Assert.AreEqual(0, list1.CompareTo(list2));
    }

    [TestMethod]
    public void ListsOfSameLengthComparedByFirstNonEqualValue()
    {
        var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 4), 3);
        var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
        Assert.IsTrue(list1.CompareTo(list2) > 0);
    }

    [TestMethod]
    public void MissingElementsOfListAreSortedAsZeros()
    {
        var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3, 4), 4);
        var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
        var comparison = list1.CompareTo(list2);
        Assert.IsTrue(comparison > 0);
        comparison = list2.CompareTo(list1);
        Assert.IsTrue(comparison < 0);
    }

    [TestMethod]
    public void MissingElementsOfListAreSortedAsZeros_Case2()
    {
        var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3, -4), 4);
        var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
        Assert.IsTrue(list1.CompareTo(list2) < 0);
        Assert.IsTrue(list2.CompareTo(list1) > 0);
    }
}

[TestClass]
public class IntegerListThatComparesByLengthTests
{
    [TestMethod]
    public void ListsAreComparedByLength()
    {
        var list1 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 3, 4), 4);
        var list2 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
        Assert.IsTrue(list1.CompareTo(list2) > 0);
        Assert.IsTrue(list2.CompareTo(list1) < 0);
    }

    [TestMethod]
    public void ListsOfEqualLengthAreComparedByValue()
    {
        var list1 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 4), 3);
        var list2 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
        Assert.IsTrue(list1.CompareTo(list2) > 0);
        Assert.IsTrue(list2.CompareTo(list1) < 0);
    }
}

我不知道他们是否在学校教授单元测试,但这说明了为什么他们应该这样做。虽然逻辑并非难以置信,但它的第一次尝试可能并不完美。如果没有创建一些测试用例来对付它,你怎么知道你是否做对了?你的导师怎么会知道?如果您修复了一个错误但这样做会破坏其他测试用例怎么办?然后,如果你必须调试,每次调试你都会获得不同的随机数。吓人。

(这是随机性真的让我失望。如果数字总是随机的,那么教练如何期望观察结果并知道它们是正确的?这是可能的但是眼球不是最可靠的方式要知道它有效。)

在现实生活中,如果没有这些单元测试告诉我它按预期工作,我不希望将稍微复杂的逻辑放入生产系统。

此外,当您阅读要求的描述时,可能会令人困惑和不清楚。但假设我理解了这些要求,现在您可以查看单元测试并更清楚地了解此代码应该执行的操作。否则其他人可能需要进行更改,如果他们无法分辨它应该做什么,他们就无法判断他们是否打破了它。如果事实证明我的要求是错误的,那么我可以更改测试以符合正确的要求。测试将失败,然后我修改代码,直到测试通过。