Как стать автором
Обновить

Комментарии 35

Немного поправлю — это не особенность C#, но особенность .NET. И весьма логичная.
Не то чтобы я хочу поспорить, но интересно, почему вы считаете это логичнее, чем возвращать -1?
Ответ, по-моему мнению, был дан в статье…

1) языки программирования «считающие» начало «стандартных» массивов с 1 -> выдавать 0 при поиске очень удобно (как и с массивом C#)

2) мы имеем возможность создать массив, нумеруя его, скажем с -3 до 5 -> -1 означает конкретный индекс, а не отсутствие рез-та.

Да, пока писал, в голову пришла мысль, что де, можно было задать в indexOf смещение массива (0 — первый элемент, 1- второй) вне зав-ти от его индексации. Скажем, для приведенного во втором примере массива indexOf == 0 означал бы всего лишь -3 индекс. Но в этом случае опять бы возникла путаница и новые открытия, что де, платформа .NET не возвращает индекс, а возвращает смещение относительно начала. И снова интриги, расследования…
НЛО прилетело и опубликовало эту надпись здесь
Вероятно, самокрут имел ввиду Nullable в качестве результата для метода indexof. И таки да, я с ним согласен — Nullable тут удобнее ибо есть Null.
Использовать nullable, где будем проводить проверки на наличие значений и доставать их? Думаю, что это не очень изящное решение.
В этом случае лучше воспользоваться идеей C# и назвать это «нештатной» ситуацией и выдавать Exception наподобие OutOfRange. Но опять же это усложнение совершенно простой с точки зрения решаемой задачи функции.
Дело в том, что Nullable — ссылочный тип.
Поэтому, если кто-то пишет оптимальный парсер больших данных, отказываясь загрязнения кучи вырезкой подстрок (Substring и т.п.), работает только с одной большой строкой и индексами на ней, ему придётся отказаться и от такого IndexOf с nullable, сделав свой IndexOf, возвращающий (-1) при неудаче.
Nullable — это не ссылочный тип.
А, точно! Спасибо за поправку
Подошло бы тогда и bool IndexOf(T value, out T index)
В принципе, да, как у IDictionary.TryGetValue.
С ним только такое неудобство, что громоздко использовать в linq-выражениях.
Поэтому приходится писать обёртку типа V GetValueByKey(K key, V defaultValueIfNotFound).
Дополню топик ссылкой на исходный код этого метода: http://www.dotnetframework.org/, строка 846
Наш метод
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static int IndexOf(Array array, Object value) {
	if (array==null)
		throw new ArgumentNullException("array");
 	int lb = array.GetLowerBound(0);
	return IndexOf(array, value, lb, array.Length);
}

Вызывает другой перегруженный метод, в конце которого мы видим (часть кода опустил):
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static int IndexOf(Array array, Object value, int startIndex, int count) {
           ...
            // Return one less than the lower bound of the array.  This way,
            // for arrays with a lower bound of -1 we will not return -1 when the
            // item was not found.  And for SZArrays (the vast majority), -1 still
            // works for them.
            return lb-1;
}
Любопытно, что для generic-массивов нет такой особенности:

public static int IndexOf(T[] array, T value)
Member of System.Array

Returns:
The zero-based index of the first occurrence of value within the entire array, if found; otherwise, –1.
И у методов IndexOf где первый параметр типизированный массив IndexOf(T[] ...)
НЛО прилетело и опубликовало эту надпись здесь
Нет, не дженерик, откройте Array класс, и у него есть методы принимающие T[] в IndexOf и там четко написано, что возвращается -1, а в методах принимающих Array первым параметром, то, что я описал в статье.
Похоже, я всех запутал.

Тип Int32[ ], который я ошибочно назвал generic-массивом, на самом деле не является Generic-типом (по мнению reflection, IsGenericType=false) и наследуется от System.Array. Сам по себе экземпляр типа Array не создаётся без указания типа элементов. Поэтому «не-generic массивов» (объектов типа Array) не бывает.
Дело в том, что T[] попадает под описание " type [ n ] " и соответственно тип такого массива — ELEMENT_TYPE_SZARRAY, что значит что это одномерный массив с индексацией от нуля.
Вообще автор откопал довольно странные массивы.
Они не совместимы с обычными System.Int32[] или System.Int32[,]
Их тип называет себя System.Int32[*]
Даже оператор индексации array[i]=a с ними не работает, нужно использовать array.SetValue(a, i);

Поэтому вряд ли кто-то столнётся с ними, ещё менее вероятно, что с IndexOf на них.
* в типе говорит о том, что CLR знает, что это не SZArray (для многомерных звёздочка не пишется, т.к. она была бы везде и не информативна).
Массивы, которые задаются как T[] — всегда индексируются с нуля, т.к. на этом основано куча оптимизаций (для SZArray в CLR есть отдельные команды, которые позволяют с такими массивами эффективнее работать, ибо нет смещения).

Если посмотреть например на сортировку массивов, то в Array.Sort реализованы методы, которые сортируют как T[], так и Array.
Array это все масивы [ ] во всем NET
Возмите тип у int[] и посмотрите какой у него базовый класс. Базовым классом будет аккурат Array.
Он не типизирован, поэтому у него нет индексаторов а доступ только через методы, причем всегда с боксингом для валю типов. Т.е. я откопал довольно не странный массив =)
Multidimensional Array System.Int32[*], создаваемый через Array.CreateInstance, странный, потому что для него нет поддержки в языке, сравнимой с обычными линейными, двумерными и N-мерными массивами (создание и инициализация через new, индексатор).

Array — это абстрактный класс, поэтому факт, что System.Int32[*] — потомок Array, ничего особенно не значит (поддержка контракта, не более)

То есть, не занимаясь интеграцией с чужими языками, вероятность встретить такой массив ничтожна, поэтому я его назвал странным.
У нас много интроинспекции в инструментарии, и со всеми масивами [ ] работа производится через Array.
А всё-таки, массивы типа T[*] живут в проекте? ;)
Если есть поле и у него тип это массив [ ] каких то типов заранее неизвестных, то через рефлексию создается как раз Array нужного типа, заполняется через методы и присваивается полю. Для этого Array и является базовым. чтобы можно было работать с масивами имея тип как Type.
Если есть поле и у него тип это массив [ ]

Значит, это обычный массив. Полю типа int[] нельзя присвоить массив System.Int32[*] — несовпадение типов возникнет.

Другие перегруженные методы, например
Array.CreateInstance(typeof(int), n1),
Array.CreateInstance(typeof(int), n1, n2),
Array.CreateInstance(typeof(int), new[]{n1, n2})
(т.е. без указания массива LowerBounds), создают обычные массивы System.Int32[] и System.Int32[,]
Тогда да, получается что с таким типом можно столкнутся в редких случаях.
Конкретно для пример из статьи, метод может быть такой

Array GetArray() { return new int[] { }; }
А может и такой:
object GetArray() { return new int[] { }; }

это не значит, что так надо писать.
НЛО прилетело и опубликовало эту надпись здесь
Я предполагаю еще писать, под общим названием, и внутри обновлять ссылки. Извиняюсь, если ввел в заблуждение.
Ожидал, что здесь будет более одного интересного «момента»…
Очень жаль, что некоторые воспринимают такие статьи как “капитанство” отбивая всякое желание к написанию, но несмотря на это, в комментариях, порой, всплывает много полезной информации.


Участвуете в конкурсе на самую короткую статью? 50 строк вместе с кодом, круто. На stackoverflow вопросы длиннее.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории