Подтвердить что ты не робот

Это плохая форма для возврата массивов в С#? Должен ли я возвращать List <T>?

У меня есть функция, которая возвращает переменное число элементов, должен ли я возвращать массив или список? Размер "коллекции" не изменяется после возврата, т.е. Для всех целей коллекция неизменна. Я бы подумал просто вернуть массив, но некоторые люди сказали, что не возвращают массивы с переменным размером из функции, так как это "плохая форма". Не знаете, почему?

Имеет ли значение, что это должно быть совместимо с .NET 2.0?

4b9b3361

Ответ 1

Плохая форма для возврата массивов, если не нужна, и особенно для возврата List<T>.

Обычно вам нужно вернуть IEnumerable<T> или IList<T>.

Если вашему пользователю просто нужно будет запускать каждый элемент, IEnumerable<T> предоставит эту возможность. Он также позволяет потенциально реализовать подпрограмму (сейчас или позже) с использованием отложенного выполнения.

Если вашему пользователю нужно обращаться к элементам по индексу, верните IList<T>. Это обеспечивает все преимущества массивов, но дает вам большую гибкость в вашей реализации. Вы можете реализовать его как массив, список или другую коллекцию, которая реализует IList<T>, и вам не нужно преобразовывать/копировать в массив.

Ответ 2

В одном из мнений я часто вижу предложение вернуться либо IList<T>, либо ReadOnlyCollection<T>. ОК, чтобы вернуть их, если у вас есть один из них - оба могут быть назначены непосредственно на IEnumerable<T> (они работают напрямую с любыми методами LINQ). Следует отметить, что ReadOnlyCollection<T> - очень легкий тип, который может обернуть любой IList<T>.

Ответ 4

Как вы, несомненно, видели ответы в этой теме, мнения по этому вопросу широки.

В общем, мои мысли таковы:

Если я возвращаю список, размер которого постоянный, и я не хочу, чтобы вызывающий абонент мог изменять мои данные (что составляет 99% времени), я верну его ReadOnlyCollection<T>. Это дает мне неизменность на стороне клиента без необходимости удвоения (или тройного или любого другого) объема памяти моих данных при создании нового списка или массива.

Я смущаюсь сказать, что "вы всегда должны возвращать IEnumerable или IEnumerable<T>". Хотя в некоторых случаях это, безусловно, подходит (и этих случаев не так много), легкий характер интерфейса IEnumerable значительно ограничивает вашу функциональность (ни один индексный поиск не является самым большим), а во многих случаях основным источником в любом случае данные будут массивом, даже если это a List<T>.

Дополнительная опасность возврата IEnumerable исходит из ленивой практики простого возврата внутреннего списка в контексте этого интерфейса. Выполнение этого предоставляет вам метод вызова, злоупотребляющий этим ярлыком, возвращая его к более надежному типу коллекции. Хороший, защитный программист этого не сделает.

Самый низкий объем памяти происходит от использования ReadOnlyCollection, построенного из List. A ReadOnlyCollection все еще подвергает вас опасности из-за злоупотреблений на основе отражения и фиксирует ссылку на изменяемый список, но это немного случайный случай.

Ответ 5

Если размер коллекции не изменится после возврата IEnumerable<T>, это будет моим выбором, так как вызывающий может затем использовать методы расширения LINQ со списком сразу.

Ответ 6

Легко получить массив из IList, если массив нужен вам, но обратное неверно. Поэтому возвращение IList предпочтительнее.

Ответ 7

Верните IEnumerable, если вам не нужно:

  • Доступ Count - затем верните IReadOnlyCollection.
  • изменить элементы, а Count - меньше ArrayList.Capacity - затем вернуть массив.
  • добавить новые элементы - затем вернуть список.
  • некоторые микрооптимизации - это бенчмарк. Например. foreach over array быстрее, чем над списком

BenchmarkDotNet = v0.10.8, OS = Windows 10 Redstone 1 (10.0.14393) Процессор = Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount = 4 Частота = 3233538 Гц, разрешение = 309,2588 нс, таймер = TSC [Хост]: Clr 4.0.30319.42000, 64 бит RyuJIT-v4.6.1648.0 Clr: Clr 4.0.30319.42000, 64 бит RyuJIT-v4.6.1648.0 Core:.NET Core 4.6.25211.01, 64bit RyuJIT

    Method |  Job | Runtime |       Mean |      Error |    StdDev |        Min |        Max |     Median | Rank | Allocated |
---------- |----- |-------- |-----------:|-----------:|----------:|-----------:|-----------:|-----------:|-----:|----------:|
  TestList |  Clr |     Clr | 5,153.3 ns |  34.002 ns | 31.806 ns | 5,119.2 ns | 5,213.4 ns | 5,135.9 ns |    3 |       0 B |
 TestArray |  Clr |     Clr |   730.1 ns |   6.962 ns |  6.512 ns |   722.4 ns |   743.9 ns |   729.5 ns |    2 |       0 B |
  TestList | Core |    Core | 5,188.4 ns | 102.816 ns | 96.174 ns | 5,070.3 ns | 5,342.6 ns | 5,185.6 ns |    3 |       0 B |
 TestArray | Core |    Core |   709.0 ns |   6.126 ns |  5.730 ns |   700.8 ns |   718.6 ns |   708.1 ns |    1 |       0 B |

Код:

[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
[ClrJob, CoreJob]
[HtmlExporter, MarkdownExporter]
[MemoryDiagnoser]
public class BenchmarkForEach
{
    List<string> testData = new List<string>();
    string[] testArray;
    public BenchmarkForEach()
    {
        for(int i=0;i<1000;i++)
        {
            testData.Add(i.ToString());
        }
        testArray = testData.ToArray();
    }
    [Benchmark]
    public int TestList()
    {
        var x = 0;
        foreach(var i in testData)
        {
            x += i.Length;
        }
        return x;
    }
    [Benchmark]
    public int TestArray()
    {
        var x = 0;
        foreach (var i in testArray)
        {
            x += i.Length;
        }
        return x;
    }

}