Модель представления не должна создавать экземпляры окон. Что вы могли бы сделать, так это внедрить вашу модель представления в службу, отвечающую за создание окон, например:
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();
}
}
Я считаю, что последнее "минимальное значение - Для этой цели формат "maximum" намного яснее, чем стиль "минимального количества" Range
. Кроме того, я не думаю, что это действительно хорошая практика - делать такое изменение от нормы, которое не быстрее, не короче, не более привычно и не очевидно яснее.
Тем не менее, я не против идея в целом. Если бы вы пришли ко мне с синтаксисом, который выглядел бы примерно как foreach (int x от 1 до 8)
, то я, вероятно, согласился бы, что это было бы улучшением по сравнению с циклом for
. Однако Enumerable.Range
довольно неуклюжий.
из-за этого вопроса, я испытал некоторые вещи придумать хороший синтаксис, не ожидая первоклассной поддержки языка. Вот то, что я имею:
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();
}
Я согласен с тем, что во многих (или даже в большинстве случаев) 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);
}
Мне нравится подход 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
в этом случае! ; -)
Строго говоря, вы злоупотребляете перечислением.
Перечислитель предоставляет средства для доступа ко всем объектам в контейнере по одному, но он не гарантирует порядок.
Можно использовать перечисление для поиска наибольшего числа в массиве. Если вы используете его, чтобы найти, скажем, первый ненулевой элемент, вы полагаетесь на детали реализации, о которых не должны знать. В вашем примере порядок кажется вам важным.
Изменить : Я ошибаюсь. Как отметил Люк (см. Комментарии), при перечислении массива в C # безопасно полагаться на порядок. Это отличается, например, от использования "for in" для перечисления массива в Javascript.
Я предполагаю, что могут быть сценарии, в которых Enumerable.Range (index, count)
более понятен при работе с выражениями для параметров, особенно если некоторые значения в этом выражении изменяются внутри цикла. В случае для
выражение будет оцениваться на основе состояния после текущей итерации, тогда как Enumerable.Range ()
вычисляется заранее.
В остальном, Я согласен, что придерживаться для
обычно лучше (более знакомый / читаемый для большего количества людей ... читабельность - очень важное значение в коде, которое необходимо поддерживать).
Я уверен, что у каждого есть свои личные предпочтения (многие предпочли бы более поздний вариант только потому, что он знаком почти со всеми языками программирования), но я, как и вы, начинаю больше любить foreach и больше, особенно теперь, когда вы можете определять диапазон.
Я думаю, что foreach + Enumerable.Range менее подвержен ошибкам (у вас меньше контроля и меньше способов сделать это неправильно, например, уменьшить индекс внутри тела, чтобы цикл никогда не заканчивался и т. Д. .)
Проблема удобочитаемости связана с семантикой функции Range, которая может изменяться с одного языка на другой (например, если задан только один параметр, он будет начинаться с 0 или 1, либо конец включен, либо исключен, либо второй параметр счетчик вместо конечного значения).
Что касается производительности, я думаю, что компилятор должен быть достаточно умен, чтобы оптимизировать оба цикла, чтобы они выполнялись с одинаковой скоростью, даже с большими диапазонами (я полагаю, что Range не создает коллекцию, но конечно итератор).
Похоже, довольно длинный подход к проблеме, которая уже решена. За Enumerable.Range
стоит целый конечный автомат, который на самом деле не нужен.
Традиционный формат имеет фундаментальное значение для разработки и знаком всем. Я не вижу никаких преимуществ в вашем новом стиле.
Это просто для развлечения. (Я бы просто использовал стандартный формат цикла « 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);
}
Фактически вы можете сделать это на C # (предоставив To
и Do
в качестве методов расширения для int
и IEnumerable
соответственно):
1.To(7).Do(Console.WriteLine);
SmallTalk навсегда!
Я хотел бы иметь синтаксис некоторых других языков, таких как Python, Haskell и т. Д.
// Write the numbers 1 thru 7
foreach (int index in [1..7])
{
Console.WriteLine(index);
}
К счастью, теперь у нас есть F # :)
Что касается C #, я должны придерживаться метода Enumerable.Range
.
@ Люк:
Я повторно реализовал ваш метод расширения 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();
}