Почему мне (иногда) приходится ссылаться на сборки, на которые ссылается сборка I?

У меня есть сборка A, которая определяет интерфейс с некоторыми перегрузками:

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

... и сборку B, которая ссылается на A (двоичный, а не на проект) и вызывает одну из перегрузок:

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

Чтобы быть точным, он вызывает перегрузку System.Windows.Point метода InverseTransform, потому что это тип свойства Location в e.

Но когда я создаю B в среде IDE, я получаю:

error CS0012: Тип "System.Drawing.Point" определен в сборке, на которую не ссылаются. Вы должны добавить ссылку на сборку "System.Drawing, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a".

хотя это даже не перегрузка, которую я вызываю. Когда я прокомментирую строку, где вызывается перегруженный метод InverseTransform, он строит отлично, даже если я все еще создаю объект типа ITransform.

Почему? И есть ли способ исправить это без необходимости добавлять ссылку на System.Drawing всюду?

Ответ 1

Компилятор должен знать, что такое System.Drawing.Point, чтобы доказать, что это не правильная перегрузка (например, если он имеет неявное преобразование).

Ответ 2

В этом методе используется что-то определенное в System.Drawing. Если вы раскомментируете это, то сборка больше не будет пытаться использовать System.Drawing; следовательно, не требуется.

Подумайте об этом таким образом, когда вы уйдете, чтобы выполнить свое действие .NET говорит, что я делаю звонок этому парню, определенному в этой сборке, и ищет соответствующий код для выполнения. Он не может найти его, поэтому он подбрасывает руки и говорит, что я сдаюсь, расскажи мне, где это.

Просто сделайте это привычкой ссылаться на каждую DLL, которую вы потенциально можете использовать.

Ответ 3

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<ITransform>();
         IPoint x = transform.Object.InverseTransform(new Point());
      }
   }
}

Вместо того, чтобы рассказывать вам, что вы не можете сделать...

Способ исправить это повлечет за собой введение IPoint Transform (IPoint x) в качестве единственного метода в вашем интерфейсе вместе с интерфейсом IPoint. Это означало бы, что System.Drawing также должен будет соответствовать вашему IPoint.

Если вам нужен этот уровень развязки, вам приходит в голову динамическое ключевое слово, поскольку вы не можете заставить Drawing.Point реализовать интерфейс после факта. Просто убедитесь, что у вас действительно отличное покрытие unit test в этой части кода, и ожидайте, что он будет работать несколько медленнее.

Таким образом, вам нужно будет ссылаться на System.Drawing только в сборках, где вы его используете.

EDIT Отражатель говорит, что подпись System.Drawing.Point

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

Ответ 4

Единственная разница между перегрузками - это типы. Вот почему компилятор не может отличить, какую перегрузку вы используете, не глядя на тип.

Поскольку тип не ссылается на исполняющую сборку, компилятор не знает тип и нуждается в прямой ссылке на сборку, содержащую определение типа.

Я сам столкнулся с этой проблемой и не хотел добавлять прямую ссылку на сборку, содержащую этот тип. Я просто добавил аргумент (boolean) к одному из методов, чтобы они больше не перегружали друг друга. Затем компилятор понял разницу между этими методами, даже если они имеют одно и то же имя, потому что они имеют различное количество аргументов. Больше не нужна ссылка на сборку, содержащую этот тип. Я знаю, что это не идеальное решение, но я не мог найти другого решения, поскольку мой метод был конструктором, поэтому я не мог изменить его подпись любым другим способом.

Ответ 5

Чтобы решить эту проблему (и предоставить вам не слишком много вызовов для переноса и т.д.)
вы можете просто определить оболочку расширения для этого вызова "Point", который вы используете, например.

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

... подайте эту lib (где расширение) ссылку System.Drawing
(и чтобы избежать необходимости добавлять ваш "обертку lib" повсюду, поскольку это могло бы победить цель, просто поместите ее в какую-то общую библиотеку, на которую вы уже ссылались, связанную с проблемой. Лучше всего, если часть "исходной" библиотеки, но говорящая в случае, если вы не можете изменить это)...

и назовите его вместо MyInverseTransform.