Какой тип исключения использовать, когда свойство не может быть пустым?

not необходимо поместить реализацию в файл заголовка, см. альтернативное решение в конце этого ответа.

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

template
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo f; 

При чтении этой строки компилятор создаст новый класс (назовем его FooInt), что эквивалентно следующему:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
}

Следовательно, компилятор должен иметь доступ к реализации методов, чтобы создать экземпляр с аргументом шаблона (в данном случае int). Если эти реализации не были в заголовке, они не были бы доступны, поэтому компилятор не смог бы создать экземпляр шаблона.

Общим решением для этого является запись объявления шаблона в заголовок файла, затем реализовать класс в файле реализации (например, .tpp) и включить этот файл реализации в конец заголовка.

// Foo.h
template 
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

// Foo.tpp
template 
void Foo::doSomething(T param)
{
    //implementation
}

Таким образом, реализация по-прежнему отделена от объявления, но доступен компилятору.

Другое решение состоит в том, чтобы сохранить реализацию отдельно и явно создать все экземпляры шаблона, которые вам понадобятся:

// Foo.h

// no implementation
template  struct Foo { ... };

//----------------------------------------    
// Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo;
template class Foo;
// You will only be able to use Foo with int or float

Если мое объяснение isn ' t достаточно ясно, вы можете взглянуть на C ++ Super-FAQ по этому вопросу .

41
задан Vadim 28 September 2009 в 17:32
поделиться

9 ответов

Рекомендации MSDN для стандартных исключений гласят:

Использовать значение для имени параметра неявного значения установщиков свойств.

В следующем примере кода показано свойство, которое выдает исключение, если вызывающая сторона передает нулевой аргумент.

public IPAddress Address
{
    get
    {
        return address;
    }
    set
    {
        if(value == null)
        {
            throw new ArgumentNullException("value");
        }
        address = value;
    }
}

Кроме того, в руководящих указаниях MSDN по проектированию свойств говорится:

Избегайте создания исключений из методов получения свойств.

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

Допустимо и допустимо генерировать исключения из установщика свойств.

Итак, бросьте ArgumentNullException в установщик на null, ArgumentException в пустую строку и ничего не делайте в получателе. Поскольку сеттер выбрасывает и только у вас есть доступ к вспомогательному полю, легко убедиться, что оно не будет содержать недопустимое значение. Иметь метательный бросок бессмысленно. Однако это может быть хорошим местом для использования Debug.Assert .

Если вы действительно не можете предоставить подходящее значение по умолчанию, то я полагаю, у вас есть три варианта:

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

  2. Заменить свойство методами: метод установки, который выдает, когда передано недопустимое значение, и метод получения, который выдает InvalidOperationException, когда свойству никогда не назначалось допустимое значение.

  3. Бросьте InvalidOperationException из геттера, так как вы можете считать «свойство никогда не было назначено» как недопустимое состояние. Хотя обычно вы не должны бросать из получателей, я полагаю, что это может быть хорошей причиной для исключения.

Если вы выбираете опции 2 или 3, вы должны также включить TryGet-метод, который возвращает значение bool, которое указывает, было ли для свойства установлено допустимое значение, и, если это так, возвращает это значение в out параметр. В противном случае вы заставляете абонентов быть готовыми обработать InvalidOperationException, если только они сами не установили свойство и не знали, что оно не сгенерирует. Сравните int.Parse с int.TryParse.

Я бы предложил использовать вариант 2 с методом TryGet. Это не нарушает каких-либо правил и накладывает минимальные требования к вызывающему коду.


О других предложениях
ApplicationException слишком много общего. ArgumentException является слишком общим для null, но в остальном хорошо. MSDN снова документы :

Выдает самое конкретное (наиболее производное) исключение, которое уместно. Например, если метод получает пустой аргумент (Nothing в Visual Basic), он должен вызвать исключение System.ArgumentNullException вместо своего базового типа System.ArgumentException.

На самом деле вам вообще не следует использовать ApplicationException ( документы ):

Извлекать пользовательские исключения из T: System. Класс исключения, а не класс T: System.ApplicationException.

Первоначально считалось, что пользовательские исключения должны быть производными от класса ApplicationException; однако, это не было найдено, чтобы добавить значительную ценность. Для получения дополнительной информации см. Рекомендации по обработке исключений.

InvalidOperationException предназначен не для случаев, когда аргументы метода или свойства недопустимы, а для случаев, когда операция в целом недопустима ( документ ). Его не следует выбрасывать из установщика:

Не вызывать исключение System.InvalidOperationException, если он находится в неподходящем состоянии. System.InvalidOperationException должно быть выброшено, если набор свойств или вызов метода не подходят, учитывая текущее состояние объекта. Например, запись в System.IO.FileStream, который был открыт для чтения, должна вызвать исключение System.InvalidOperationException.

Между прочим, InvalidOperationException для случая, когда операция недопустима для текущего состояния объекта. Если операция всегда недопустима для всего класса, вы должны использовать NotSupportedException .

68
ответ дан Joren 28 September 2009 в 17:32
поделиться

Ну, это не аргумент, если вы ссылаетесь на свойство класса. Таким образом, вы не должны использовать ArgumentException или ArgumentNullException.

NullReferenceException произойдет, если вы просто оставите вещи в покое, поэтому я предполагаю, что это не то, что вы ищете.

Таким образом, использование ApplicationExeption или InvalidOperationException, вероятно, будет вашим лучшим выбором, если обязательно укажите содержательную строку для описания ошибки.

2
ответ дан John Fisher 28 September 2009 в 17:32
поделиться

Я бы бросил InvalidOperationException . MSDN говорит, что «выдается, когда вызов метода недопустим для текущего состояния объекта».

4
ответ дан bdukes 28 September 2009 в 17:32
поделиться

Вполне уместно выдать ArgumentNullException, если кто-нибудь попытается присвоить ноль.

Свойство никогда не должно вызывать операцию чтения.

2
ответ дан Mark Seemann 28 September 2009 в 17:32
поделиться

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

Подробное сообщение для конструктора было бы очень уместным здесь

1
ответ дан JaredPar 28 September 2009 в 17:32
поделиться

Конструктор устанавливает ненулевое значение? Если это так, я бы просто выбросил ArgumentNullException из сеттера.

1
ответ дан Tinister 28 September 2009 в 17:32
поделиться

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

Кроме того, требуется, чтобы свойство было установлено в конструкторе.

Таким образом, вы вводите действительное значение, а не возвращаетесь позже и говорите, что не можете определить остаток на счете, так как он не установлен.

Но я бы согласился с ответом bduke.

1
ответ дан James Black 28 September 2009 в 17:32
поделиться

Существует прецедент для расширения интерпретации ArgumentNullException до значения «строковый аргумент нулевой или пустой»: System.Windows.Clipboard.SetText в этом случае будет вызывать ArgumentNullException.

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

1
ответ дан Joe 28 September 2009 в 17:32
поделиться

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

0
ответ дан erikkallen 28 September 2009 в 17:32
поделиться
Другие вопросы по тегам:

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