Есть ли оправдание для создания исключения из неявного преобразования?

Из MSDN:

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

Хотя я не возражаю ни по одному конкретному пункту и согласен с тем, что все это очень хорошо, есть ли когда-нибудь причина, достаточно веская, чтобы оправдать нарушение части о неявных преобразованиях, а не об исключении?

Конкретный случай, который у меня есть передо мной, это тот, где:

  1. У меня есть функция, которая возвращает пользовательский объект коллекции (назовем его FooCollection).
  2. Эта функция может возвращать коллекции с одним элементом, и из исходного кода можно определить, произойдет это или нет. (под этим я подразумеваю только вызов функции, а не саму функцию)
  3. Если это произойдет, то с вероятностью 99,9 % пользователю нужен этот единственный элемент, а не коллекция с одним элементом.

Теперь я думаю, не включить ли неявное преобразование из FooCollection => Foo, чтобы скрыть эту маленькую деталь реализации, но это преобразование будет работать только в том случае, если в коллекции есть один элемент.

Можно ли выбросить Exception в этом случае? Или вместо этого я должен использовать явное приведение? Любые другие идеи о том, как я мог бы справиться с этим (нет, из-за деталей реализации я не могу просто использовать две функции)?

EDIT: я считаю достойным отметить, что FooCollection не реализует никаких интерфейсов и фактически не расширяет Collection, как может подразумевать название, поэтому ответы на основе LINQ бесполезны. Кроме того, хотя в коллекции реализован числовой индекс, это не самый интуитивный способ работы с коллекцией, поскольку он в основном опирается на именованный индекс.


person Matthew Scharley    schedule 08.10.2009    source источник


Ответы (4)


Я придерживаюсь рекомендаций: вы никогда не должны бросать вызовы из неявного преобразования.

Я определенно не стал бы предоставлять неявное преобразование в этом случае. Даже идея явного приведения кажется мне неправильной: Foo x = (Foo)fooCollection просто не кажется правильным.

Почему бы вам просто не позволить вызывающему коду побеспокоиться о преобразовании из FooCollection в Foo? Код был бы намного более интуитивным для всех:

Foo a = fooCollection[0];
Foo b = fooCollection.First();
Foo c = fooCollection.FirstOrDefault();
// etc
person LukeH    schedule 08.10.2009
comment
Foo x = (Foo)fooCollection тоже не будет "правильным" (хотя и скомпилируется). Foo x = (Foo)fooCollection.x() будет так, как это будет использоваться. - person Matthew Scharley; 08.10.2009
comment
x() — функция, которая возвращает подмножество из 1 элемента из fooCollection. - person Matthew Scharley; 08.10.2009
comment
В любом случае, вы все еще пытаетесь преобразовать некоторую коллекцию Foo в Foo, что мне кажется странным. Почему бы просто не позволить вызывающему коду сделать Foo x = fooCollection.x()[0] или что-то подобное? - person LukeH; 08.10.2009
comment
Среди прочего, он чувствует себя грязным. Но создание исключения из неявного преобразования тоже не кажется правильным. Ну что ж, я полагаю, ТАК в любом случае говорил :) - person Matthew Scharley; 08.10.2009
comment
Я бы сказал немного по-другому: любой экземпляр объекта, который когда-либо выбрасывал при выполнении неявного преобразования, следует считать сломанным. Если, например. если экземпляр объекта, не являющийся потокобезопасным, повреждается в результате одновременных действий нескольких потоков, для этого экземпляра может быть уместно генерировать исключение в том, что обычно было бы расширением приведения типов, но только поскольку экземпляр поврежден. - person supercat; 26.09.2012

Это явно не нормально. Никогда не используйте исключения для реализации логики!

Вы можете использовать Linq-Statement FooCollection.FirstOrDefault(), который даст null или первый элемент.

person Thomas Weller    schedule 08.10.2009

Актерский состав должен быть явным. Будет очень странно иметь возможность присвоить FooCollection Foo хотя бы без приведения.

При этом броски ИМХО - не лучший способ сделать это. Даже если вы откажетесь от этого, я буду использовать функцию, потому что даже если вы не контролируете реализацию этих классов, вы можете по крайней мере добавить методы расширения, такие как Foo ToFoo(this FooCollection collection), чтобы выполнить работу.

person Julien Lebosquain    schedule 08.10.2009
comment
На самом деле я на 50% уверен, что это тоже не сработает, поскольку это в контексте С# 4.0 и динамических типов. - person Matthew Scharley; 08.10.2009
comment
т.е. Я не уверен, учитывает ли динамическая маршрутизация методы расширения или нет. Черт возьми, теперь у меня есть кое-что еще, что я хочу попробовать! - person Matthew Scharley; 08.10.2009

Неявная версия копирования, которая работает только в том случае, если в коллекции есть только 1 элемент? То есть, в большинстве случаев это не работает?

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

person Maximilian Mayerl    schedule 08.10.2009