是否可以在空检查中推断类型的名称

问题描述 投票:1回答:2

我正在玩一个webapi2项目。使用控制器类 - >

调用处理业务逻辑的服务类, - >

它使用处理数据库调用的存储库。为了便于阅读,我决定在我的服务类中使用nullchecks(即:

var object = _repository.GetById(5) ?? throw new CustomException(CustomException.Object1NotFound_Exception_Message);

).

这样我的控制器逻辑保持清洁和可读,避免在控制器方法[get / post / put / delete]中进行这些检查。

这样,我可以尝试/捕获我的控制器逻辑,并捕获(customexception ex)并调用扩展方法ex.converttostatuscoderesult。 (如下图所示)。

public class CustomException : Exception
{
    public const string Object1NotFound_Exception_Message = "Object not found using ID.";
    public const string Object2NotFound_Exception_Message = "Object2 not found using ID.";
    public const string UserNotAllowedX_Exception_Message = "Current user not allowed to do X.";
    public const string UserNotAllowedY_Exception_Message = "Current user not allowed to do Y.";
    <~even more strings containing ExceptionMessages> 

    public int ExceptionStatusCodeDefinition { get; set; }

    public CustomException(string message) : base(message)
    {
        switch (message)
        {
            case Object1NotFound_Exception_Message:
            case Object2NotFound_Exception_Message:

                ExceptionStatusCodeDefinition = 404;
                break;
            case UserNotAllowedX_Exception_Message:
            case UserNotAllowedY_Exception_Message:
            case UserNotAllowedZ_Exception_Message:
                ExceptionStatusCodeDefinition = 403;
                break;
            default:
                ExceptionStatusCodeDefinition = 400;
                break;
        }
    }
}

public static class CustomExceptionExtention
{
    public static IActionResult ConvertToStatusCodeResult(this CustomException exception)
    {
        return new Microsoft.AspNetCore.MvcStatusCodeResult(exception.ExceptionStatusCodeDefinition);
    }
}

但是,此方法需要事先设置异常消息。这不可避免地意味着我有一个太长的列表与异常消息。

我尝试重构这个尝试推断类型的名称并有一个异常消息NotFound_Exception_Message。并在运行时附加类型名称。

起初我尝试了一个Type的开关,由于编译器的原因(我理解它的方式,如果继承发挥作用,编译器不可能告诉我需要哪个类型名称),它不起作用

试图绕过这个我做了这个课:

 public class TypeCase
{

 public static TypeCase GetType(Type type)
    {
        return new TypeCase(type);
    }

    public string TypeName { get; set; }

    public TypeCase(object type)
    {
        TypeName = type.GetType().Name;
    }
}

只要对象具有值,这就可以正常工作,因为如果该对象引用为null,则无法反映对象的实例。

我一直在解决这个问题。我希望有人可以解决这个问题,或者向我解释为什么这是一个糟糕的解决方案。因为我开始认为这种方法是一种明确的代码味道。

(我知道这种方法不会在IActionResult中返回异常消息。这也是一个问题,但超出了这个问题的范围。)

我非常感谢这个问题的帮助。

c# error-handling asp.net-web-api2
2个回答
1
投票

直接的答案是否定的,你不能做你想做的事。如果由于函数返回null而抛出异常,则无法检查将返回的对象的类型。

你所知道的是GetById返回的声明类型。换句话说,如果该函数声明为

Foo GetById(int id)

然后你知道它返回的是一个Foo。如果你得到一个结果,你可以检查它,看它的类型是Foo还是继承自Foo的其他东西。但如果你没有得到一个结果,你所能知道的就是它本来是一个Foo。但既然你要求Foo,那是唯一重要的类型。

换句话说,不需要推断该方法返回的类型。它声明了它返回的类型。您知道类型是什么,因为您正在调用方法来获取该类型的对象。如果您还不知道该类型是什么,则没有理由调用该方法。

由于您知道类型,并且唯一的细节是下一个异常消息是下一个类型,下一步是弄清楚如何在异常消息中传递类型。

说实话,这是我们经常讨论的事情。你可能对此感到满意:

var object = _repository.GetById(5) ?? throw new CustomException("Foo not found using ID.");

真的,有多糟糕?即使消息只是“Foo not found”,堆栈跟踪也会显示方法,然后您可以确定它正在尝试使用ID检索它。

使用常量是很好的,但是当值具有一些重要意义时,它更为重要。如果你的下一个例外有一个拼写错误 - “Blag not foound using ID” - 它会很混乱,但它不会破坏任何东西。如果消息更长并且重复,我还可以看到使用常量。

这是我迄今为止的第一个建议。如果你真的想确保你的异常消息是常量的,只在一个地方声明,并且你正在创建自定义异常,你可以做这样的事情(虽然我真的,真的不会。)

// Really, don't do this.
public class ItemNotFoundByIdException<T> : Exception
{
    public ItemNotFoundByIdException()
    :base($"{typeof(T).Name} not found by ID.") { }
}

然后,如果你想通过ID获得Foo,你可以这样做:

var foo = _repository.GetById(5) ?? throw new ItemNotFoundByIdException<Foo>();

但这会导致复杂的异常层次结构。除非您或其他人要捕获此特定异常类型并以不同于其他异常类型的方式处理它,否则它只是额外的复杂性而没有任何好处。

我知道我们如何对这类事情着迷,但这并不是你应用程序的重要部分。这不值得。我只需要在需要它们的地方对这些简短的异常消息进行硬编码。


0
投票

您可以尝试使用泛型,并创建一个帮助函数来为您进行检查。

public static T GetWithNullCheck<T>(Func<T> fetchFunc)
{
    T t = fetchFunc();

    if (t != null) return t;

    var typeOfT = typeof(T);
    var typeName = typeOfT.Name;

    throw new CustomException($"{typeName} not found.");

    // short version
    // return fetchFunc() ?? throw new CustomException($"{typeof(T).Name} not found.");
}

你可以像这样使用它

var object = GetWithNullCheck(() => _repository.GetById(5));
© www.soinside.com 2019 - 2024. All rights reserved.