Мысли о foreach со Счетным. Диапазон по сравнению с традиционным для цикла

Модель представления не должна создавать экземпляры окон. Что вы могли бы сделать, так это внедрить вашу модель представления в службу, отвечающую за создание окон, например:

public class MainWindowViewModel
{
    private readonly IWindowService _windowService;

    public MainWindowViewModel(IWindowService windowService)
    {
        _windowService = windowService;
        CreateWindowCommand = new DelegateCommand(() =>
        {
            _windowService.CreateWindow(new SomeViewModel());
        });
    }

    public ICommand CreateWindowCommand { get; }
}

Определить интерфейс IWindowService в проекте модели представления и конкретную реализацию этого в просмотр / презентация проекта:

public class WindowService : IWindowService
{
    public void CreateWindow(SomeViewModel vm)
    {
        Window win = new Window();
        win.DataContext = vm;
        win.Show();
    }
}
64
задан Vadim Ovchinnikov 4 March 2018 в 07:41
поделиться

13 ответов

Я считаю, что последнее "минимальное значение - Для этой цели формат "maximum" намного яснее, чем стиль "минимального количества" Range . Кроме того, я не думаю, что это действительно хорошая практика - делать такое изменение от нормы, которое не быстрее, не короче, не более привычно и не очевидно яснее.

Тем не менее, я не против идея в целом. Если бы вы пришли ко мне с синтаксисом, который выглядел бы примерно как foreach (int x от 1 до 8) , то я, вероятно, согласился бы, что это было бы улучшением по сравнению с циклом for . Однако Enumerable.Range довольно неуклюжий.

52
ответ дан 24 November 2019 в 15:44
поделиться

Как использовать новый синтаксис сегодня

из-за этого вопроса, я испытал некоторые вещи придумать хороший синтаксис, не ожидая первоклассной поддержки языка. Вот то, что я имею:

using static Enumerizer;

// prints: 0 1 2 3 4 5 6 7 8 9
foreach (int i in 0 <= i < 10)
    Console.Write(i + " ");

Не различие между <= и <.

я также создал репозиторий подтверждения концепции на GitHub еще с большей функциональностью (инвертированное повторение, пользовательский размер шага).

А минимальная и очень ограниченная реализация вышеупомянутого цикла посмотрела бы что-то как подобный это:

public readonly struct Enumerizer
{
    public static readonly Enumerizer i = default;

    public Enumerizer(int start) =>
        Start = start;

    public readonly int Start;

    public static Enumerizer operator <(int start, Enumerizer _) =>
        new Enumerizer(start);

    public static Enumerizer operator >(int _, Enumerizer __) =>
        throw new NotImplementedException();

    public static IEnumerable<int> operator <=(Enumerizer start, int end)
    {
        for (int i = start.Start; i < end; i++)
            yield return i;
    }

    public static IEnumerable<int> operator >=(Enumerizer _, int __) =>
        throw new NotImplementedException();
}
1
ответ дан 24 November 2019 в 15:44
поделиться

Я согласен с тем, что во многих (или даже в большинстве случаев) foreach гораздо более читабелен, чем стандартный for -loop при простом итерации по коллекции . Однако ваш выбор использования Enumerable.Range (index, count) не является сильным примером значения foreach для.

Для простого диапазона, начинающегося с 1, Enumerable.Range (index, count) выглядит вполне читаемым. Однако, если диапазон начинается с другого индекса, он становится менее читаемым, потому что вам нужно правильно выполнить index + count - 1 , чтобы определить, каким будет последний элемент. Например…

// Write the numbers 2 thru 8
foreach (var index in Enumerable.Range( 2, 7 ))
{
    Console.WriteLine(index);
}

В этом случае я предпочитаю второй пример.

// Write the numbers 2 thru 8
for (int index = 2; index <= 8; index++)
{
    Console.WriteLine(index);
}
1
ответ дан 24 November 2019 в 15:44
поделиться

Мне нравится подход foreach + Enumerable.Range , и я иногда его использую.

// does anyone object to the new style over the traditional style?
foreach (var index in Enumerable.Range(1, 7))

Я возражаю против var ] злоупотребление в вашем предложении. Я ценю var , но, черт возьми, просто напишите int в этом случае! ; -)

0
ответ дан 24 November 2019 в 15:44
поделиться

Строго говоря, вы злоупотребляете перечислением.

Перечислитель предоставляет средства для доступа ко всем объектам в контейнере по одному, но он не гарантирует порядок.

Можно использовать перечисление для поиска наибольшего числа в массиве. Если вы используете его, чтобы найти, скажем, первый ненулевой элемент, вы полагаетесь на детали реализации, о которых не должны знать. В вашем примере порядок кажется вам важным.

Изменить : Я ошибаюсь. Как отметил Люк (см. Комментарии), при перечислении массива в C # безопасно полагаться на порядок. Это отличается, например, от использования "for in" для перечисления массива в Javascript.

1
ответ дан 24 November 2019 в 15:44
поделиться

Я предполагаю, что могут быть сценарии, в которых Enumerable.Range (index, count) более понятен при работе с выражениями для параметров, особенно если некоторые значения в этом выражении изменяются внутри цикла. В случае для выражение будет оцениваться на основе состояния после текущей итерации, тогда как Enumerable.Range () вычисляется заранее.

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

2
ответ дан 24 November 2019 в 15:44
поделиться

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

3
ответ дан 24 November 2019 в 15:44
поделиться

Я думаю, что foreach + Enumerable.Range менее подвержен ошибкам (у вас меньше контроля и меньше способов сделать это неправильно, например, уменьшить индекс внутри тела, чтобы цикл никогда не заканчивался и т. Д. .)

Проблема удобочитаемости связана с семантикой функции Range, которая может изменяться с одного языка на другой (например, если задан только один параметр, он будет начинаться с 0 или 1, либо конец включен, либо исключен, либо второй параметр счетчик вместо конечного значения).

Что касается производительности, я думаю, что компилятор должен быть достаточно умен, чтобы оптимизировать оба цикла, чтобы они выполнялись с одинаковой скоростью, даже с большими диапазонами (я полагаю, что Range не создает коллекцию, но конечно итератор).

6
ответ дан 24 November 2019 в 15:44
поделиться

Похоже, довольно длинный подход к проблеме, которая уже решена. За Enumerable.Range стоит целый конечный автомат, который на самом деле не нужен.

Традиционный формат имеет фундаментальное значение для разработки и знаком всем. Я не вижу никаких преимуществ в вашем новом стиле.

9
ответ дан 24 November 2019 в 15:44
поделиться

Это просто для развлечения. (Я бы просто использовал стандартный формат цикла « for (int i = 1; i <= 10; i ++) ».)

foreach (int i in 1.To(10))
{
    Console.WriteLine(i);    // 1,2,3,4,5,6,7,8,9,10
}

// ...

public static IEnumerable<int> To(this int from, int to)
{
    if (from < to)
    {
        while (from <= to)
        {
            yield return from++;
        }
    }
    else
    {
        while (from >= to)
        {
            yield return from--;
        }
    }
}

Вы также можете добавить Step метод расширения также:

foreach (int i in 5.To(-9).Step(2))
{
    Console.WriteLine(i);    // 5,3,1,-1,-3,-5,-7,-9
}

// ...

public static IEnumerable<T> Step<T>(this IEnumerable<T> source, int step)
{
    if (step == 0)
    {
        throw new ArgumentOutOfRangeException("step", "Param cannot be zero.");
    }

    return source.Where((x, i) => (i % step) == 0);
}
39
ответ дан 24 November 2019 в 15:44
поделиться

Фактически вы можете сделать это на C # (предоставив To и Do в качестве методов расширения для int и IEnumerable соответственно):

1.To(7).Do(Console.WriteLine);

SmallTalk навсегда!

9
ответ дан 24 November 2019 в 15:44
поделиться

Я хотел бы иметь синтаксис некоторых других языков, таких как Python, Haskell и т. Д.

// Write the numbers 1 thru 7
foreach (int index in [1..7])
{
    Console.WriteLine(index);
}

К счастью, теперь у нас есть F # :)

Что касается C #, я должны придерживаться метода Enumerable.Range .

6
ответ дан 24 November 2019 в 15:44
поделиться

@ Люк: Я повторно реализовал ваш метод расширения To () и использовал для этого метод Enumerable.Range () . Таким образом, он получается немного короче и использует как можно больше инфраструктуры, предоставленной нам .NET:

public static IEnumerable<int> To(this int from, int to)
{ 
    return from < to 
            ? Enumerable.Range(from, to - from + 1) 
            : Enumerable.Range(to, from - to + 1).Reverse();
}
5
ответ дан 24 November 2019 в 15:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: