为什么我(有时)必须引用我引用的程序集引用的程序集?

我有一个程序集A,它定义了一些带有一些重载的接口:

public interface ITransform { Point InverseTransform(Point point); Rect InverseTransform(Rect value); System.Drawing.Point InverseTransform(System.Drawing.Point point); } 

…以及引用A(二进制文件而不是项目)的程序集B并调用其中一个重载:

 var transform = (other.Source.TransformToDisplay != null && other.Source.TransformToDisplay.Valid) ? other.Source.TransformToDisplay : null; if (transform != null) { e.Location = transform.InverseTransform(e.Location); } 

确切地说,它调用InverseTransform方法的System.Windows.Point重载,因为这是e属性Location的类型。

但是当我在IDE中构建B时,我得到:

错误CS0012:类型’System.Drawing.Point’在未引用的程序集中定义。 您必须添加对程序集’System.Drawing,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b03f5f7f11d50a3a’的引用。

即使这甚至不是我所说的过载。 当我注释掉调用重载方法InverseTransform ,即使我仍在实例化ITransform类型的对象,它也能正常构建。

为什么? 有没有办法解决这个问题,而无需在任何地方添加对System.Drawing的引用?

编译器需要知道System.Drawing.Point是什么,以certificate它不是正确的重载(例如,如果它有隐式转换)。

该方法使用System.Drawing定义的内容。 如果你取消注释它,那么该程序集将不再尝试使用System.Drawing ; 因此,没有要求。

想想这样,当你开始执行你的行动.NET说好的我正在调用这个程序集中定义的这个人,并寻找适当的代码来执行。 它找不到它所以它抛出它的手并说我放弃你告诉我它在哪里。

只需养成引用您可能使用的每个DLL的习惯。

 namespace ClassLibrary1 { public interface ITransform { dynamic InverseTransform(dynamic point); } } using ClassLibrary1; using Moq; namespace ConsoleApplication9 { interface IPoint { } class Point : IPoint { } class Program { static void Main(string[] args) { var transform = new Mock(); IPoint x = transform.Object.InverseTransform(new Point()); } } } 

而不是告诉你你不能做什么……

解决这个问题的方法需要引入IPoint Transform(IPoint x)作为接口中唯一的方法,以及IPoint接口。 这意味着System.Drawing也必须符合您的IPoint。

如果你想要那种解耦,动态关键字会浮现在脑海中,因为你不能让Drawing.Point在事后实现一个接口。 确保在这部分代码上有非常好的unit testing覆盖率,并期望它的执行速度稍慢。

这样,您只需要在实际使用它的程序集中引用System.Drawing。

EDIT Reflector说System.Drawing.Point的签名是

 [Serializable, StructLayout(LayoutKind.Sequential), TypeConverter(typeof(PointConverter)), ComVisible(true)] public struct Point { } 

重载之间的唯一区别是类型。 这就是为什么编译器无法在不查看类型的情况下区分您正在使用的重载。

由于执行程序集未引用该类型,因此编译器不知道类型并需要直接引用包含类型定义的程序集。

我自己运行了这个问题,并且不希望添加对包含该类型的程序集的直接引用。 我只是在其中一个方法中添加了一个参数(boolean),因此它们不再是彼此的重载。 然后编译器理解方法之间的区别,即使它们具有相同的名称,因为它们具有不同数量的参数。 它不再需要对包含该类型的程序集的引用。 我知道这不是一个理想的解决方案,但我找不到另一个解决方案,因为我的方法是一个构造函数,所以我无法以任何其他方式更改其签名。

解决这个问题(并提供你没有太多的调用来包装等)
你可以简单地为你只使用例如的’Point’调用定义一个扩展包装器

 public static Point MyInverseTransform(this ITransform mytransform, Point point) { return mytransform.InverseTransform(point); } 

…提供lib(扩展名为)System.Drawing引用
(并且为了避免必须在任何地方添加你的’包装器lib’,因为这会破坏目的,只需将它放在你已经引用的一些常见的lib中,与问题相关。最好是’源’lib的一部分但是说如果你不能改变那个)…

然后通过MyInverseTransform调用它。