【问题标题】:How to 'sneak' interface behind 3rd party objects如何“潜入”第 3 方对象背后的界面
【发布时间】:2015-01-09 10:05:51
【问题描述】:

我正在与C#.NET 4.5 合作。
我有 2 个对象(实际上更多,但为了简单起见,我们坚持使用两个),它们是独立的实体,都来自 3rd 方库,但是它们确实很少有共同的属性。

我想做一个抽象机制来处理这些属性。如果这些对象是我的,我可以通过添加界面轻松完成

class Foo : IFooBar
{
    public string A { get; set; }
    public string B { get; set; }
}

class Bar : IFooBar
{
    public string A { get; set; }
    public string B { get; set; }
}

interface IFooBar
{
    string A { get; set; }
    string B { get; set; }
}

public static class Extensions
{
    public static IFooBar ProcessedFoobars(IFooBar fooBar)
    {
    ...(do things to A and B)
    }
}

但是,由于它们来自第 3 方,我没有(不知道)将它们放在接口后面的方法。

我看到 ATM 的选项:

  1. FooBar转换为MyFooMyBar,它们是我的内部对象,将MyFooMyBar放在接口后面并以这种方式处理它们

  2. 使用只接受属性作为输入的方法。

    Tuple<string, string> DoThings(string A, string B)
    {
    ...(do things to A and B)
    }
    

这将涉及来自第 3 方对象的每种风格的大量映射。

  1. 此时我倾向于使用反射。

    public T FooBarProcessor<T>(T fooBar)
    {
        var type = typeof (T);
        var propertyA = type.GetProperty("A");
        var propertyB = type.GetProperty("B");
        var a = propertyA.GetValue(fooBar, null);
        var b = propertyB.GetValue(fooBar, null);
        ... (do things to A and B)
        propertyA.SetValue(fooBar, a);
        propertyB.SetValue(fooBar, b);
        return fooBar;
    }
    

有没有办法在 3rd 方对象(或其他一些解决方法)后面“偷偷摸摸”接口,让我可以让多个对象看起来好像它们在接口后面,所以我可以同时处理它们方式。

是什么让我希望可以做到这一点 - PostSharp 确实允许在付费版本中执行“方面继承”(我自己没有尝试过,所以可能会有所不同)和如果他们以某种方式这样做 - 那么这可以做到。

【问题讨论】:

  • PostSharp 从头开始​​修改和重建程序集。您别无选择,只能将第 3 方对象包装在您自己的对象中,您可以在其中添加接口。
  • PostSharp '修补' IL - 你不想做任何事情......
  • 如果不修改程序集,我认为这是不可能的。您可以选择创建自己的代理类(使用您的通用接口)调用第三方类。
  • @LIUFA 嗯...总是有dynamic,这将允许您针对类型进行回避类型,但这会将所有问题(例如缺少属性或不同类型)转移到运行时并在您的应用程序中涉及 DLR,您可能希望也可能不希望在这种微不足道的情况下调用它。我个人要么包装类型(适配器答案),要么让方法直接获取属性。

标签: c# interface .net-4.5


【解决方案1】:

你需要的是adapter pattern

您可以创建实现您的接口的类并在后台使用 Foo & Bar:

interface IFooBar
{
    string A { get; set; }
    string B { get; set; }
}

class FooAdapter : IFooBar
{
    private readonly Foo _foo;

    public FooAdapter(Foo foo)
    {
        _foo = foo;
    }

    public string A
    {
        get { return _foo.A; }
        set { _foo.A = value; }
    }

    public string B
    {
        get { return _foo.B; }
        set { _foo.B = value; }
    }
}


class BarAdapter : IFooBar
{
    private readonly Bar _bar;

    public BarAdapter(Bar bar)
    {
        _bar = bar;
    }

    public string A
    {
        get { return _bar.A; }
        set { _bar.A = value; }
    }

    public string B
    {
        get { return _bar.B; }
        set { _bar.B = value; }
    }
}

【讨论】:

  • 这个 THE '最正确的答案'(如果时间不是问题的话),但是在我的情况下,快速而肮脏的解决方案更合适。谢谢 Ufuk。
  • @LIUFA 为了社区的利益,您应该真正标记最佳解决方案并使用您喜欢的解决方案。
  • @LIUFA 我对你的爱!
【解决方案2】:

如果您想要一个正确或最常见的解决方案,您应该关注Ufuk´s answer

我的解决方案提供了一种更快的方法来解决问题,但如果长期使用,不建议这样做。

当我正确理解您的问题时,您可以尝试使用接受动态对象作为参数的方法。

public static void ChangeCommonProperties(dynamic thirdPartyObject){
  thirdPartyObject.A = "Hello";
  thirdPartyObject.B = "World";
}

ChangeCommonProperties(new Foo());
ChangeCommonProperties(new Bar());

只要传入的对象有属性并且属性类型正确,就可以正常工作。否则你会得到一个RuntimeBinderException 详细说明出了什么问题。

【讨论】:

  • dynamic 不应用作正确设计的替代品。它旨在与动态类型语言互操作一起使用,而不是用于 OO 解决方法。
  • @Gusdor。当然 Ufuk 的答案是更好和正确的方法,但是您需要实现许多包装类来处理这种情况,并且当涉及多种类型时,这可能会很复杂
  • 如果发生重大更改,此解决方案只会在运行时失败。只有当你有测试覆盖率时,你才能找到它。适配器解决方案将在编译时失败。从长远来看,“复杂”的方式会收回成本。这就是正确解决方案和 hack 之间的区别。
  • @Gusdor 这不是 hack,而是解决问题的另一种方法。
  • @LIUFA 希望您升级到下一个库版本时它不会崩溃!
【解决方案3】:

感谢@Jehof,我最终使用了稍微修改的解决方案。

public T ProcessFooBars<T>(dynamic anything) 
{ 
    return (T) ChangeCommonProperties(anything); 
}

public dynamic ChangeCommonProperties(dynamic thirdPartyObject)
{
    thirdPartyObject.A = "Hello";
    thirdPartyObject.B = "World";
}

希望这可以为您节省一些时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-19
    相关资源
    最近更新 更多