2012-04-18 1 views
11

У меня есть сборки, который определяет интерфейс с некоторыми перегрузками:Почему мне (иногда) приходится ссылаться на сборки, на которые ссылается сборка, с которой я ссылаюсь?

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 я получаю: CS0012

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

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

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

+0

Из любопытства вы могли бы переименовать последнюю перегрузку в «InverseTransform2» и попробовать еще раз? Я не знаю ответа, но мне интересно, имеет ли он какое-либо отношение к разрешению перегрузки. – dasblinkenlight

+0

Является ли 'e.Location' конкретным объектом' System.Windows.Point' или другим классом, который происходит из 'System.Windows.Point'? –

+0

@dasblinkenlight: да, это связано с разрешением перегрузки, используя разные имена методов, решает его, но я не хочу менять интерфейс – mtijn

ответ

12

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

+0

, но даже если я специально (повторно) внесен в System.Windows.Point, он все еще не компилируется. почему это * не * System.Drawing.Point, когда он явно * является * System.Windows.Point? – mtijn

+0

@mtijn: Вы определили интерфейс таким образом: он ожидает System.Drawing.Point: 'System.Drawing.Point InverseTransform (точка System.Drawing.Point);' Даже если у вас есть актерский состав, он все равно должен знать что такое System.Drawing.Point. – Skalli

+0

Считаете ли вы, что это ситуация, когда компилятор может быть умнее? Если известно, что 'e.Location' является' System.Windows.Point' (а не является его производным), тогда нет возможности «System.Drawing.Point» быть самой конкретной перегрузкой. Возможно, он пропустит эту часть разрешения перегрузки. –

4

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

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

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

+0

нет, он не вызывает перегрузку System.Windows.Point метода, а не System.Drawing.Point one – mtijn

+1

@mtijn: Он нуждается в его разрешении при перегрузке. См. Мое редактирование. – SLaks

+1

Yup as @SLaks говорит, что вы все еще используете его. – scottheckel

2
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 х) в качестве единственного метода в интерфейсе, вместе с интерфейсом IPOINT , Это означало бы, что System.Drawing должен будет соответствовать вашему IPoint.

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

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

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

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

не думал об использовании динамических пока .. Я попробовал 'dynamic transform = ...' вместо ' var transform = ... ', и это похоже на компиляцию, но я еще не запускал ее. но если бы я все-таки менял интерфейс, я мог бы добиться такого же эффекта с дженериками, верно? – mtijn

+0

Если ваш вопрос беспокоит вас, попробуйте использовать FastMember. Если вы считаете, что вам нужна поддержка компилятора, см. Ответ от SLaks. – GregC

+1

Я согласен с тем, что было бы неплохо, если бы компилятор не попросил вас добавить ссылку, если тип не должен быть скомпилирован в IL. Это уменьшит шум за счет того, что процесс компиляции будет несколько более скрытым. – GregC

0

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

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

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

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

2

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

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

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