Это действительно очень меня побуждает, поэтому я надеюсь, что кто-нибудь сможет дать мне разумное обоснование того, почему все так, как есть.
NotImplementedException. Вы меня дергаете за ногу, да?
Нет, я не собираюсь прибегать к дешевке, говоря: «Погоди, метод реализован - он выдает исключение NotImplementedException». Да, верно, вы должны реализовать метод для генерирования NotImplementedException (в отличие от вызова чистой виртуальной функции в C++ - теперь это имеет смысл!). Хотя это чертовски смешно, в моей голове есть более серьезная проблема.
Мне просто интересно, как при наличии NotImplementedException можно что-нибудь сделать с .Net? Ожидается ли, что каждый вызов абстрактного метода будет заключаться в блокировку try catch для защиты от методов, которые могут быть не реализованы? Если вы поймаете такое исключение, что, черт возьми, вы должны с ним делать ??
Я не вижу способа проверить, действительно ли метод реализован, не вызывая его. Поскольку его вызов может иметь побочные эффекты, я не могу выполнить все проверки заранее, а затем запустить свой алгоритм. Мне нужно запустить свой алгоритм, поймать NotImplementedExceptions и каким-то образом откатить мое приложение до некоторого нормального состояния.
Это безумие. Безумный. Безумный. Итак, вопрос: Почему существует NotImplementedException?
В качестве упреждающего удара я не хочу, чтобы кто-либо отвечал, «потому что дизайнерам необходимо поместить это в автоматически сгенерированный код». Это ужасно. Я бы предпочел, чтобы автоматически сгенерированный код не компилировался, пока вы не предоставите реализацию. Например, автоматически созданная реализация может быть throw NotImplementedException; где NotImplementedException не определено!
Кто-нибудь когда-нибудь ловил и обрабатывал NotImplementedException? Вы когда-нибудь оставляли NotImplementedException в своем коде? Если да, то представляло ли это бомбу замедленного действия (то есть вы случайно оставили ее там) или недостаток конструкции (метод не следует реализовывать и никогда не будет вызван)?
Я также очень подозрительно отношусь к NotSupportedException ... Не поддерживается? Что за? Если он не поддерживается, почему он является частью вашего интерфейса? Может ли кто-нибудь в Microsoft указать неправильное наследование? Но я мог бы задать еще один вопрос по этому поводу, если я не получу слишком много оскорблений для этого.
Дополнительная информация:
Этот - интересное чтение на эту тему.
Кажется, существует полное согласие с Брэд Абрамс, что «NotImplementedException предназначен для функциональности, которая еще не реализована, но на самом деле должна (и будет). Что-то вроде того, что вы могли бы использовать в Начало при создании класса, получите все методы там выбрасывая NotImplementedException, а затем удалите их с помощью реального кода ... "
Комментарии из Джаред Парсонс очень слабые, и их, вероятно, следует игнорировать: NotImplementedException: генерировать это исключение, когда тип не реализует метод по какой-либо другой причине.
MSDN еще слабее по этому вопросу, просто заявляя, что «исключение, которое выдается, когда запрошенный метод или операция не реализованы».
Получу ли я значок вместе с тегом Rant?
Могу только догадываться, что вам никогда не приходилось реализовывать библиотеку с заданным интерфейсом ...
Хорошая догадка, но ты ошибаешься. Могу только догадываться, что вы не понимаете вопроса.
@Daniel - есть и более этимологический ответ. В Win32 C API было определено возвращаемое значение E_NOTIMPL. Каждое такое возвращаемое значение было сопоставлено с исключением .Net.
@Heath - значит, вы считаете, что NotImplementedException существует как системное исключение, из-за грехов нашего прошлого?





А как насчет прототипов или незавершенных проектов?
Я не думаю, что использование исключения - плохая идея (хотя в этом случае я использую окно сообщения).
Итак, он никогда не должен попадать в производственный код? Тогда почему это часть ядра .NET? Если вам нужна эта концепция, просто «бросьте новый объект ();» с комментарием о том, что метод ожидает реализации.
Кстати, я даю вам +1, потому что вы предпочитаете использовать что-то другое, а не исключение. Лично я бы добавил assert (false) и фиктивное возвращаемое значение, предполагая, что мне нужен код для компиляции.
@ Дэниел Пол: но, конечно, это опаснее, потому что утверждения не будут скомпилированы в код выпуска? ...
@ Дэниел Пол: на самом деле ты пробудил мою память, и я могу вспомнить ситуацию, когда именно это произошло ...
Если вы программируете по контракту, тестируете свою систему и доказываете правильность, утверждения - это просто денди. Альтернатива просто кажется крылом и молитвой. Я - активный сторонник утверждений и правильного оформления вашего дизайна. Не должно быть удачи, которая заставляет вашу систему работать.
Основное использование исключения NotImplementedException - это сгенерированный код-заглушка: таким образом вы не забудете его реализовать !! Например, Visual Studio будет явно реализовывать методы / свойства интерфейса, при этом тело генерирует исключение NotImplementedException.
@ Дэниел Пол: выброшенное исключение!
@Mitch: Предполагая, что вы вызываете код. Не все используют инструменты покрытия кода, так что это можно легко упустить.
Лучше, чтобы метод генерировал исключение, чем просто «ничего не делал», потому что он не реализован. Таким образом, у вас будет шанс найти и исправить проблему.
@ Дэниел Пол: и утверждение страдает той же проблемой.
@Mitch: утверждения - это инструмент для проверки условий, которые не могут (по замыслу) возникать в вашей системе - любое сработавшее утверждение указывает на ошибку программирования. Исключения указывают на обстоятельства времени выполнения, которые (не обязательно) указывают на ошибки программирования. Яблоки и апельсины.
Что вы можете сделать, так это найти во всем коде NotImplementedException и убедиться, что вы не нашли ноль при выпуске.
Он предназначен для поддержки довольно распространенного варианта использования - рабочего, но только частично завершенного API. Скажем, я хочу, чтобы разработчики протестировали и оценили мой API - WashDishes() работает, по крайней мере, на моей машине, но я еще не успел написать код DryDishes(), не говоря уже о PutAwayDishes(). Вместо того, чтобы молча терпеть неудачу или выдавать какое-то загадочное сообщение об ошибке, я могу совершенно ясно понять, почему DryDishes() не работает - я еще не реализовал его.
Его родственное исключение NotSupportedException имеет смысл в основном для моделей провайдеров. Многие посудомоечные машины имеют функцию сушки, поэтому она входит в интерфейс, но моя дисконтная посудомоечная машина не поддерживает ее. Я могу сообщить об этом через NotSupportedException
Но почему это системное исключение? Почему вы не можете создать собственное исключение? Почему MS сделала эту часть ядра .NET? Что касается посудомоечной машины без функции сушки - почему у нее есть метод сушки? Неправильное наследование.
Почему вы предпочли бы написать свое исключение для такой ситуации?
потому что тогда я могу полностью удалить его из сборок выпуска, чтобы случайно не оставить его в коде.
Но у вас все еще есть свобода делать это; вы не обязаны использовать NotSupportedException. Тем не менее, ситуация возникает настолько часто, что для нее в Framework существует стандартный класс, как и положено.
Исправление: NotImplementedException, хотя NotSupportedException также применимо.
У вас нет такой свободы, когда все стандартные инструменты автоматически генерируют код с заглушками, которые генерируют NotImplementedException! Вы также застряли, если его используют сторонние сборки.
Вы можете использовать «поиск использования» в своей среде IDE для исключения исключений. Вот почему
@krosenvold: Вот почему? Функция IDE, определяющая дизайн платформы? Звучит в обратном направлении для меня.
@Daniel Paull: NotImplementedException является частью ядра, потому что люди ленивы, если бы его не было, они бы использовали Exception, NotSupportedException, InvalidOperationException или какое-либо другое исключение.
Это также часть ядра, потому что это полезно для генераторов кода, которые требуют, чтобы вы что-то реализовали (например, автоматическая реализация интерфейса визуальных студий). Кроме того, если он случайно попадает в производство, это означает, что вы случайно забыли реализовать что-то, что вы не тестировали по этому пути кода, и что у вас есть ошибка.
Re NotImplementedException - служит для нескольких целей; он обеспечивает единственное исключение, которое (например) ваши модульные тесты могут заблокировать на случай незавершенной работы. Но кроме того, он действительно делает то, о чем говорится: этого просто нет (пока). Например, «моно» бросает это повсюду для методов, которые существуют в библиотеках MS, но еще не написаны.
Re NotSupportedException - не все доступно. Например, многие интерфейсы поддерживают пару «можно ли это сделать?». / "сделай это". Если "ты сможешь это сделать?" возвращает false, вполне разумно для "do this" выбросить NotSupportedException. Примерами могут быть IBindingList.SupportsSearching / IBindingList.Find() и т. д.
Итак, должно быть исключение Mone.NotImplementedException - почему оно является частью ядра .NET? Если вы избегаете неправильного наследования, объект будет тем, чем он является, так что "вы можете это сделать?" / "сделать это" избыточны.
Это определенно пахнет неправильной наследственностью.
Большинство разработчиков в Microsoft знакомы с шаблонами проектирования, в которых подходит NotImplementedException. На самом деле это довольно распространенное явление.
Хорошим примером является Составной узор, где многие объекты можно рассматривать как один экземпляр объекта. Компонент используется как базовый абстрактный класс для (правильно) унаследованных конечных классов. Например, классы File и Directory могут наследовать от одного и того же абстрактного базового класса, поскольку они очень похожи по типам. Таким образом, их можно рассматривать как единый объект (что имеет смысл, если подумать о файлах и каталогах - например, в Unix все является файлом).
Итак, в этом примере будет метод GetFiles () для класса Directory, однако класс File не будет реализовывать этот метод, потому что это не имеет смысла. Вместо этого вы получаете NotImplementedException, потому что у файла нет дочерних элементов, как у каталога.
Обратите внимание, что это не ограничивается .NET - вы встретите этот шаблон во многих объектно-ориентированных языках и платформах.
Почему File не возвращает пустой список при запросе его дочерних элементов через абстрактный интерфейс?
Как разработчик приложений, я бы точно этого не ожидал. Как насчет метода AddFile (), который реализуется классом Directory - чего бы вы ожидали, если бы вы вызывали его в классе File? Я ожидал исключения. Такое поведение должно быть последовательным в хорошем API.
Файл - это не каталог - неправильное наследование. Изменяемые абстрактные интерфейсы необходимо разрабатывать тщательно.
-1, если подклассы не могут реализовать GetFiles, тогда иерархия неверна, en.wikipedia.org/wiki/Liskov_substitution_principle
В моем примере файл не наследуется от каталога; вы неправильно это прочитали. Извините, это не нарушает принцип замены Лискова. Базовый абстрактный класс компонента выдает исключение. Соответствующая реализация этих методов зависит от подклассов.
Возможно, это плохая аналогия, но точка зрения Джарвиса верна. Я предпочитаю просматривать составные методы, которые не могут быть реализованы, но не выполняются. Вы часто будете видеть это с виртуальными методами.
Это противоречит распространенному мнению в других ответах, что ни один производственный код никогда не должен генерировать NotImplementedExceptions. Мне трудно понять, что означает «можно сделать, но не может». Если вы сказали: «Пойдет, но пока нет», то я снова счастлив - см. Принятый ответ.
Microsoft очень широко использует NotImplementedException в объектных моделях Microsoft Office. Как программиста PowerPoint, это иногда сводит меня с ума! Например, когда я пытаюсь вызвать определенные методы или свойства Shape или AnimationSettings, они могут работать нормально или могут вызывать NotImplementedException в зависимости от ситуации. Я бы хотел, чтобы был способ проверить метод на NotImplementedException, не вызывая его.
NotImplementedException выбрасывается для некоторого метода .NET (см. Синтаксический анализатор C# в Code DOM, который не реализован, но метод существует!) Вы можете проверить с помощью этого метода Microsoft.CSharp.CSharpCodeProvider.Parse
Однажды я сошел с ума, когда понял, что должен написать свой собственный синтаксический анализатор. Это действительно плохо.
Не просто «плохо», а непростительно.
взгляните на этот список: blogs.msdn.com/brada/archive/2004/07/29/201354.aspx#203896
Why does the NotImplementedException exist?
NotImplementedException - отличный способ сказать, что что-то еще не готово. Почему он не готов - отдельный вопрос для авторов метода. В производственном коде вы вряд ли поймаете это исключение, но если вы это сделаете, вы можете сразу увидеть, что произошло, и это намного лучше, чем пытаться выяснить, почему методы были вызваны, но ничего не произошло или даже хуже - получите какой-то «временный» результат и получите «забавные» побочные эффекты.
Is NotImplementedException the C# equivalent of Java's UnsupportedOperationException?
Нет, в .NET есть NotSupportedException
I have to run my algorithm, catch NotImplementedExceptions and the some how roll back my application to some sane state
Хороший API имеет документацию по методам XML, в которой описаны возможные исключения.
I'm very suspicious of the NotSupportedException also... Not supported? What the? If it's not supported, why is it part of your interface?
Причин может быть миллион. Например, вы можете ввести новую версию API и не хотите / не можете поддерживать старые методы. Опять же, гораздо лучше увидеть описательное исключение, чем копаться в документации или отлаживать сторонний код.
Спасибо, aku - поддержка API - использование устаревших методов мощь является разумной причиной для этого исключения. Хотя я бы предпочел увидеть исключение DeprecatedMethodException.
Есть атрибут Obsolete, я бы хотел, чтобы у нас было что-то вроде атрибута NotImplemented для получения предупреждений во время компиляции.
@aku: отличный комментарий - это решает проблему невозможности определить, действительно ли метод реализован, перед его вызовом (не то, чтобы это когда-либо приходилось делать).
Несмотря на то, что я часто использую исключение (см. Мой ответ), я бы предпочел этот атрибут, особенно если бы я мог превратить его в аспект, который генерирует исключение (NotImplemented - мой первый выбор, для ясности).
Java UnsupportedOperationException также может быть .NET InvalidOperationException.
Атрибут NotImplemented будет работать только в том случае, если класс напрямую (а не вызывает метод с использованием интерфейса)
Исключение InvalidOperationException .Net более близко соответствует исключению IllegalStateException в Java.
@aku - Да, дружище. Вот что у них есть на Яве. Разработчики Java могут вызывать статический метод «Trace», например NotImplementedException.Trace («предупреждение !! не реализовано !!»); Я завидую явистам
Что ж, отчасти согласен. Если интерфейс был создан таким образом, что не весь класс может реализовать все его части, на мой взгляд, его следовало бы разбить.
Если IList можно или нельзя изменить, его следовало разбить на две части: одну для неизменяемой части (геттеры, поиск и т. д.), А другую - для изменяемой части (сеттеры, добавление, удаление и т. д.).
Для меня это звучит как потенциальное минное поле. В далеком прошлом я однажды работал над унаследованной сетевой системой, которая работала без остановок в течение многих лет и перестала работать более одного дня. Когда мы отследили проблему, мы обнаружили некоторый код, который явно не был закончен и который никогда не мог бы сработать - буквально, как будто программист был прерван во время его написания. Было очевидно, что этот конкретный путь кода никогда раньше не использовался.
Закон Мерфи гласит, что нечто подобное просто напрашивается на случай NotImplementedException. В наши дни TDD и т. д., Его следует забрать перед выпуском, и, по крайней мере, вы можете использовать grep-код для этого исключения перед выпуском, но все же.
При тестировании трудно гарантировать охват каждого случая, и это похоже на то, что это усложняет вашу работу, делая проблемы времени выполнения тем, что могло быть проблемами времени компиляции. (Я думаю, что аналогичный вид «технического долга» связан с системами, которые в значительной степени полагаются на «утиную печать», хотя я признаю, что они очень полезны).
Есть одна ситуация, которую я считаю полезной: TDD.
Я пишу свои тесты, затем создаю заглушки, чтобы тесты компилировались. Эти заглушки не делают ничего, кроме throw new NotImplementedException();. Таким образом, тесты по умолчанию не пройдут, несмотря ни на что. Если бы я использовал какое-то фиктивное возвращаемое значение, оно могло бы генерировать ложные срабатывания. Теперь, когда все тесты компилируются и терпят неудачу из-за отсутствия реализации, я занимаюсь этими заглушками.
Поскольку я никогда не использую NotImplementedException ни в какой другой ситуации, ни один NotImplementedException никогда не перейдет в код выпуска, так как он всегда приводит к провалу некоторых тестов.
Вам не нужно ловить это повсюду. Хорошие API документируют возникшие исключения. Это те, которые вам следует искать.
Обновлено: Я написал правило FxCop, чтобы найти их.
Это код:
using System;
using Microsoft.FxCop.Sdk;
/// <summary>
/// An FxCop rule to ensure no <see cref = "NotImplementedException"/> is
/// left behind on production code.
/// </summary>
internal class DoNotRaiseNotImplementedException : BaseIntrospectionRule
{
private TypeNode _notImplementedException;
private Member _currentMember;
public DoNotRaiseNotImplementedException()
: base("DoNotRaiseNotImplementedException",
// The following string must be the assembly name (here
// Bevonn.CodeAnalysis) followed by a dot and then the
// metadata file name without the xml extension (here
// DesignRules). See the note at the end for more details.
"Bevonn.CodeAnalysis.DesignRules",
typeof (DoNotRaiseNotImplementedException).Assembly) { }
public override void BeforeAnalysis()
{
base.BeforeAnalysis();
_notImplementedException = FrameworkAssemblies.Mscorlib.GetType(
Identifier.For("System"),
Identifier.For("NotImplementedException"));
}
public override ProblemCollection Check(Member member)
{
var method = member as Method;
if (method != null)
{
_currentMember = member;
VisitStatements(method.Body.Statements);
}
return Problems;
}
public override void VisitThrow(ThrowNode throwInstruction)
{
if (throwInstruction.Expression != null &&
throwInstruction.Expression.Type.IsAssignableTo(_notImplementedException))
{
var problem = new Problem(
GetResolution(),
throwInstruction.SourceContext,
_currentMember.Name.Name);
Problems.Add(problem);
}
}
}
И это метаданные правила:
<?xml version = "1.0" encoding = "utf-8" ?>
<Rules FriendlyName = "Bevonn Design Rules">
<Rule TypeName = "DoNotRaiseNotImplementedException" Category = "Bevonn.Design" CheckId = "BCA0001">
<Name>Do not raise NotImplementedException</Name>
<Description>NotImplementedException should not be used in production code.</Description>
<Url>http://stackoverflow.com/questions/410719/notimplementedexception-are-they-kidding-me</Url>
<Resolution>Implement the method or property accessor.</Resolution>
<MessageLevel Certainty = "100">CriticalError</MessageLevel>
<Email></Email>
<FixCategories>NonBreaking</FixCategories>
<Owner></Owner>
</Rule>
</Rules>
Для этого вам необходимо:
ссылки Microsoft.FxCop.Sdk.dll и Microsoft.Cci.dll
Поместите метаданные в файл с именем DesignRules.xml и добавьте его в качестве встроенного ресурса в вашу сборку.
Назовите вашу сборку Bevonn.CodeAnalysis. Если вы хотите использовать разные имена для метаданных или файлов сборки, убедитесь, что вы соответствующим образом изменили второй параметр базового конструктора.
Затем просто добавьте полученную сборку в правила FxCop и удалите эти чертовы исключения из своего драгоценного кода. Есть некоторые угловые случаи, когда он не будет сообщать об исключении NotImplementedException, когда оно выбрасывается, но я действительно думаю, что вы безнадежны, если на самом деле пишете такой ктулийский код. Для нормального использования, то есть throw new NotImplementedException();, он работает, и это все, что имеет значение.
Я полностью согласен. Добавить это в любую другую настройку - это все равно, что иметь на вашей веб-странице знак «В разработке».
Хороший. Однако почему NotImplementedException является частью ядра .NET? Я бы предпочел, чтобы эта концепция была привязана к поставщику, и я бы условно скомпилировал свое NotImplementedException во всех сборках общедоступных релизов. Это не концепция ядра .NET.
Что в нем причиняет вред? Для меня это экономит время на написание глупого исключения. И с моим подходом мне не нужно его компилировать. Я могу гарантировать, что он никогда не используется в коде выпуска. И это точно не делает структуру намного больше ...
Хорошо, я говорю вам, что это исключение может быть не лучшим решением. Я бы предпочел идею аспекта.
@ Мартиньо: да, детка, ты придешь.
@Martinho: означает добавить, что повреждение, вызванное его нахождением в ядре, заключается в том, что вы не можете удалить его из своей сборки, поэтому можно отправить продукт или патч, в котором все еще есть эти неприятные глючники. Я предпочитаю технику, которая заставляет невозможно совершать такую ошибку.
@Daniel: Вы удаляете его из своего кода, реализуя эту функцию. Я не понимаю, в чем заключается путаница (простите меня, если это звучит нелепо, этого не должно быть).
@Daniel: объедините функцию NotImplemented с TDD, чтобы обеспечить реализацию функции; как только функция будет реализована, удалите исключение. Он никогда не выпускает код. NotSupportedException предназначено для поставщиков, хорошо документировано и ДОЛЖНО быть обнаружено клиентами.
@Mike: если он никогда не использовался в выпущенном коде, почему он находится в ядре .NET? Если он никогда не попадает в выпущенный код, его не нужно ни документировать, ни отлавливать. PS: ваш тон меня просто устраивает! Трудно оставаться мягким и мягким за 300 символов.
@Daniell Paull. Очень приятно иметь это в ядре .net, потому что это представляет собой стандартизованный, несформированный способ, с помощью которого я могу "находить способы использования" в моей среде IDE для выявления незавершенного материала.
@krosenvold: функция IDE, определяющая дизайн платформы? Звучит в обратном направлении для меня.
@krosenvold: Вы можете «найти использования» с помощью XYZException. Это будет работать так же. Вам просто нужно написать XYZException самостоятельно.
Я принял это как «правильный» ответ, поскольку я согласен с таким использованием исключения. Это использование все еще кажется мне опасным, но с правильными сдержками и противовесами такое использование разумно.
Мне нравится идея использования FxCop для двойной проверки отсутствия NotImplementedExceptions. Я использовал его в одном из своих проектов и обнаружил одну небольшую ошибку. В VisitThrow вам нужно проверить, что значение throwInstruction.Expression не равно нулю. Это происходит всякий раз, когда кто-то перебрасывает голым «броском»; инструкция. Кроме этого, большое спасибо!
+1 За ктулхианский код. Лучшее прилагательное, которое я когда-либо слышал для обозначения плохого кода.
К сожалению, в последнее время я по локоть в ктулхийском коде. Нет NotImplementedExceptions, о которых я знаю, но я бы предпочел функцию, которой не существует, той, которая существует только для объятий щупальцами.
Редко использую для исправления интерфейса. Предположим, что у вас есть интерфейс, которому необходимо соответствовать, но определенный метод никогда не будет вызван кем-либо, поэтому просто вставьте NotImplementedException, и если кто-то вызовет его, они будут знать, что делают что-то не так.
Метод интерфейса, который нельзя вызвать? Какого черта?
Если вы не реализуете весь интерфейс, не реализуйте его. С этой методологией вы должны использовать языки, которые умеют не печатать, например Python или даже C# 4 :(.
Итак, у вас есть 10 классов, реализующих интерфейс, но у одного или нескольких из них есть дополнительные методы, которые вам нужно вызвать. Как это сделать? Я реализую интерфейс с дополнительным методом и вызываю его, если тип совпадает. Какое у вас решение? 10 отдельных классов?
@Slough: дайте мне пример, где это так, и я исправлю ваш дизайн бесплатно.
В IDataReader есть масса методов. Мне пришлось реализовать его интерфейс, прежде чем я узнал, что мне понадобятся только три метода. Интерфейс находится в платформе .net. Я не могу это изменить. Я также передаю его классу фреймворка, который не могу изменить. Это плохой интерфейс, но я застрял в нем.
@Mike: Я чувствую твою боль, и этот сценарий может быть верен, поскольку твою руку заставили. Поэтому MS включила это плохое исключение, чтобы поддержать их плохой интерфейс и дизайн фреймворка. Разумный аргумент. Я думаю, что в данном случае две ошибки только усугубили ситуацию.
Случай с IDataReader и друзьями указывает на использование. В большинстве случаев используется только подмножество его методов. При построении интерфейса как оболочки неиспользуемые заглушки должны что-то делать. Это самый разумный поступок.
Почему вы чувствуете необходимость перехватывать все возможные исключения? Вы тоже оборачиваете каждый вызов метода с помощью catch (NullReferenceException ex)?
Код заглушки, бросающий NotImplementedException, является заполнителем, если он заставляет его выпустить, это должно быть ошибкой, как и NullReferenceException.
Если используется просто как заполнитель, почему NotImplementedException является частью ядра .NET? Я бы предпочел, чтобы эта концепция была привязана к поставщику, и я бы условно скомпилировал свое NotImplementedException во всех сборках общедоступных релизов. Это не концепция ядра .NET.
@Daniel, я думаю, у нас разные мнения по этому поводу, но я бы определенно не компилировал исключения в сборках релизов, точно так же, как я бы не ловил NullReferenceException - я бы исправил ошибку.
@orip: Я думаю, вы неправильно поняли. Компилируйте класс NotImplementedException, а не места, где он использовался. Это приведет к тому, что любое случайное использование NotImplementedException станет ошибкой компиляции.
@ Дэниел: ах, в этом есть смысл.
В моем коде есть несколько NotImplementedExceptions. Часто это происходит из части интерфейса или абстрактного класса. Некоторые методы, которые, как мне кажется, могут мне понадобиться в будущем, имеют смысл как часть класса, но я просто не хочу тратить время на добавление, если оно мне действительно не понадобится. Например, у меня есть интерфейс для всех отдельных видов статистики в моей игре. Одним из таких типов является ModStat, который представляет собой сумму базовой характеристики плюс все модификаторы (например, оружие, броня, заклинания). В моем интерфейсе статистики есть событие OnChanged, но мой ModStat работает, вычисляя сумму всех статистических данных, на которые он ссылается при каждом вызове. Таким образом, вместо того, чтобы создавать тонну событий ModStat.OnChange каждый раз при изменении статистики, у меня просто генерируется NotImplementedException, если кто-то пытается добавить / удалить слушателя в OnChange.
Все языки .NET - это продуктивность, так зачем тратить время на кодирование того, что вы даже не будете использовать?
"Так зачем тратить время на кодирование того, что ты даже не будешь использовать?" Интересно - похоже, что вы реализовали заглушки, которые, возможно, никогда не реализовать. Следуйте своему собственному совету, и исключение NotImplementedException вам не понадобится!
Один Даниил сказал, что вы не должны писать заглушки напрасно. Если они вам «могут» понадобиться в будущем, напишите их в будущем. В большинстве случаев сборки .NET версии очень хорошо.
«Следуйте своему собственному совету, и вам не понадобится NotImplementedException!» Смысл исключения здесь в том, что вы можете скомпилировать, но все еще терпят неудачу, если вызывается метод, и знаете, почему ваш код не удался. Добавьте код, если получите исключение - вот и все.
Почему бы не создать собственный тип исключения? Почему это основная концепция .net?
NotImplementedException
Исключение возникает, когда запрошенный метод или операция не реализованы.
Создание этого единственного исключения, определенного в ядре .NET, упрощает их поиск и устранение. Если бы каждый разработчик создал свой собственный ACME.EmaNymton.NotImplementedException, было бы труднее найти их всех.
NotSupportedException
Исключение возникает, когда вызываемый метод не поддерживается.
Например, когда есть попытка чтения, поиска или записи в поток, который не поддерживает вызываемую функциональность.
Например, сгенерированные итераторы (с использованием ключевого слова yield) - это IEnumerator, но метод IEnumerator.Reset выдает NotSupportedException.
«облегчает их поиск и искоренение» - проще? Это твоя причина? Не потому, что это правильный способ, а потому, что это самый простой способ. Хороший мужчина.
Почему поток, который не поддерживает произвольный доступ, имеет метод поиска? Не будем прятаться за плохим дизайном потокового интерфейса Microsoft.
Теперь я боюсь называть Reset () любым перечислителем.
Конечно, INotResetEnumerator, возможно, был бы лучше, от которого происходит IEnumerator. Но приводит к слишком большому количеству занятий.
Было бы интересно правильно его спроектировать. Я не согласен с тем, что это приведет к слишком большому количеству интерфейсов.
Все зависит от степени детализации, у вас может быть IListMutable (который содержит Add, Clear, Insert, Remove, RemoveAt) и IListImmutable (Contains, CopyTo, GetEnumerator, IndexOf). MyList: IListMutable, IListImmutable {/ * impl * /}
Но что, если вам нужен список только для добавления и только для чтения? MyList: IListAdd, IListInsert, IListImmutable {/ * impl * /} и пусть IListMutable: IListAdd, IListClear, IListInsert, IListRemove, IListRemoveAt
Интерфейс для изменения абстрактных типов всегда будет сложным и должен разрабатываться очень тщательно. Осмелимся ли мы спросить, является ли квадрат прямоугольником?
Похоже, вы предлагаете, чтобы разработчик любого интерфейса всегда мог угадывать все варианты использования интерфейса, как оракул. Конечно, в рамках одного проекта это возможно; в общем, я думаю, что нет.
@zacharyrsnow: Если следовать здравым принципам объектно-ориентированного дизайна, то интерфейсы будут интуитивно понятными и будут служить ясной цели. Поскольку цель ясна, никаких суперсил (например, оракула) во время проектирования не требуется. Так что в целом я так думаю ...
@dalle: правильным дизайном было бы, чтобы IEnumerable не предоставлял сброс, но предоставлял довольно слабые гарантии поведения перечисления при обновлении коллекции (выброс исключения был бы вариантом, но не обязательным, если бы все элементы, которые существуют во всем перечислении возвращаются ровно один раз и т. д.); IMultipassEnumerable добавит Reset, FastCount и Count (FastCount вернет по своему выбору либо -1, либо правильный счетчик; его можно использовать для предварительного выделения места для списка, если размер коллекции был известен до того, как он был полностью перечислен) .
Я думаю, есть много причин, по которым MS добавила NotImplementedException в фреймворк:
Франкодвайер считает NotImplementedException потенциальной бомбой замедленного действия. Я бы сказал, что любой незаконченный код - это бомба замедленного действия, но NotImplementedException гораздо легче обезвредить, чем альтернативы. Например, ваш сервер сборки может сканировать исходный код для всех случаев использования этого класса и сообщать о них как о предупреждениях. Если вы действительно хотите его забанить, вы можете даже добавить в свою систему управления версиями ловушку перед фиксацией, которая предотвращает регистрацию такого кода.
Конечно, если вы выбросите собственное NotImplementedException, вы можете удалить его из окончательной сборки, чтобы убедиться, что не осталось бомб замедленного действия. Но это будет работать только в том случае, если вы будете использовать свою собственную реализацию последовательно во всей команде, и вы должны убедиться, что не забыли удалить ее перед выпуском. Кроме того, вы можете обнаружить, что не можете удалить его; возможно, есть несколько приемлемых вариантов использования, например, при тестировании кода, который не поставляется клиентам.
Почему бы не иметь «Microsoft.Development.Support» или аналогичную сборку, в которой есть полезные классы и утилиты (включая NotImplementedException). Намерение состоит в том, чтобы ссылаться на эту сборку в сборках разработчика, но не в выпущенном коде?
Для этого потребуется добавить ссылку на сборку Microsoft.Development.Support для каждого проекта, который ее использует, что создаст еще одно неудобство и препятствие для инструментов.
А проблема с этим есть? Я бы предпочел, чтобы моя система была исправлена, что доставляет небольшие неудобства моим разработчикам. Было бы удобнее использовать библиотеки DLL, но выгода оправдывает затраты (под стоимостью я имею в виду усилия и сложность).
Я обобщу свои взгляды по этому поводу в одном месте, поскольку они разбросаны по нескольким комментариям:
Вы используете NotImplementedException, чтобы указать, что член интерфейса еще не реализован, но будет. Вы комбинируете это с автоматическим модульным тестированием или тестированием QA, чтобы определить функции, которые еще необходимо реализовать.
Как только функция будет реализована, вы удалите NotImplementedException. Для этой функции написаны новые модульные тесты, чтобы убедиться, что она работает должным образом.
NotSupportedException обычно используется поставщиками, которые не поддерживают функции, не имеющие смысла для определенных типов. В этих случаях определенные типы вызывают исключение, клиенты перехватывают их и обрабатывают соответствующим образом.
Причина того, что и NotImplementedException, и NotSupportedException существуют в Framework, проста: ситуации, которые к ним приводят, являются обычными, поэтому имеет смысл определить их в Framework, чтобы разработчикам не приходилось постоянно их переопределять. Кроме того, это позволяет клиентам легко узнать, какое исключение нужно перехватить (особенно в контексте модульного теста). Если вам нужно определить собственное исключение, они должны выяснить, какое исключение нужно перехватывать, что, по крайней мере, является контрпродуктивным расходом времени и часто неверным.
Вы признаете, что NotImplementedException будет удалено - в этом случае в ядре .NET нет необходимости. Это ваша личная забота о вашем собственном процессе разработки.
Вот один пример: в Java всякий раз, когда вы реализуете интерфейс Iterator, вы должны переопределить очевидные методы hasNext() и next(), но есть еще и delete(). В 99% случаев, которые у меня есть, мне это не нужно, поэтому я просто кидаю NotImplementedException. Это намного лучше, чем молча ничего не делать.
Еще лучше было бы, чтобы в интерфейсе не было метода delete (). Интерфейсы звучат так же ломанно, как потоки .net.
Оба они - хаки для решения двух распространенных проблем.
NotImplementedException - это обходной путь для разработчиков, которые являются астронавтами архитектуры и любят сначала записывать API, а потом код. Очевидно, что, поскольку это не инкрементный процесс, вы не можете реализовать все сразу и поэтому хотите притвориться, что вы наполовину закончили, выбрасывая NotImplementedException.
NotSupportedException - это способ обойти ограничения систем типов, подобных тем, которые существуют в C# и Java. В этих системах типов вы говорите, что Rectangle 'является' Shape, если и только если Rectangle наследует все характеристики Shapes (включая функции-члены + переменные). Однако на практике это не так. Например, Квадрат - это Прямоугольник, но Квадрат - это ограничение Прямоугольника, а не обобщение.
Поэтому, когда вы хотите унаследовать и ограничить поведение родительского класса, вы бросаете NotSupported на методы, которые не имеют смысла для ограничения.
Нет никаких причин, по которым астронавт-архитектор не может разработать разумный интерфейс, кроме как - они плохо разбираются в дизайне и не способны выполнять свою работу. Архитектор (yuk) не должен иметь последнее слово при проектировании API; перед его внедрением следует проконсультироваться с разработчиками и подписать их.
Когда вы хотите унаследовать и ограничить поведение родительского класса, вы нарушаете инварианты базового класса / интерфейса. Если в контракте базового класса / интерфейса явно не указано, что реализации могут не поддерживать все функции (возможно, с шаблоном CanDo / Do ()), не делайте этого. Скорее всего, вы сломаете клиентский код. Если вам нужно разорвать контракт Rectangle для создания класса Square, Square это не (т.е.не наследуется от) Rectangle. Я знаю, что легко рассматривать наследование как «есть», но иногда это может быть очень плохой метафорой.
@Afd - Я предлагаю вам почитать. «Квадрат - это особый прямоугольник» - это повсеместный пример, показывающий, где люди ошибаются при наследовании.
На самом деле нет причин для фактического ловить NotImplementedException. При попадании он должен убить ваше приложение, и сделать это очень болезненно. Единственный способ исправить это - не поймать его, а изменить исходный код (либо реализовать вызываемый метод, либо изменить вызывающий код).
Это очень странный ответ. Если никто никогда не предназначен для перехвата исключения, зачем его бросать и почему оно является частью ядра .Net? ПРИМЕЧАНИЕ: то, что вы генерируете исключение, не означает, что оно не будет обнаружено. Почему не просто Assert (false) как в отладочной, так и в выпускной сборках?
Что бы вы сделали в оговорке о вылове? Специально для NotImplementedException?
Если бы я поступил по-своему, я бы никогда не бросил и не поймал NotImplementedException. Я пытаюсь найти причину, по которой он является частью основной библиотеки.
Есть много других исключений, которые вы не должны улавливать (StackOverflowException, OutOfMemoryException).
Это исключение необходимо для COM-взаимодействия. Это E_NOTIMPL. Связанный блог также показывает другие причины
Если единственное допустимое использование - сопоставление с COM E_NOTIMPL, то это исключение обязательно должно быть в пространстве имен взаимодействия. Какие еще допустимые варианты использования размещены в этом блоге? Аргумент, что это указывает на временное состояние развития, неубедителен; используйте для этого собственное исключение.
Из ECMA-335, спецификация CLI, в частности типы библиотеки CLI, System.NotImplementedException, раздел примечаний:
«Некоторые типы и конструкции, указанные в других частях настоящего стандарта, не требуются для реализаций интерфейса командной строки, которые соответствуют только профилю ядра. Например, набор функций с плавающей запятой состоит из типов данных с плавающей запятой System.Single и System.Double. Если поддержка для них исключена из реализации, любая попытка ссылаться на подпись, которая включает типы данных с плавающей запятой, приводит к исключению типа System.NotImplementedException ".
Таким образом, исключение предназначено для реализаций, реализующих только минимальные профили соответствия. Минимально необходимый профиль - это профиль ядра (см. ECMA-335 4-е издание - раздел IV, раздел 3), который включает BCL, поэтому исключение включено в «основной API», а не в какое-либо другое место.
Использование исключения для обозначения зарезервированных методов или для методов, созданных конструктором, не имеющих реализации, означает неправильное понимание цели исключения.
Я не понимаю, почему эта информация НЕ включена в документацию MSDN для реализации интерфейса командной строки MS.
Я не могу поручиться за NotImplementedException (я в основном согласен с вашим мнением), но я широко использовал NotSupportedException в основной библиотеке, которую мы используем на работе. DatabaseController, например, позволяет вам создать базу данных любого поддерживаемого типа, а затем использовать класс DatabaseController во всем остальном коде, не слишком заботясь о типе базы данных под ней. Довольно простые вещи, правда? Если NotSupportedException пригодится (и где я бы использовал свою собственную реализацию, если бы она еще не существовала), это два основных экземпляра:
1) Перенос приложения в другую базу данных Часто утверждают, что это редко, если вообще когда-либо, случается или необходимо. Фигня * т. Убирайся больше.
2) Та же база данных, другой драйвер Самый последний пример этого - когда клиент, использующий приложение с поддержкой Access, обновился с WinXP до Win7 x64. Поскольку 64-битный драйвер JET отсутствует, их ИТ-специалист установил вместо этого AccessDatabaseEngine. Когда у нашего приложения произошел сбой, мы могли легко увидеть из журнала, что это был сбой DB.Connect с NotSupportedException, который мы быстро смогли устранить. Другой недавний пример - один из наших программистов, пытающийся использовать транзакции в базе данных Access. Несмотря на то, что Access поддерживает транзакции, наша библиотека не поддерживает транзакции Access (по причинам, выходящим за рамки данной статьи). NotSupportedException, ваше время сиять!
3) Общие функции Я не могу придумать здесь краткий пример «из опыта», но если вы думаете о чем-то вроде функции, которая добавляет вложение к электронному письму, вы хотите, чтобы она могла принимать несколько распространенных файлов, таких как JPEG, все, что получено из различных потоковые классы и практически все, что имеет метод ".ToString". Что касается последней части, вы, конечно, не можете учитывать все возможные типы, поэтому сделайте их универсальными. Когда пользователь передает OurCrazyDataTypeForContainPrivateSpreadsheetData, используйте отражение, чтобы проверить наличие метода ToString, и вернуть NotSupportedException, чтобы указать на отсутствие поддержки указанных типов данных, которые не поддерживают ToString.
NotSupportedException ни в коем случае не является важной функцией, но это то, что я использую гораздо больше, когда работаю над более крупными проектами.
Выброс NotImplementedException - наиболее логичный способ для IDE сгенерировать код заглушки компиляции. Например, когда вы расширяете интерфейс и заставляете Visual Studio вставлять его за вас.
Если вы немного использовали C++ / COM, он тоже существовал, за исключением того, что он был известен как E_NOTIMPL.
Для него есть допустимый вариант использования. Если вы работаете над определенным методом интерфейса, вы хотите, чтобы код компилировался, чтобы вы могли его отлаживать и тестировать. Согласно вашей логике вам нужно будет удалить метод из интерфейса и закомментировать некомпилируемый код-заглушку. Это очень фундаменталистский подход, и, хотя он имеет свои достоинства, не все будут или должны его придерживаться. Кроме того, большую часть времени вы хотите, чтобы интерфейс был законченным.
Наличие NotImplementedException прекрасно определяет, какие методы еще не готовы, в конце концов, это так же просто, как нажать Ctrl+Shift+F, чтобы найти их все, я также уверен, что инструменты статического анализа кода тоже это подберут.
Вы не должны отправлять код с исключением NotImplementedException. Если вы думаете, что, не используя его, вы можете улучшить свой код, продолжайте, но есть более продуктивные вещи, которые вы можете сделать, чтобы улучшить качество исходного кода.
«Согласно вашей логике, вам нужно удалить метод из интерфейса и закомментировать некомпилируемый код-заглушку». Давайте не будем вкладывать слова в мой рот - я бы никогда не посоветовал вам это сделать. Я согласен, что во время разработки у вас будут нереализованные заглушки. Но эти заглушки могут вызвать ваше собственное исключение. Я просто не понимаю, почему NotImplementedException должно существовать как часть основных библиотек .Net.
Что ж, в этом случае вы выполнили обычный StackOverflow и не спросили, что вы действительно хотели спросить. Ваш собственный ответ достаточно лаконично отвечает на ваш вопрос, но я, и я уверен, что другие читают ваш вопрос как «Вы не должны бросать исключения для нереализованных методов».
Зачем выбрасывать собственные исключения, если для этого есть стандарт? Повторное изобретение колеса - одна из основных причин, почему работа в «нашей» отрасли иногда бывает такой болезненной (синдром NIH ...). В некоторых местах вам могут понадобиться заглушки, и создание стандартизированного исключения позволяет легко отслеживать их с помощью инструментов. например, "grep". Я также часто использую NIE с TDD. Есть много мест, где они имеют смысл. Просто оставить заглушку иногда даже более опасно, я люблю спамить свой код с помощью "// TODO:", потому что, как только он записан, я не могу его забыть.
Две причины:
Во время разработки методы игнорируются и вызывают исключение, чтобы напомнить разработчикам, что написание их кода еще не завершено.
Реализация интерфейса подкласса, который по замыслу не реализует один или несколько методов унаследованного базового класса или интерфейса. (Некоторые интерфейсы слишком общие.)
Если вы работаете с интерфейсами, которые «слишком общие» для класса, который вы пишете, вам, вероятно, следует подумать об интерфейсе, который имеет только те методы, которые вам действительно нужны. Если у IFooBar есть три метода, а у меня есть экземпляр IFooBar, эти методы лучше реализовать (или, по крайней мере, генерировать исключения, определенные контрактом). Я бы сказал, что 95% или более случаев выброса NotImplementedException () будет нарушением принципа замены Лискова.
Да, но иногда у вас нет выбора, например, когда вы используете стандартную библиотеку .NET. Рассмотрим некоторые из «необязательных» методов в классах итератора и потоков ввода-вывода.
@S. ДеПоу: Это может не нарушать принцип замещения Лискова, если есть другой способ определить, следует ли вызывать метод во время выполнения, например, тест CanXXX (), который сообщает вам, что вы можете вызвать XXX (). Однако я очень подозрительно отношусь к такого рода интерфейсам, поскольку они пахнут неправильным наследованием - как и (2) в этом ответе.
Если вы не хотите его использовать, просто проигнорируйте его. Если у вас есть блок кода, успех которого зависит от успеха каждой его части, но он может дать сбой между ними, тогда ваш единственный вариант - поймать базовый Exception и откатить то, что нужно откатить. Забудьте о NotImplementedException. Может быть множество исключений, таких как MyRandomException, GtfoException и OmgLolException. После того, как вы изначально напишите код, я мог бы подойти и выбросить ДРУГОЙ тип исключения из API, который вы вызываете. Тот, которого не существовало, когда вы писали свой код. Обрабатывайте те, которые умеете обрабатывать, и откатывайте все остальные, например, catch(Exception). Думаю, это довольно просто ... Я тоже считаю, что это пригодится. Особенно, когда вы пробуете необычные вещи с языком / фреймворком, которые иногда навязывают вам что-то.
У меня есть один пример - сериализация. Я добавил в свою библиотеку .NET свойства, которых нет в базе данных (например, удобные оболочки для существующих «тупых» свойств, таких как свойство FullName, объединяющее FirstName, MiddleName и LastName). Затем я хочу сериализовать эти типы данных в XML, чтобы отправить их по сети (например, из приложения ASP.NET в JavaScript), но структура сериализации сериализует только общедоступные свойства с помощью средств доступа как get, так и set. Я не хочу, чтобы вы могли установить FullName, потому что тогда мне придется разбирать его, и может быть какой-то непредвиденный формат, который я неправильно разбираю, и целостность данных вылетает из окна. Для этого проще просто использовать базовые свойства, но поскольку язык и API требуют, чтобы у меня был аксессуар set, я выброшу NotImplementedException (я не знал о NotSupportedException, пока не прочитал эту ветку, но любой из них работает) так что если какой-нибудь программист в будущем все же попытается установить FullName, он обнаружит исключение во время тестирования и осознает свою ошибку.
catch (Exception) или аналогичный catch (...) в C++ - одна из самых злобных вещей, которые вы можете сделать в программировании. Это называется «подмести под ковер» и начинается ваше погружение в Большой шар грязи. Помещение catch (Exception) куда угодно непросто и непросто - если вы думаете, что это так, то вам предстоит пройти долгий путь, прежде чем вы сможете написать надежный код. В вашем примере, если FullName считается неизменяемым (т. Е. Не может быть установлено), почему в API есть установщик для него? Что-то не так с вашим (или Microsoft) дизайном. Есть множество способов избежать добавления этого сеттера ...
catch(Exception) - это именно то, что вам нужно в «верхней части» приложения, где, если бы исключение было иначе оставлено необработанным, среда выполнения должна была бы обработать его вместо этого, что обычно уродливо и неприятно для пользователя. По сути, это крах. Примеры находятся в Main или в Page_Load. У свойства FullName есть сеттер, потому что он требуется для API сериализации, как я сказал в сообщении, которое вы прокомментировали ...
NotImplementedException существует только для облегчения разработки. Представьте, что вы начинаете реализацию интерфейса. Вы хотели бы иметь возможность по крайней мере построить, когда вы закончите реализацию одного метода, прежде чем переходить к следующему. Замещение методов NotImplemented с помощью NotImplementedExceptions - отличный способ сохранить незаконченный код, который очень легко обнаружить позже. В противном случае вы рискуете быстро реализовать что-то, что можете забыть исправить.
Вы можете использовать любую методологию, которая вам нравится, так что вы можете «заглушить» все, что захотите. Зачем Microsoft поместила это в стандартную библиотеку? Вы можете так же легко определить собственное исключение, которое нужно выбросить. Если вы использовали свой собственный класс исключения, вы можете удалить определение класса из своего кода, и компилятор сообщит вам, если вы забыли заменить одну из своих заглушек реальной реализацией - мне это нравится намного больше, чем поиск шаблонов в текст ... Обратите внимание, что вы не можете удалить NotImplementedException из сборки, поэтому это не вариант, если вы его используете.
Допустим, у вас есть этот метод в вашем производственном коде
public void DoSomething()
Какой из них вы бы выбрали, если хотите, чтобы его закончили позже?
public void DoSomething()
{
}
или же
public void DoSomething()
{
throw new NotImplementedException();
}
Я бы обязательно взял второй. В сочетании с Elmah или любым другим механизмом регистрации ошибок, который у вас есть (реализован как аспект всего вашего приложения). Вместе с фильтрацией журнала / исключений для запуска уведомления по электронной почте о критической ошибке, когда она обнаружена.
Аргумент, что NotImplementedException == незаконченный, также неверен. (1) Выявление нереализованных методов следует оставить на усмотрение модульных / интеграционных тестов. Если у вас 100% покрытие (что вы должны сделать сейчас с таким количеством инструментов для создания макетов / заглушек / кода) без NotImplementedException, о чем вы беспокоитесь? (2) Генерация кода. Просто просто. Опять же, если я сгенерирую код и использую только половину сгенерированного кода, почему бы мне не иметь NotImplementedException в остальной части сгенерированной заглушки?
Это как сказать, что код не должен компилироваться, если каждый ввод, допускающий значение NULL, не должен проверяться / обрабатываться на значение NULL. (ИНАЧЕ ошибка в триллион долларов, если не больше). Язык должен быть гибким, а тесты / контракты - твердыми.
Хуже того, как насчет Point ComputeLocation() { return default(Point); }. Есть много случаев, когда правильное поведение для метода без возвращаемого значения - просто ничего не делать (например, многие классы реализуют IDisposable.Dispose() как бездействующий), но наличие метода, возвращающего тип структуры, возвращает экземпляр по умолчанию, как если бы это был реальная стоимость кажется действительно сомнительной.
+1 за правильный вопрос о нечеткой проблеме, заслуживающей внимания.