Равенство последовательностей массивов объектов, содержащих двумерные массивы объектов - PullRequest
0 голосов
/ 09 ноября 2019

Я хотел закодировать функцию, делающую что-то со входом object[,] и возвращающую object[], элементы которой object[,]. Я просто написал что-то глупое для начала, без ввода данных, чтобы сначала настроить тесты, а затем я правильно кодирую функцию:

public static class TestData
{
    public static object[,] Island1()
    {
        object[,] res = new object[3, 2];
        res[0, 0] = 1;
        res[0, 1] = 0;
        res[1, 0] = 1;
        res[1, 1] = 1;
        res[2, 0] = 1;
        res[2, 1] = 0;
        return res;
    }
}

public class ComponentsFinder
{
    public object[] GetIslands()
    {
        return  new object[]{TestData.Island1()};
    }
}

и тест:

[TestClass]
public class TestCompomentsFinder
{
    [TestMethod]
    public void FirstTest()
    {
        object[,] island1 = TestData.Island1();
        object[] expected = new object[] {island1};
        object[] actual = new ComponentsFinder().GetIslands();
        bool res = actual.SequenceEqual(expected);
        Assert.IsTrue(res);
    }
}

Этот тестне получается, и я знаю почему: даже если оба object[] содержат только один object, представляющий object[,], который представляет одну и ту же "матрицу", они не указывают на один и тот же массив, следовательно, провал теста.

Если бы у меня был реальный класс C вместо object[,], это не было бы проблемой, поскольку я заставил бы C правильно реализовать IEquatable, и тогда переопределение Equals было бы вызваноSequenceEquals и тест будет в порядке.

Но здесь у меня нет класса, так что мне делать? Должен ли я действительно обернуть все в классах или есть другой способ проверить равенство моих object[] (что равнозначно проверке равенства object[,] в смысле наличия одинаковых размеров и одинаковых соответствующих коэффициентов,коэффициенты моих object[,] являются типом реализации IEquatable)?

1 Ответ

0 голосов
/ 11 ноября 2019

Вы правы, почему SequenceEqual() терпит неудачу. Массивы в C # являются ссылочными типами, что означает, что при их сравнении вы получаете reference равенство, что означает, что CLR проверяет, являются ли они в буквальном смысле одним и тем же объектом в памяти (два object[,] s являются различными объектамив памяти.)

Кроме того, SequenceEqual() выполняет итерацию по самым внешним элементам object[], но не может проникнуть в содержимое этих массивов для итерации по внутреннему object[,].

Вам необходимо значение равенства, чтобы вы могли сравнивать значения объектов, а не их ссылки.

Однако, поскольку вы используете object s для всего, вы 'не получим равного значения, даже если объекты на самом деле ints. Посмотрите этот пример в интерактивном окне C #:

> object object1 = 1;
> object object2 = 1;
> object1 == object2
false
> (int)object1 == (int)object2
true

Вам нужно будет преобразовать отдельные значения обратно в int s, прежде чем вы сможете сделать правильное сравнение с ними. В любом случае, я бы порекомендовал использовать массивы int, просто для лучшей безопасности типов (и, возможно, чуть-чуть лучшей производительности, если не выполнять кучу преобразований в боксы).

При этом общий подход заключается впереберите каждый отдельный элемент и сравните их по отдельности. Если они int s в object[,] массивах, убедитесь, что сначала их разыграли ((int)). Что касается реализации этого сравнения, у вас есть несколько вариантов.

Вы можете реализовать свой собственный содержащий класс, как вы уже упоминали. В этом случае вы можете переопределить / реализовать интерфейсы для обеспечения необходимой вам функциональности. Вы также можете реализовать автономный вспомогательный метод (например, public void ArrayCompare(object[,] arr1, object[,] arr2)).

Я реализовал пример в качестве метода расширения, который позволит вам использовать ArrayExtension.ArrayCompare(actual, expected) или actual.ArrayCompare(expected) для использованияит.

public static class ArrayExtension
    {
        public static bool ArrayCompare(this object[,] arr1, object[,] arr2)
        {
            if (arr1.Rank != arr2.Rank) return false;

            var numDims = arr1.Rank;
            for(var i = 0; i < numDims; i++)
                if (arr1.GetLength(i) != arr2.GetLength(i))
                    return false;

            for(var j = 0; j < numDims; j++)
            {
                var dimLength = arr1.GetLength(j);
                for(var k = 0; k < dimLength; k++)
                    if ((int)arr1[j, k] != (int)arr2[j, k])
                        return false;
            }

            return true;
        }
    }
...