问题描述
昨天我们队中的两名球员来到我身边有一个不常见的问题。我们正在使用我们的Winforms应用程序中的第三方组件。所有的代码都已经写过了。然后,他们希望将另一个第三方组件(由同一个供应商)合并到我们的应用程序中。为了他们的喜悦,他们发现第二个组件与第一个组件具有完全相同的公共成员。但令人沮丧的是,这两个组件具有完全独立的继承层次结构,并且不实现通用接口。让你想知道...嗯,让我很奇怪。问题的一个例子:
public class ThirdPartyClass1
{
public string Name
{
get
{
returnThirdPartyClass1;
}
}
public void DoThirdPartyStuff()
{
Console.WriteLine(ThirdPartyClass1正在做它的事情);
}
}
public class ThirdPartyClass2
{
public string Name
{
get
{
returnThirdPartyClass2;
}
}
public void DoThirdPartyStuff()
{
Console.WriteLine(ThirdPartyClass2正在做它的事情);
}
}
很高兴他们觉得复制并粘贴他们为第一个组件不是正确的答案。所以他们正在考虑将组件瞬间分配到对象引用中,然后在检查它是什么类型之后修改代码以进行条件转换。但是,这可以说比拷贝和粘贴方法更丑陋。
所以他们问我是否可以写一些反射代码来访问属性,并将方法从两种不同的对象类型,因为我们知道它们是什么,它们完全一样。但我的第一个想法是,优雅。我想,这个问题必须有一个更好的,优雅的解决方案。
我的第一个问题是,党组件类密封?他们不是。至少我们有这个。
所以,由于它们没有被密封,问题可以通过以下方式解决:
从两个第三方类的一致成员中提取一个公共接口。我叫它常见的。
public interface ICommon
{
string Name
{
get;
}
void DoThirdPartyStuff();
}
然后创建2个新类; DerivedClass1和DerivedClass2分别继承自ThirdPartyClass1和ThirdPartyClass2。这两个新类都实现了ICommon接口,但是否则完全为空。
public class DerivedClass1
:ThirdPartyClass1, ICommon
{
}
public class DerivedClass2
:ThirdPartyClass2,ICommon
{
}
现在,即使派生类为空,接口由基类满足,这是我们首先从中提取接口的地方。
最终的类图看起来像这样。
所以现在,而不是我们以前有的:
ThirdPartyClass1 c1 = new ThirdPartyClass1();
c1。 DoThirdPartyStuff();
现在我们可以做:
ICommon common = new DerivedClass1();
常见。 DoThirdPartyStuff();
同样可以使用DerivedClass2。
结果是引用了ThirdPartyClass1实例的所有现有代码都可以保留,只需将ICONMON引用的ThirdPartyClass1引用替换即可。然后可以为ICommon引用一个DerivedClass1或DerivedClass2的实例,该实例又分别继承自ThirdPartyClass1和ThirdPartyClass2。所有这些都只是工作。
我不知道是否有一个特定的名字,但对我来说,它看起来像一个适配器模式的变体。
也许我们可以解决C#4.0中的动态类型的问题,但是没有编译时检查的好处。
我会非常有兴趣知道是否有其他人有另一种优雅的方法来解决这个问题。
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 been written against it. They then wanted to incorporate another third-party component, by the same vender, into our application. To their delight they found that the second component had the exact same public members as the first. But to their dismay, the 2 components have completely separate inheritance hierarchies, and implement no common interfaces. Makes you wonder... Well, makes me wonder.
An example of the problem:
Incompatible Types http://www.freeimagehosting.net/uploads/f9f6b862f1.png
public class ThirdPartyClass1
{
public string Name
{
get
{
return "ThirdPartyClass1";
}
}
public void DoThirdPartyStuff ()
{
Console.WriteLine ("ThirdPartyClass1 is doing its thing.");
}
}
public class ThirdPartyClass2
{
public string Name
{
get
{
return "ThirdPartyClass2";
}
}
public void DoThirdPartyStuff ()
{
Console.WriteLine ("ThirdPartyClass2 is doing its thing.");
}
}
Gladly they felt copying and pasting the code they wrote for the first component was not the correct answer. So they were thinking of assigning the component instant into an object reference and then modifying the code to do conditional casts after checking what type it was. But that is arguably even uglier than the copy and paste approach.
So they then asked me if I can write some reflection code to access the properties and call the methods off the two different object types since we know what they are, and they are exactly the same. But my first thought was that there goes the elegance. I figure there has to be a better, graceful solution to this problem.
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.
这篇关于如何使2个不兼容的类型,但与同一个成员可互换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!