Тип возврата T не может быть возвращен как ноль?C # Generics - PullRequest
10 голосов
/ 08 сентября 2011

У меня есть метод, который в общем случае десериализует хранимый объект из предоставленного пользователями пути к файлу и типа объекта. Метод работает нормально, за исключением случаев, когда пользователь указывает неверный путь к файлу. Я хотел бы, чтобы мой метод возвращал ноль в этом случае, но когда я пытаюсь вернуть ноль, я получаю ошибку компиляции. Я пытался использовать обнуляемый тип, но получил ошибку компиляции. Вместо этого я печатаю объект и возвращаю его, но это вызывает ошибку во время выполнения. Я хотел бы знать, если кто-нибудь знает правильный способ разрешить возвращать нуль. Код выглядит следующим образом:

        public static T RestoreObj<T>(string datafile)
    {


        try
        {
            var fs = File.OpenRead(datafile);
            var bf = new BinaryFormatter();
            var obj = (T) bf.Deserialize(fs);
            fs.Close();
            return obj;
        }
        catch (Exception e)
        {
            MessageBox.Show("Could not load. Accepts valid *.dom files only. " + e);

            // TODO: how to do this? this will throw a runtime error, and if null returned, a compilation error
            var o = new object();
            return (T) o;
        }
    }

После рассмотрения качественных комментариев Эрика Липперта я пересмотрел метод, чтобы он выглядел так, как показано ниже. Преимущество использования 'using' состоит в том, что он автоматически генерирует блок try..finally, который будет вызывать метод dispose (FileStream реализует IDisposable, если это не так, это будет ошибкой компиляции). Еще одна приятная вещь заключается в том, что выбрасываемое исключение относится к тому, что на самом деле происходит, а не к тому, что я описал выше.

        public static T RestoreObj<T>(string datafile) 
    {
        using (var fs = File.OpenRead(datafile))
        {
            var bf = new BinaryFormatter();
            var obj = (T)bf.Deserialize(fs);
            return obj;
        }
    }

Ответы [ 7 ]

20 голосов
/ 08 сентября 2011

Если вы собираетесь работать только с классами, добавьте ограничение where T : class:

public static T RestoreObj<T>(string datafile) where T : class

Если вы также собираетесь десериализовать структуры, просто верните default(T).Это будет null для ссылочных типов и значение по умолчанию (обычно 0) для структур.Как указывает @JMH, default(Nullable<T>) является null -содержащим обнуляемым.

11 голосов
/ 08 сентября 2011

Я бы решил проблему, не написав этот код в первую очередь.

Метод должен делать одну вещь и делать это хорошо;Вы смешиваете код десериализации с кодом сообщения об ошибках .

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

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

Пока мы говорим о качестве кода, вы должны использовать блоки "using", чтобыдескрипторы файла закрываются, если происходит исключение.Не делайте явно fs.Close() - скорее, сделайте using(var fs = ... ) и позвольте компилятору сгенерировать удаление, которое закрывает файл.

4 голосов
/ 08 сентября 2011

Вы можете использовать default(T) вместо null, что будет null для ссылочного типа и значение по умолчанию для типов значений.

2 голосов
/ 08 сентября 2011

Не для всех типов можно установить null.

Вы должны ограничить T:

public static T RestoreObj<T>(string datafile) where T : class

Ваш другой вариант (если вы не работаете строго с классами)) должен вернуть default(T), а не ноль.

1 голос
/ 08 сентября 2011

Если вы объявите свой метод таким образом, вы сможете вернуть null:

public static T RestoreObj<T>(string datafile) : where T class

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

1 голос
/ 08 сентября 2011

Установить ограничение на T как:

 public static T RestoreObj<T>(string datafile) where T : class
                                               //^^^^^^^^^^^^^^

Это означает, что вы можете вызывать этот метод с помощью T, который является ссылочным типом, который может быть null. Вы не можете вызывать этот метод, когда T является типом значения, хотя вы вызываете его, когда T равно Nullable<V>:

class  R {} //Reference type!
struct V {} //Value type!

 Xyz.RestoreObj<R>("abc"); //ok
 Xyz.RestoreObj<V>("abc"); //compilation error
 Xyz.RestoreObj<Nullable<V>>("abc"); //ok
0 голосов
/ 08 сентября 2011

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

            if (!File.Exists(FilePath))
            return default(T);

Используя это, вы получите значение по умолчанию для типа. Например, если вы дадите ему тип 'int', он вернет 0 вместо нуля.

...