我正在使用具有多个不同“服务”的客户端API(到目前为止大约10个),每个都作为自己的命名空间导入。它们的标准API调用模式的一部分涉及返回错误消息数组:
public class Error {
public String ErrorMessage {get;set}
public int errorNumber {get;set}
..etc
}
我一直在努力清理和统一我们对这些消息的处理。我试图用一个函数来处理它们,例如:
void CheckErrors(List<Error> errors) {
if(errors != null && errors.Count() > 0)
throw new Exception(errors.First().ErrorMessage);
}
(实际功能更复杂,但这提供了一般的想法)
然而,事实证明,他们的每个服务都有自己(相同)的Error类定义。在C ++中,我可以模拟这个函数并且它可以正常工作,或者以动态语言工作,但是在C#中我没有能够找到一种方法来做到这一点而不用10+副本的相同功能,每个在Error类型上都有不同的命名空间。
在我自己的代码中,我可以为这些类添加一个接口,但由于它不是我的代码,我认为你不能在C#中做到这一点?我可以创建一个从这些中继承的包装类并实现接口,但是我仍然遇到每个命名空间/服务的重复代码。
有没有更清洁的方法来解决这个问题?
答案 0 :(得分:8)
您可以考虑使用反射或dynamic
使用后期绑定解决方案。两者都有相同的缺点:你的编译时间类型安全性很差,但如果它是一个非常孤立和包含的代码片段,它应该是可以容忍的:
反射
void CheckErrors(List<object> errors) {
if(errors != null && errors.Count() > 0)
{
var firstError = errors.First();
throw new Exception(
firstError.GetType()
.GetProperty("ErrorMessage")
.GetValue(firstError)
.ToString()); }
动态
void CheckErrors(List<dynamic> errors) {
if(errors != null && errors.Count() > 0)
throw new Exception(errors.First().ErrorMessage); }
答案 1 :(得分:8)
请耐心等待......您可能需要另一个Error
类,其属性与其Error
类相同,但您在命名空间中定义。
您的方法CheckErrors
使用您对Error
的定义。
最后,您可以使用AutoMapper在您的Error
类型与您的类型之间进行映射。这正是AutoMapper的设计目标。由于您的所有合同都是相同的,因此AutoMapper配置应该是微不足道的。当然,你会产生一些映射的运行时费用,但我认为这会导致最干净的静态类型解决方案,因为你无法改变它们的接口。
AutoMapper配置+用法将如下所示:
//See AutoMapper docs for where to put config, it shouldn't happen on every call
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TheirApi.Error, MyNamespace.MyErrorDefinition>();
}
var mapper = config.CreateMapper();
MyErrorDefinition myErrors = mapper.Map<List<MyErrorDefinition>>(listOfTheirErrorObjects);
CheckErrors(myErrors);
答案 2 :(得分:4)
另一种方法是使用lambdas:
void CheckErrors<T>(IEnumerable<T> errors, Func<T,string> getMessage)
{
if (errors?.Count() > 0) throw new Exception(getMessage(errors.First()));
}
然后这样称呼:
CheckErrors(errors, (e) => e.ErrorMessage);
答案 3 :(得分:0)
我会定义自己的Error类,它有一个构造函数,它接受来自供应商的任何错误对象并将其转换。例如:
<?php
function str_between($string, $start, $end, $flags = "mi") {
$pattern = "/(?<={$start})(.*)(?={$end})/{$flags}";
preg_match($pattern, $string, $matches);
return $matches[0];
}
$string1 = "...This is a very long an complex string .
This is a very long string. This is a very long EXAMPLE string.
This is a very long string. This is a very long string.";
var_dump(str_between($string1, "a", "EXAMPLE"));
然后,如果您的第三方库中有错误列表,则可以使用LINQ转换它:
public class Error
{
public string Message { get; private set; }
public int ErrorNumber { get; private set; }
public Error(object vendorError)
{
var t = vendorError.GetType();
foreach (var source in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
foreach (var dest in typeof(Error).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (dest.Name != source.Name) continue;
if (dest.PropertyType != source.PropertyType) continue;
dest.SetValue(this, source.GetValue(vendorError, null));
}
}
}
}
现在您可以按正常方式访问这些属性。