当您不使用异常来控制流时,代码如何显示?

Nic*_*arr 8 c# exception

我已经在其他回答的问题中看到了关于何时抛出异常的建议,但现在我的API有了新的噪音.我没有调用try/catch块中包含的方法(烦恼的异常),而是使用可能在处理过程中发生的错误集合的参数参数.我理解为什么在try/catch中包装所有内容是控制应用程序流程的一种不好的方法,但我很少看到反映这种想法的代码.

这就是为什么整件事对我来说似乎很奇怪.这种做法应该是正确的编码方式,但我没有在任何地方看到它.除此之外,我还不太清楚当"坏"行为发生时如何与客户端代码相关联.

以下是我正在讨论的一些代码片段,用于保存由网络应用用户上传的图片.不要让细节出汗(这很难看),只要看看我将这些输出参数添加到所有内容以获取错误消息的方式.

public void Save(UserAccount account, UserSubmittedFile file, out IList<ErrorMessage> errors)
{
    PictureData pictureData = _loader.GetPictureData(file, out errors);

    if(errors.Any())
    {
        return;
    }

    pictureData.For(account);

    _repo.Save(pictureData);
}
Run Code Online (Sandbox Code Playgroud)

这是正确的想法吗?我可以合理地期望用户提交的文件在某种程度上是无效的,所以我不应该抛出异常,但是我想知道文件有什么问题,所以我产生错误消息.同样,任何现在使用此保存方法的客户端也希望找出整个图片保存操作的错误.

关于返回包含结果和其他错误消息的状态对象,我有其他想法,但这感觉很奇怪.我知道在任何地方都有参数很难维护/重构/等.

我会喜欢这方面的指导!

编辑:我认为用户提交的文件片段可能会让人们想到加载无效图像和其他"硬"错误所产生的异常.我认为这段代码片段更好地说明了我认为抛出异常的想法在哪些方面是不受欢迎的.

有了这个,我只是保存一个新的用户帐户.我在用户帐户上进行状态验证,然后点击持久存储以查明是否已使用用户名.

public UserAccount Create(UserAccount account, out IList<ErrorMessage> errors)
{
    errors = _modelValidator.Validate(account);

    if (errors.Any())
    {
        return null;
    }

    if (_userRepo.UsernameExists(account.Username))
    {
        errors.Add(new ErrorMessage("Username has already been registered."));
        return null;
    }

    account = _userRepo.CreateUserAccount(account);

    return account;
}
Run Code Online (Sandbox Code Playgroud)

我应该抛出某种验证异常吗?或者我应该返回错误消息?

And*_*ite 9

尽管存在性能问题,但我认为允许异常被抛出方法实际上更加清晰.如果在您的方法中有任何可以处理的异常,您应该适当地处理它们,否则,让它们冒泡.

返回out参数中的错误或返回状态代码感觉有点笨拙.有时遇到这种情况时,我会想象.NET框架如何处理错误.我不相信有很多.NET框架方法可以在out参数中返回错误,或者返回状态代码.


Rex*_*x M 7

根据定义,"例外"是指例行程序无法恢复的特殊情况.在您提供的示例中,看起来这意味着图像无效/损坏/不可读/等.这应该被抛出,并冒泡到最顶层,并且决定如何处理异常做.异常本身包含有关出错的最完整信息,这些信息必须在较高级别提供.

当人们说你不应该使用异常来控制程序流时,他们的意思是:(例如)如果用户试图创建一个帐户但帐户已经存在,你不应该抛出一个AccountExistsException,然后在应用程序能够向用户提供反馈,因为已存在的帐户不是特例.您应该期望这种情况并将其作为正常程序流程的一部分来处理.如果您无法连接到数据库,是一个例外情况.

您的用户注册示例的部分问题在于您尝试将太多内容封装到单个例程中.如果你的方法试图做不止一件事,那么你必须跟踪多个事物的状态(因此事情变得丑陋,比如错误信息列表).在这种情况下,你可以做的是:

UsernameStatus result = CheckUsernameStatus(username);
if(result == UsernameStatus.Available)
{
    CreateUserAccount(username);
}
else
{
    //update UI with appropriate message
}

enum UsernameStatus
{
    Available=1,
    Taken=2,
    IllegalCharacters=3
}
Run Code Online (Sandbox Code Playgroud)

显然这是一个简化的例子,但我希望这一点很明确:你的例程应该只尝试做一件事,并且应该有一个有限的/可预测的操作范围.这使得更容易停止和重定向程序流以处理各种情况.