如何在C#中处理多个相同的类

时间:2018-05-23 15:42:46

标签: c#

我正在使用具有多个不同“服务”的客户端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#中做到这一点?我可以创建一个从这些中继承的包装类并实现接口,但是我仍然遇到每个命名空间/服务的重复代码。

有没有更清洁的方法来解决这个问题?

4 个答案:

答案 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));
            }
        }
    }
}

现在您可以按正常方式访问这些属性。

See my example on DotNetFiddle