2011-02-02 2 views
4

Мой вопрос об именах, дизайне и вариантах реализации. Я вижу, что я собираюсь в двух разных направлениях: как решить проблему, и мне интересно узнать, как справиться с проблемой могут другие люди, которые могли столкнуться с подобными проблемами. Это часть эстетики, функция части.Переопределить ToString или предоставить метод расширения имени не для ToString для интерфейса?

Немного фона для кода ... Я создал тип, называемый ISlice<T>, который предоставляет ссылку в раздел источника элементов, который может быть коллекцией (например, массивом, списком) или строкой. Основная поддержка исходит из нескольких классов реализации, которые поддерживают быструю индексацию с использованием маркеров Begin и End для среза, чтобы получить элемент из исходного источника. Цель состоит в том, чтобы обеспечить возможности среза, аналогичные тому, что предоставляет язык Go при использовании индексации стиля Python (т. Е. Поддерживаются как положительные, так и отрицательные индексы).

Чтобы сделать создание фрагментов (экземпляров ISlice<T>) проще и более «бегло», я создал набор методов расширения. Например:

static public ISlice<T> Slice<T>(this IList<T> source, int begin, int end) 
{ 
    return new ListSlice<T>(source, begin, end); 
} 

static public ISlice<char> Slice(this string source, int begin, int end) 
{ 
    return new StringSlice(source, begin, end); 
} 

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

Эти подпрограммы хорошо работают и позволяют легко нарезать коллекцию или строку. Мне также нужен способ взять срез и создать его копию как массив, список или строку. Вот где вещи становятся «интересными». Первоначально я думал, что мне нужно будет создать методы расширения ToArray, ToList, но потом вспомнил, что варианты LINQ выполняют оптимизацию, если ваша коллекция реализует ICollection<T>. В моем случае, ISlice<T>, наследует от него, хотя, по большому счету, к моему огорчению, поскольку мне не нравится бросать NotSupportedExceptions из таких методов, как Add. Несмотря ни на что, я получаю их бесплатно. Отлично.

Как преобразовать обратно в строку, поскольку нет встроенной поддержки для преобразования IEnumerable<char> обратно в строку? Ближайшая вещь, которую я нашел, является одной из строк. Перегрузка Concat, но она не будет обрабатывать символы как можно эффективнее. Столь же важным с точки зрения дизайна является то, что он не выпрыгивает, как рутина «преобразования».

Первой мыслью было создать метод расширения ToString, но это не работает, поскольку ToString - это метод экземпляра, который означает, что он перехватывает методы расширения и никогда не будет вызван. Я мог бы переопределить ToString, но поведение было бы непоследовательным, так как ListSlice<T> понадобится для специального случая его ToString для времен, где T является символом. Мне это не нравится, поскольку ToString даст что-то полезное, когда параметр типа является символом, но имя класса в других случаях. Кроме того, если в будущем будут созданы другие типы срезов, мне нужно будет создать общий базовый класс для обеспечения того же поведения, или каждый класс должен будет выполнить эту же проверку. Метод расширения на интерфейсе будет обрабатывать гораздо более элегантно.

Метод расширения приводит меня к вопросу об именовании. Очевидным является использование ToString, но, как было сказано ранее, это запрещено. Я мог бы назвать это чем-то другим, но что? ToNewString? NewString? CreateString? Что-то в методе To-family позволяло ему входить в подпрограммы ToArray/ToList, но ToNewString выступает как «нечетный», если смотреть в редакторе intellisense и в коде. NewString/CreateString не так понятны, как вам следовало бы знать, чтобы их искать. Он не соответствует шаблону «метод преобразования», который предоставляет метод To-family.

Перейти с переопределением ToString и принять противоречивое поведение, жестко закодированное в реализацию ListSlice<T> и другие реализации? Идите с более гибким, но потенциально более слабо названным маршрутом метода расширения? Есть ли третий вариант, который я не рассматривал?

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

Update

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

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

Уловка заключается в том, что в конце обработки возникает желание превратить обработанные данные на основе среза в более дружественный тип API и .NET, например списки, массивы и строки. Это упрощает передачу данных в другие API. Он также позволяет отбрасывать срезы, таким образом, также большие данные устанавливают срезы, на которые делается ссылка.

+1

FYI, добавляя символы обратного тика, позволит вам говорить такие вещи, как 'IEnumerable ' – StriplingWarrior

+0

Спасибо за то, что на него набросились. Пропустил этот связанный ввод кода. –

ответ

5

Вы бы даже подумали о том, что ToString дает полезный результат для коллекции/перечисляемого типа? Разве это нарушит принцип наименьшего удивления?

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

Как для этого:

Что о преобразовании обратно в строку, как нет встроенной поддержки для преобразования IEnumerable> полукокса < легко обратно в строку?

Лично я бы просто использовать string constructor taking an array:

string result = new string(mySlice.ToArray()); 

Это очевидно, понял, и ожидал - я ожидаю, чтобы создать новую строку, передавая объект в конструктор.

+0

Я подумал об этом, но поскольку целью реализации моего среза является минимизация распределений, приведенное выше приведет к двум копиям данных среза из моего текущего понимания того, как работает конструктор строк. Одна копия в массив, а затем строка делает защитную копию переданного массива. Это возможность, хотя, по крайней мере, для небольших рабочих наборов. Обновлено мое сообщение, чтобы больше узнать о предположениях, связанных с моей реализацией среза. –

+0

@James: Как вы планируете реализовать свой метод ToString() по-другому? Вам нужно будет сгенерировать строку - в какой-то момент она скопирует данные ... –

+0

Я собирался построить строку с помощью StringBuilder, а затем ToString(). Было ли сравнение производительности между этим решением сегодня утром и подход array/constructor с неудовлетворительными результатами. У меня создалось впечатление, что SB работал над копированием на запись. Я был прав, что это _did_. Выяснил [поведение изменено] (http://stackoverflow.com/questions/4166155/does-stringbuilder-become-immutable-after-a-call-to-tostring) в 4.0. Ваш подход - это не только правильный идиоматический подход, но и лучший подход в этом сценарии. Благодарю. –

1

Возможно, причиной вашей головоломки является тот факт, что вы лечите string как ICollection<char>. Вы не указали подробную информацию о проблеме, которую пытаетесь решить, но, возможно, это неправильное предположение.

Это правда, что строка IEnumerable<char>. Но, как вы заметили, если предположить, что прямое сопоставление с коллекцией символов создает проблемы. Строки просто слишком «особенные» в рамках.

Посмотрите на него с другого конца, было бы очевидно, что разница между ISlice<char> и ISlice<byte> заключается в том, что вы можете связать первую строку с строкой? Будет ли какая-то конкатенатная операция над последним, что имеет смысл? Как насчет ISlice<string>? Разве я не могу объединить их?

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

+0

Обновлено мое сообщение больше о моих предположениях в моей реализации. Но вы поднимаете еще одну проблему, о которой я не говорил о идее создания операции типа concat. Возможное, будущее направление, которое я рассматривал, - это «композиционная» операция, которая позволяет вам брать два или более фрагментов и строить из них более крупный блок. Это, по сути, секция concat, которая даст больший «срез». Это отличается от того, каким будет преобразование в строку, которая делает копию данных символа репликой среза, но в формате .NET. –

Смежные вопросы