Параметры/возвраты наборов должны быть IEnumerable <T> или T []?

1120 Не знаю, какую неудачу вы видите, но я боюсь, что для вас это не самый лучший способ. .o Файлы и исполняемый файл ($(OUTPUT)) являются отдельными правилами. Если последнее не удается, первое уже не рассматривается. См. Документацию :

.DELETE_ON_ERROR: Если .DELETE_ON_ERROR упоминается как цель где-либо в make-файле, то make удалит цель правила, если оно изменилось и его рецепт завершается с ненулевым статусом выхода, так же, как и при получении сигнала. См. Ошибки в рецептах .

blockquote>

Другими словами, если ваша цель, создающая бинарный объект, потерпела неудачу после того, как сама цель .o была обновлена, make уничтожит измененный файл. Но если ваш исполняемый файл не связывался, он не вернется и не удалит объектные файлы.

Не уверен, что это было бы неплохо, но если бы вам действительно это было нужно, вы, вероятно, могли бы добиться этого путем рефакторинга вашего make-файла, чтобы в основном иметь прямое exec + objs из правила исходных условий с одним рецептом. Очевидный недостаток: такое правило будет означать изменение одного файла .c, в результате чего все файлы будут перекомпилированы (в основном сводит на нет существенные преимущества использования make).


РЕДАКТИРОВАТЬ: Я немного расширю комментарий, чтобы прояснить. Похоже, что вам нужно: в случае, если файл .c поврежден и сборка не удалась, удалите старый файл .o. Это совершенно не совсем так, как работает .DELETE_ON_ERROR. Если файл .o уже обновлен, а затем правило не выполнено, он удаляет его ( «удаляет цель правила, если оно изменилось» ), но в случае упомянутой синтаксической проблемы, компилятор потерпит неудачу, прежде чем он создаст файл .o.

Итак, если, например, вы обновили свое правило (шаблон) для компиляции, чтобы оно сначала touch присваивало (эффективно обновляет метку времени) файл .o, а затем пытается компилировать. После сбоя вызова компилятора и правила make сочтет, что цель поврежденного корня была обновлена, и удалит ее. В качестве альтернативы вы также можете изменить правило, чтобы сначала попытаться rm получить ожидаемый файл «.o», и в этом случае вам на самом деле не нужно будет использовать .DELETE_ON_ERROR (и если нет изменений в соответствующих источниках, правило делает не привыкать, так что на самом деле это не так страшно, как кажется). В любом случае это не совсем чисто, но ведет к поведению, которое, как я понимаю, вы описываете.

12
задан Jay Bazuzi 28 December 2008 в 20:47
поделиться

8 ответов

Я прошел фазу раздавания T[], и короче говоря, это - боль в задней стороне. IEnumerable<T> намного лучше

Однако интересно, с последней оценкой универсального типа IEnumerable, если это - хорошая идея. Имеет больше смысла использовать T [] универсальный тип? IList? Или что-то еще

Последняя оценка состоит точно в том почему IEnumerable так хорошо. Вот рабочий процесс в качестве примера:

IEnumerable<string> files = FindFileNames();
IEnumerable<string> matched = files.Where( f => f.EndsWith(".txt") );
IEnumerable<string> contents = matched.Select( f => File.ReadAllText(f) );
bool foundContents = contents.Any( s => s.Contains("orion") );

Для нетерпеливого это получает список имен файлов, отфильтровывает .txt файлы, затем устанавливает foundContents к истинному, если какой-либо из текстовых файлов содержит слово orion.

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

Если бы Вы написали этот точно тот же самый код с помощью массивов, то Вы закончили бы тем, что загрузили все содержание файла впереди, и только затем (если Вы уезжаете, любая RAM) был бы любой из них быть просканированным. Надо надеяться, это понимает через приблизительно, почему ленивые списки так хороши.

Одна вещь, которая не стала обращенной, хотя, кажется, проблема потокобезопасности. Если, например, Вы берете IEnumerable<T> аргумент методу и это перечисляется в другом потоке, затем когда тот поток пытается получить доступ к нему, результаты могли бы отличаться, чем те, которые были предназначены, чтобы быть переданными в. Что еще хуже, пытаясь перечислить IEnumerable<T> дважды - я верю, выдает исключение. Разве мы не должны стремиться сделать наши методы ориентированными на многопотоковое исполнение?

Потокобезопасность является гигантским отвлекающим маневром здесь.

При использовании массива, а не счетного похоже, что это должно быть более безопасно, но это не. Большую часть времени, когда люди возвращаемые массивы объектов, они создают новый массив и затем помещают старые объекты в него. Если Вы возвращаете тот массив, то те исходные объекты могут затем быть изменены, и Вы заканчиваете с точно видом проблем многопоточности, которых Вы стараетесь избегать.

Частичное решение не состоит в том, чтобы возвратить массив исходных объектов, но массив новых или клонированных объектов, таким образом, другие потоки не могут получить доступ к исходным. Это полезно, однако нет никакой причины IEnumerable решение не может также сделать этого. Каждый не более ориентирован на многопотоковое исполнение, чем другой.

14
ответ дан 2 December 2019 в 04:09
поделиться

По большей части Вы не должны передавать массивы (T[]) на public интерфейсы. Посмотрите сообщение в блоге Eric Lippert "Массивы, которые рассматривают несколько вредными" для деталей.

Исключением являются методы, которые берут a params массив, поскольку это должен быть массив. (Надо надеяться, будущая версия позволит"params IList<s> foo"в сигнатурах методов.)

Для internal участники, сделайте то, что Вы любите; Вы управляете обеими сторонами интерфейса.

11
ответ дан 2 December 2019 в 04:09
поделиться

Выходные типы должны быть максимально определенными, ввести типы, должно быть максимально свободным.

Так возврат T[], но возьмите IEnumerable<T> как введено.

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

4
ответ дан 2 December 2019 в 04:09
поделиться

"Разве мы не должны стремиться сделать наши методы ориентированными на многопотоковое исполнение?"

Мы должны быть преднамеренными о способностях нашего кода.

Просто, потому что метод не ориентирован на многопотоковое исполнение, не означает, что это - отказ, просто что необходимо знать, что факт, прежде чем Вы попытаетесь использовать его в многопоточной программе. Вы стремитесь сделать Main() ориентированный на многопотоковое исполнение? Потокобезопасность имеет значение в однопоточной программе?

Так или иначе я не думаю, что действительно имеет смысл говорить, что "Нечто ориентировано на многопотоковое исполнение", только, что "Нечто имеет следующие характеристики, которые важны в многопоточном контексте".

"при попытке перечислить IEnumerable дважды - я верю, выдает исключение".

Это было бы плохо. К счастью можно протестировать его, вместо того, чтобы развернуть FUD.

Я думаю, что Вы спрашиваете, "разве я не должен возвращать копию своего набора вместо ссылки на внутренний членский набор, так, чтобы вызывающие стороны не изменяли мои данные"? И ответ является неполным, "возможно". Существует много способов приблизиться к этой проблеме.

4
ответ дан 2 December 2019 в 04:09
поделиться

Реализовывая интерпретируемый язык программирования, записанный в C#, я также должен был выбрать между возвращаемым значением текстового объекта [] и Списком для подобных Linq функций как карта и фильтр. В конце я выбираю ленивый вариант подобного Шепелявости списка. Существует конструктор списка, который имеет аргумент IEnumerable: z = новый LazyList (...). Каждый раз, когда программа относится к одному из свойств LazyList (голова, хвост или isempty), счетное оценено всего для одного шага (голова, и isempty становятся определенными, хвост становится другим ленивым списком). Преимущество lazylist по IEnumerable состоит в том, что первый позволяет рекурсивные алгоритмы и не делает (имейте к), пострадайте от нескольких оценок.

0
ответ дан 2 December 2019 в 04:09
поделиться

Я выбрал бы между IList и IEnumerable и не рассмотрю массива вообще.

Обратите внимание, что IEnumerable не имеет свойства Count или Length за исключением дополнительного метода, в то время как массив и IList делают. Так, я основывал бы свое решение об этом:

Если возвращаемое значение знало число элементов-> IList или массив (если Вы должны), иначе IEnumberable

1
ответ дан 2 December 2019 в 04:09
поделиться

Однако интересно, с последней оценкой универсального типа IEnumerable, если это - хорошая идея. Имеет больше смысла использовать T [] универсальный тип? IList? Или что-то еще?

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

IEnumerable T только говорит: "Я могу выполнить итерации по этому набору путем вызова GetEnumerator. Когда я делаю это, каждый элемент может быть назван T." IEnumerable T ничего не говорит об отложенных вычислениях.

Рассмотрите эти три объявления:

//someInts will be evaluated lazily
IEnumerable<int> someInts = Enumerable.Range(0, 100);

//oddInts will be evaluated lazily
IEnumerable<int> oddInts = someInts.Where(i => i % 2 == 1);

//evenInts will be evaluated eagerly
IEnumerable<int> evenInts = someInts.Except(oddInts).ToList();

Даже при том, что ссылочный тип всех трех является IEnumerable интервала, только evenInts нетерпеливо оценен (потому что ToList перечисляет свою цель и возвращает экземпляр Списка).

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

public IEnumerable<int> GetInts()
{
  ...
  return someInts.ToList()
}
2
ответ дан 2 December 2019 в 04:09
поделиться

Можно всегда делать это для потокобезопасности, она будет, вероятно, работать лучше в большом количестве экземпляров, поскольку нет никакой блокировки набора (это - копия). Обратите внимание, что это означает, что Вы делаете LINQ или foreach не на наборе, а на участнике.

  public IEnumerable<ISubscription> this[string topic] {
            get  { 
                rwlock.EnterReadLock();
                try {
                    return subscriptionsByTopic[GetTopic(topic)].ToArray<ISubscription>();              
                    //thread safe
                }
                finally {
                    rwlock.ExitReadLock();
                }
            }
        }

Также не используйте IEnumerable для приложений уровня SOA / много приложений уровня, поскольку Вы наклоняетесь, сериализируют интерфейсы (не представляя много боли).WCF работы лучше со Списком.

0
ответ дан 2 December 2019 в 04:09
поделиться
Другие вопросы по тегам:

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