Подтвердить что ты не робот

Каков наиболее подходящий способ обработки поврежденных входных данных в конструкторе С#?

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

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

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

Вариант 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;
}
4b9b3361

Ответ 1

Я бы сделал что-то по линии варианта 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);

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

Обновление. Как уже упоминалось в комментариях, лучший шаблон:

    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;
    }

Ответ 2

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

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

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

Ответ 3

Оба варианта №1 и №3 являются хорошим выбором и распространены в инфраструктуре .Net. Он также распространен для обеспечения того же типа. Рассмотрим 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
  }
}

Ответ 4

Из Руководство по дизайну конструктора Microsoft (MSDN),

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

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


Factory Метод не подходит для решения этой проблемы. См. Конструкторы vs Factory Методы

От Руководства по дизайну рамок: соглашения, идиомы и шаблоны для многоразовых библиотек .NET

5.3 Конструктор Конструктор

Рассмотрите возможность использования статического метода Factory вместо конструктора, если семантика желаемой операции не сопоставляется непосредственно с конструкцией нового экземпляра или, если следовать рекомендациям по дизайну конструктора чувствует себя неестественно.

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


Реализации .NET BCL действительно генерируют исключения из конструкторов

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

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

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


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

Ответ 5

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

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

Итак, пока вы не выбираете 3, выберите тот, с которым вы наиболее комфортно.:)