是否可以在空检查中推断类型的名称
Is it possible to infer the name of a type on a null check
我正在玩一个 webapi2 项目。
使用控制器 class ->
调用处理业务逻辑的服务 class,->
它使用处理数据库调用的存储库。
为了可读性,我决定在我的服务 class 中进行空检查(即:
var object = _repository.GetById(5) ?? throw new CustomException(CustomException.Object1NotFound_Exception_Message);
).
这样我的控制器逻辑保持清晰和可读,避免在控制器方法中进行这些检查[get/post/put/delete]。
这样,我可以 try/catch 我的控制器逻辑,并捕获(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 的开关,但由于编译器的原因它不起作用(我的理解是,如果继承起作用,编译器不可能告诉我需要哪个类型名)
为了规避这个我做了这个 class:
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,则无法反映对象的实例。
我一直在为这个问题伤脑筋。
我希望有人能阐明这个问题,或者向我解释为什么这是一个糟糕的解决方案。
因为我开始认为这种方法是一种明确的代码味道。
(我知道这种方法不会 return IActionResult 中的异常消息。这也是一个问题,但超出了这个问题的范围。)
我非常感谢在这个问题上的帮助。
您可以尝试使用泛型,并创建一个辅助函数来为您进行检查。
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));
直接的答案是否定的,你不能做你想做的事。如果因为函数返回 null 而抛出异常,则无法检查本应返回的对象的类型。
您只知道 声明的 类型 GetById
returns。换句话说,如果该函数声明为
Foo GetById(int id)
那你就知道returns是什么Foo
了。如果你得到一个结果,你可以检查它,看看它的类型是 Foo
还是从 Foo
继承的其他东西。但如果你没有得到结果,你所能知道的就是它会是一个 Foo
。但由于您要的是 Foo
,这是唯一重要的类型。
换句话说,不需要推断方法 returns 的类型。它声明它 returns 的类型。您知道类型是什么,因为您正在调用该方法来获取该类型的对象。如果您还不知道类型是什么,您就没有理由调用该方法。
既然您知道类型,并且一个异常消息与下一个异常消息唯一不同的细节就是类型,下一步就是弄清楚如何在异常消息中传达类型。
老实说,这是我们经常想得太多的事情。你可能对此没问题:
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>();
但这会导致异常的复杂层次结构。除非您或其他人要捕获此特定异常类型并以不同于其他异常类型的方式处理它,否则它只会增加额外的复杂性而没有任何好处。
我知道我们对这类事情很着迷,但这不是您申请中的重要部分。这不值得。我会在您需要的地方硬编码这些简短的异常消息。
我正在玩一个 webapi2 项目。 使用控制器 class ->
调用处理业务逻辑的服务 class,->
它使用处理数据库调用的存储库。 为了可读性,我决定在我的服务 class 中进行空检查(即:
var object = _repository.GetById(5) ?? throw new CustomException(CustomException.Object1NotFound_Exception_Message);
).
这样我的控制器逻辑保持清晰和可读,避免在控制器方法中进行这些检查[get/post/put/delete]。
这样,我可以 try/catch 我的控制器逻辑,并捕获(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 的开关,但由于编译器的原因它不起作用(我的理解是,如果继承起作用,编译器不可能告诉我需要哪个类型名)
为了规避这个我做了这个 class:
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,则无法反映对象的实例。
我一直在为这个问题伤脑筋。 我希望有人能阐明这个问题,或者向我解释为什么这是一个糟糕的解决方案。 因为我开始认为这种方法是一种明确的代码味道。
(我知道这种方法不会 return IActionResult 中的异常消息。这也是一个问题,但超出了这个问题的范围。)
我非常感谢在这个问题上的帮助。
您可以尝试使用泛型,并创建一个辅助函数来为您进行检查。
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));
直接的答案是否定的,你不能做你想做的事。如果因为函数返回 null 而抛出异常,则无法检查本应返回的对象的类型。
您只知道 声明的 类型 GetById
returns。换句话说,如果该函数声明为
Foo GetById(int id)
那你就知道returns是什么Foo
了。如果你得到一个结果,你可以检查它,看看它的类型是 Foo
还是从 Foo
继承的其他东西。但如果你没有得到结果,你所能知道的就是它会是一个 Foo
。但由于您要的是 Foo
,这是唯一重要的类型。
换句话说,不需要推断方法 returns 的类型。它声明它 returns 的类型。您知道类型是什么,因为您正在调用该方法来获取该类型的对象。如果您还不知道类型是什么,您就没有理由调用该方法。
既然您知道类型,并且一个异常消息与下一个异常消息唯一不同的细节就是类型,下一步就是弄清楚如何在异常消息中传达类型。
老实说,这是我们经常想得太多的事情。你可能对此没问题:
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>();
但这会导致异常的复杂层次结构。除非您或其他人要捕获此特定异常类型并以不同于其他异常类型的方式处理它,否则它只会增加额外的复杂性而没有任何好处。
我知道我们对这类事情很着迷,但这不是您申请中的重要部分。这不值得。我会在您需要的地方硬编码这些简短的异常消息。