How to make 2 incompatible types, but with the same members, interchangeable?

前端 未结 4 1852
鱼传尺愫
鱼传尺愫 2020-12-29 16:35

Yesterday 2 of the guys on our team came to me with an uncommon problem. We are using a third-party component in one of our winforms applications. All the code has already b

相关标签:
4条回答
  • 2020-12-29 17:12

    Add an interface. You could add one wrapper (that implements the interface) for each of the 3rd parties.

    Anyway, if you have the code of those 3rd parties, you could skip the wrapper thing and directly implement the interface. I'm quite sure you don't have the source, though.

    0 讨论(0)
  • 2020-12-29 17:19

    If you're using .Net 4 you can avoid having to do alot of this as the dynamic type can help with what you want. However if using .Net 2+ there is another (different way) of achieving this:

    You can use a duck typing library like the one from Deft Flux to treat your third party classes as if they implemented an interface.

    For example:

    public interface ICommonInterface
    {
        string Name { get; }
        void DoThirdPartyStuff();
    }
    
    //...in your code:
    ThirdPartyClass1 classWeWishHadInterface = new ThirdPartyClass1()
    ICommonInterface classWrappedAsInterface = DuckTyping.Cast<ICommonInterface>(classWeWishHadInterface);
    
    classWrappedAsInterface.DoThirdPartyStuff();
    

    This avoids having to build derived wrapper classes manually for all those classes - and will work as long as the class has the same members as the interface

    0 讨论(0)
  • 2020-12-29 17:33

    My first question was, are the 2 third-party component classes sealed? They were not. At least we have that.

    So, since they are not sealed, the problem is solvable in the following way:

    Extract a common interface out of the coinciding members of the 2 third-party classes. I called it Icommon.

    public interface ICommon
    {
        string Name
        {
            get;
        }
    
        void DoThirdPartyStuff ();
    }
    

    Then create 2 new classes; DerivedClass1 and DerivedClass2 that inherit from ThirdPartyClass1 and ThirdPartyClass2 respectively. These 2 new classes both implement the ICommon interface, but are otherwise completely empty.

    public class DerivedClass1
        : ThirdPartyClass1, ICommon
    {
    }
    
    public class DerivedClass2
        : ThirdPartyClass2, ICommon
    {
    }
    

    Now, even though the derived classes are empty, the interface is satisfied by the base classes, which is where we extracted the interface from in the first place. The resulting class diagram looks like this.

    alt text http://www.freeimagehosting.net/uploads/988cadf318.png

    So now, instead of what we previously had:

    ThirdPartyClass1 c1 = new ThirdPartyClass1 ();
    c1. DoThirdPartyStuff ();
    

    We can now do:

    ICommon common = new DerivedClass1 ();
    common. DoThirdPartyStuff ();
    

    And the same can be done with DerivedClass2.

    The result is that all our existing code that referenced an instance of ThirdPartyClass1 can be left as is, by just swapping out the ThirdPartyClass1 reference for a ICommon reference. The ICommon reference could then be given an instance of DerivedClass1 or DerivedClass2, which of course in turn inherits from ThirdPartyClass1 and ThirdPartyClass2 respectively. And all just works.

    I do not know if there is a specific name for this, but to me it looks like a variant of the adaptor pattern.

    Perhaps we could have solve the problem with the dynamic types in C# 4.0, but that would have not had the benefit of compile-time checking.

    I would be very interested to know if anybody else has another elegant way of solving this problem.

    0 讨论(0)
  • 2020-12-29 17:35

    What about some wrappers?

    public class ThirdPartyClass1 {
        public string Name {
            get {
                return "ThirdPartyClass1";
            }
        }
    
        public void DoThirdPartyStuff() {
            Console.WriteLine("ThirdPartyClass1 is doing its thing.");
        }
    }
    
    public interface IThirdPartyClassWrapper {
        public string Name { get; }
        public void DoThirdPartyStuff();
    }
    
    public class ThirdPartyClassWrapper1 : IThirdPartyClassWrapper {
    
        ThirdPartyClass1 _thirdParty;
    
        public string Name {
            get { return _thirdParty.Name; }
        }
    
        public void DoThirdPartyStuff() {
            _thirdParty.DoThirdPartyStuff();
        }
    }
    

    ...and the same for ThirdPartyClass2, then you use the wrapper interface in all your methods.

    0 讨论(0)
提交回复
热议问题