Каков наиболее подходящий способ обработки поврежденных входных данных в конструкторе C #? - PullRequest
23 голосов
/ 03 июня 2011

Я читаю данные из файла и создаю объекты на основе этих данных.Формат данных не находится под моим контролем и иногда поврежден.Каков наиболее подходящий способ обработки этих ошибок при построении объектов в C #?

В других языках программирования я возвратил значение NULL, но это не похоже на вариант с C #.

Мне удалось выяснить следующие параметры, но я был бы признателен за советы более опытных программистов на C #:

Вариант 1. Считайте файл внутри конструктора и выведите исключение, когдаисходные данные повреждены:

try
{
    obj = Constructor(sourceFile);
    ... process object ...
}
catch (IOException ex)
{
    ...
}

Вариант 2. Создайте объект, затем используйте метод для чтения данных из исходного файла:

obj = Constructor();
obj.ReadData(sourceFile);
if (obj.IsValid)
{
    ... process object ...
}

или, возможно,генерировать исключения при ошибке:

obj = Constructor();
try
{
    obj.Read(sourceFile);
    ... process object ...
}
catch
{
    ...
}

Вариант 3. Создать объект с использованием статического метода TryParse:

if (ObjClass.TryParse(sourceFile, out obj))
{
    ... process object ...
}

и, если да, следует ли реализовать вариант 3внутренне используя вариант 1?

public static bool TryParse(FileStream sourceFile, out ObjClass obj)
{   
    try
    {
        obj = Constructor(sourceFile);
        return true;
    }
    catch (IOException ex)
        return false;
}

Ответы [ 5 ]

19 голосов
/ 03 июня 2011

Я бы сделал что-то вроде варианта 3):

class ObjectClass
{
    protected ObjectClass(...constructor parameters your object depends on...)
    {
    }

    public static ObjectClass CreateFromFile(FileStream sourceFile)
    {
        .. parse source file
        if (parseOk)
        {
            return new ObjectClass(my, constructor, parameters);
        }
        return null;
    }
}

И затем использовал бы это так:

ObjClass.CreateFromFile(sourcefile);

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

Обновление : как упоминалось вкомментарии лучше шаблон таков:

    public static ObjectClass CreateFromFile(FileStream sourceFile)
    {
        .. parse source file
        if (!parseOk)
        {
            throw new ParseException(parseErrorDescription);
        }
        return new ObjectClass(my, constructor, parameters);
    }

    public static bool TryCreateFromFile(FileStream sourceFile, out ObjectClass obj)
    {
        obj = null;
        .. parse source file
        if (!parseOk)
        {
            return false;
        }
        obj = new ObjectClass(my, constructor, parameters);
        return true;
    }
13 голосов
/ 03 июня 2011

Я бы не помещал в конструктор ничего, что могло бы вызвать исключение - за исключением случаев, когда что-то пошло не так.
Если ваш конструктор имеет возможное возвращаемое значение, отличное от допустимого объекта, вы должны инкапсулировать его.

Возможно, самый безопасный способ - создать фабричный метод (общедоступная статическая функция в классе, которая принимает ссылку на файл и возвращает новый экземпляр класса или значение null). Этот метод должен сначала проверить файл и его данные, а затем создать новый объект.

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

5 голосов
/ 03 июня 2011

Из Рекомендации по проектированию Microsoft Constructor (MSDN) ,

При необходимости генерируйте исключения из конструкторов экземпляров.

Конструкторы должны генерировать и обрабатывать исключения, как любыеметод.В частности, конструктор не должен перехватывать и скрывать любые исключения, которые он не может обработать.


Заводской метод не является правильным способом решения этой проблемы.См. Конструкторы против фабричных методов

Из Рекомендации по проектированию платформы: соглашения, идиомы и шаблоны для многократно используемых библиотек .NET

5.3 КонструкторПроектирование

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

При необходимости генерировать исключения из конструкторов экземпляров.


.NET BCL реализуют исключения из конструкторов

Например, Конструктор списка (Int32) , выдает ArgumentOutOfRangeException , когда аргумент емкости списка отрицательный.

var myList = new List<int>(-1); // throws ArgumentOutOfRangeException

Аналогично, ваш конструктор должен генерировать исключение соответствующего типа, когдаон читает файл.Например, он может выдать FileNotFoundException , если файл не существует в указанном месте и т. Д.


Дополнительная информация

5 голосов
/ 03 июня 2011

Оба варианта # 1 и # 3 являются хорошим выбором и распространены в .Net Framework.Также распространено предоставлять оба для того же самого типа.Рассмотрим Int32.TryParse и Int32.Parse.Предоставление обоих дает разработчикам немного больше гибкости, не умаляя целостности типа.

Я бы настоятельно рекомендовал вам избегать # 2.Этот шаблон заставляет автора типа и потребителя типа обрабатывать экземпляры типа в нескольких состояниях

  • Создано, но не полностью инициализировано
  • Инициализировано и действительно
  • ИнициализированоНеправильно

Это накладывает бремя на каждого потребителя, чтобы иметь дело с экземплярами, находящимися во всех разных состояниях (даже если ответ просто бросить).Кроме того, это навязывает потребителям нестандартную модель.Разработчики должны понимать, что ваш тип является особенным, и что его нужно создать, а затем инициализировать.Это идет вразрез со стандартным способом создания объектов в .Net.

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

class MyType { 
  struct ParsedData { 
    // Data from the file
  }

  public MyType(string filePath) : this(Parse(filePath)) { 
    // The Parse method will throw here if the data is invalid
  }

  private MyType(ParsedData data) {
    // Operate on the valid data.  This doesn't throw since the errors
    // have been rooted out already in TryParseFile
  }

  public static bool TryParse(string filePath, out MyType obj) {
    ParsedData data;
    if (!TryParseFile(filePath, out data)) {
      obj = null;
      return false;
    }

    obj = new MyType(data);
    return true;
  }

  private static ParsedData Parse(string filePath) {
    ParsedData data;
    if (!TryParseFile(filePath, out data)) {
      throw new Exception(...);
    }
    return data;
  }

  private static bool TryParseFile(string filePath, out ParsedData data) {
    // Parse the file and implement error detection logic here
  }
}
3 голосов
/ 03 июня 2011

Все эти решения работают, но, как вы сказали, C # не позволяет возвращать null из конструктора. Вы либо получаете объект, либо исключение. Поскольку это путь C #, я бы не выбрал вариант 3, потому что он просто имитирует тот другой язык, о котором вы говорите.

Множество людей [править], среди которых Мартин, как я прочитал в его ответе :) [/ edit] думаю, что хорошо, чтобы ваш конструктор был чистым и маленьким. Я не уверен в этом. Если ваш объект бесполезен без этих данных, вы также можете прочитать данные в конструкторе. Если вы хотите сконструировать объект, установить некоторые параметры и затем прочитать данные (особенно с возможностью повторить попытку при сбое чтения), подойдет и отдельный метод. Так что вариант 2 тоже хорошая возможность. Может быть, даже лучше, в зависимости от вкуса.

Так что, пока вы не выберете 3, выберите тот, который вам наиболее удобен. :)

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