23 c# parsing constructor exception
我正在从文件中读取数据并根据此数据创建对象.数据格式不受我的控制,偶尔也会损坏.在C#中构造对象时,最合适的处理这些错误的方法是什么?
在其他编程语言中,我返回了一个null,但这似乎不是C#的一个选项.
我已经设法找出以下选项,但我希望更有经验的C#程序员的建议:
选项1.读取构造函数内的文件,并在源数据损坏时抛出异常:
try
{
obj = Constructor(sourceFile);
... process object ...
}
catch (IOException ex)
{
...
}
Run Code Online (Sandbox Code Playgroud)
选项2.创建对象,然后使用方法从源文件中读取数据:
obj = Constructor();
obj.ReadData(sourceFile);
if (obj.IsValid)
{
... process object ...
}
Run Code Online (Sandbox Code Playgroud)
或者可能在出错时抛出异常:
obj = Constructor();
try
{
obj.Read(sourceFile);
... process object ...
}
catch
{
...
}
Run Code Online (Sandbox Code Playgroud)
选项3.使用静态TryParse方法创建对象:
if (ObjClass.TryParse(sourceFile, out obj))
{
... process object ...
}
Run Code Online (Sandbox Code Playgroud)
如果是这样,我应该在内部使用选项1实现选项3吗?
public static bool TryParse(FileStream sourceFile, out ObjClass obj)
{
try
{
obj = Constructor(sourceFile);
return true;
}
catch (IOException ex)
return false;
}
Run Code Online (Sandbox Code Playgroud)
Chr*_*Wue 19
我会按照选项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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后像这样使用它:
ObjClass.CreateFromFile(sourcefile);
Run Code Online (Sandbox Code Playgroud)
通常,构造函数应该将所有基本上定义类的属性作为参数.执行重量级计算(如解析文件)最好留给工厂方法,因为构造函数通常不希望执行复杂且可能长时间运行的任务.
更新:如评论中所述,更好的模式是:
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;
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*ngs 13
我不会把任何东西放到可能抛出异常的构造函数中 - 除非出现问题.
如果构造函数具有可能的返回值而不是有效对象,则应该封装它.
最安全的方法可能是创建一个工厂方法(类中的公共静态函数接受文件引用并返回该类的新实例或null).此方法应首先验证文件及其数据,然后才创建新对象.
如果文件数据具有简单结构,则可以先将其加载到某个局部变量中,然后使用此数据构造对象.否则,您仍然可以决定 - 在您的工厂方法内 - 如果您想要尝试/捕获构造或使用上述任何其他点.
选项#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
}
}
Run Code Online (Sandbox Code Playgroud)
如果合适,请从实例构造函数中抛出异常.
构造函数应该像任何方法一样抛出和处理异常.具体来说,构造函数不应该捕获并隐藏它无法处理的任何异常.
Factory Method不是解决此问题的正确方法.请参阅构造函数与工厂方法
5.3构造函数设计
如果所需操作的语义不直接映射到新实例的构造,或者遵循构造函数设计指南感觉不自然,请考虑使用静态工厂方法而不是构造函数.
如果合适,请从实例构造函数中抛出异常.
.NET BCL实现会从构造函数中抛出异常
例如,列表构造函数(Int32)在列表的capacity参数为负数时抛出ArgumentOutOfRangeException.
var myList = new List<int>(-1); // throws ArgumentOutOfRangeException
Run Code Online (Sandbox Code Playgroud)
同样,构造函数在读取文件时应抛出适当类型的异常.例如,如果文件在指定位置不存在,它可能抛出FileNotFoundException等.
更多信息