通过强制转换或鸭式输入不同的对象类型来重用相同的功能

Reusing the same function by casting, or duck typing different object types

在 .NET 应用程序中,我正在使用两个远程 WCF 服务,它们都具有 "same" 对象的定义:CoreService.CustomerProductService.Customer.

"same" 特意放在引号里;从名称空间的角度来看,它们是两个不同的实体。然而,这是纯粹的,因为服务的方式 generated/consumed。在这种情况下,两个对象都来自后端系统中的同一个库是一个给定的事实。

在特定情况下,我需要从两种对象类型中提取内容。我有一个最初为一个特定实例构建的函数:

private static string _ExtractFoo(CoreService.Customer customer) {
    // removed for the sake of brevity
    return string.Empty;
}

我想要的实际上是重用相同的操作,通过提供重载并通过转换或装箱的方式简单地尝试说服编译器和运行时这将起作用(简单地想 duck typing 如果你会的)。

以下情况不适用:

private static string _ExtractFoo(ProductService.Customer customer) {

    // #1 - Cast, results in error:
    //      Cannot convert type ... via a built-in conversion
    return _ExtractFoo((CoreService.Customer) customer);

    // #2 - Safe cast, results in error:
    //      Cannot convert type ... via a built-in conversion
    return _ExtractFoo(customer as CoreService.Customer);

    // #3 - Works for compiler, breaks at runtime where 'casted' is null
    dynamic d = customer;
    var casted = d as CoreService.Customer;
    return _ExtractFoo(casted);
}

一个简单的修复 确实有效 首先序列化到 json:

private static string _ExtractFoo(ProductService.Customer customer) {
        // awkward hack - but it blends!
        var serialized = JsonConvert.SerializeObject(customer);
        var deserialized = JsonConvert.DeserializeObject<CoreService.Customer>(serialized);

        return _ExtractFoo(deserialized);
}

考虑到这两个对象的属性和值保证是匹配的,所以这样做是有道理的。尽管如此,这很昂贵而且似乎完全没有必要。

另一种选择是使用 implicit conversion operator。但是,考虑到对象是服务生成的,我不太了解如何使用运算符扩展这两个对象。

重点是不要争论这是否是最佳做法。也不知道如何找到替代方案,例如在不同服务引用之间重用相同的共享对象。我很清楚这个 hack 的尴尬。可以说,从语言的角度来看,我觉得这是一个有趣的挑战。

这让我想到了真正的问题:是否有 更优雅的方法 来欺骗编译器吞下它,或者更确切地说,制作更便宜的 cast/boxing 在两个 "different but the same" 对象之间,允许我重用 _ExtractFoo() 实现?

更新 I - 让外部网络服务使用通用接口不是一种选择。另外,最好知道 Customer 对象具有相当深的嵌套属性和子对象层次结构;使用 AutoMapper 或手动地图之类的东西会很麻烦(更不用说容易出错)。

更新 II - 为了将来的参考,我试图解释我的 problem/question 是我如何修改 _ExtractFoo() 方法 - 或者它的实现 - 因此它可以应用于 CoreService.CustomerProductService.Customer(考虑到以上所有内容)。它绝对不是 "please list all other alternatives" 意义上的开放性问题,尽管在我看来作为答案提供的内容作为选项肯定是可行的。

在我脑海中,你的选择是:

  1. 获取两个源 类 以实现相同的接口并传递它而不是具体类型。这将是更好的选择,但我猜这里不可能。
  2. 反序列化并返回序列化以在类型之间进行转换。你已经有了这段代码,但就像你说的那样它可能很慢。
  3. 使用 AutoMapper 等映射库在类型之间进行转换。这非常快,但需要您从 Nuget 引入外部库(我已经多次使用 AutoMapper)
  4. 自己手动映射属性。这可能是最快的代码,但写起来很糟糕。
  5. 在整个链条中使用 dynamic,而不仅仅是在顶部。你失去了编译时类型检查,但它应该相当快。例如,而不是像这样的函数:

    public static string _ExtractFoo(ProductService.Customer customer)
    {
        return customer.DoSomethingExciting();
    }
    

    你会得到这个:

    public static string _ExtractFoo(dynamic customer)
    {
        return customer.DoSomethingExciting();
    }
    

    如果需要,您可以添加一些检查以确保 customerProductService.CustomerCoreService.Customer,如果您想要一些安全的话。