Тип возврата T не может быть возвращен как null? С# Generics
У меня есть метод, который в общем случае десериализует сохраненный объект из предоставленного пользователями пути и типа объекта. Метод работает нормально, за исключением случаев, когда пользователь предоставляет недопустимый путь к файлу. Я хотел бы, чтобы мой метод возвращал null в этом случае, но когда я пытаюсь вернуть null, я получаю ошибку компиляции. Я попытался использовать тип с нулевым значением, но получаю ошибку компиляции. Вместо этого я запускаю объект и возвращаю его, но он вызывает ошибку времени выполнения. Я хотел бы знать, знает ли кто-нибудь правильный способ разрешить возврат null. Код выглядит следующим образом:
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;
}
}
После рассмотрения комментариев качества Eric Lippert к рассмотрению я пересмотрел метод, чтобы он выглядел так, как вы видите ниже. Преимущество использования "использования" заключается в том, что он автоматически генерирует блок 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;
}
}
Ответы
Ответ 1
Я бы решил проблему, не написав этот код в первую очередь.
Метод должен делать одно и делать это хорошо; вы смешиваете код десериализации с кодом сообщения об ошибках.
Не делай этого. Лучше всего было бы, чтобы метод десериализации выдавал исключение и записывал другой код, который обрабатывает ошибки и ошибки отчетов пользователю.
В более общем плане, опасно иметь метод, который ест исключения и затем возвращает фиктивные данные. Это просто создает проблемы на линии, когда ничего не подозревающий код, который вызывает ваш метод, ожидает получения хороших данных.
Пока мы говорим о качестве кода, вы должны использовать "использование" блоков, чтобы гарантировать, что дескрипторы файлов будут закрыты, если произойдет исключение. Не делайте явно fs.Close()
- скорее, do using(var fs = ... )
, и пусть компилятор генерирует удаление, которое закрывает файл.
Ответ 2
Если вы собираетесь работать только с классами, добавьте ограничение where T : class
:
public static T RestoreObj<T>(string datafile) where T : class
Если вы также хотите десериализовать структуры, просто верните default(T)
. Это будет null
для ссылочных типов, а значение по умолчанию (обычно 0) для структур. Как указывает @JMH, default(Nullable<T>)
является null
-содержащим значением, допустимым.
Ответ 3
Вы можете использовать default(T)
вместо null
, который будет null
для ссылочного типа и значения по умолчанию для типов значений.
Ответ 4
Не все типы могут быть установлены на null
.
Вам нужно ограничить T
:
public static T RestoreObj<T>(string datafile) where T : class
Другой вариант (если вы не строго работаете с классами) должен возвращать default(T)
, а не null.
Ответ 5
Ограничьте ограничение на 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
Ответ 6
Если вы объявите свой метод следующим образом, вы должны вернуть null
:
public static T RestoreObj<T>(string datafile) : where T class
T
также может быть структурой, которая не может быть null
. Если вы ограничиваете T
классом, он должен быть ссылочным типом, и вам разрешено возвращать null
.
Ответ 7
Вы можете использовать следующее, чтобы проверить, существует ли файл и вернуть значение по умолчанию для этого типа.
if (!File.Exists(FilePath))
return default(T);
Используя это, вы получите значение по умолчанию для этого типа. Например, если вы укажете ему тип 'int', он вернет 0 вместо null.