2012-04-18 15 views
11

Ich habe eine Baugruppe A, die eine Schnittstelle mit einigen Überlastungen definiert:Warum muss ich (manchmal) auf Baugruppen verweisen, die von der Baugruppe referenziert werden?

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

... und eine Baugruppe B, die mit A (die binäre, nicht das Projekt) verweist, und ruft eine der Überlastungen:

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

Um genau zu sein, ruft die System.Windows.Point Überlastung des InverseTransform Verfahrens, weil das die Art der Eigenschaft Location in e ist.

Aber wenn ich B in den IDE bauen erhalte ich:

Fehler CS0012: Der Typ 'System.Drawing.Point' ist in einer Baugruppe definiert, die nicht verwiesen wird. Sie müssen der Assembly 'System.Drawing, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a' einen Verweis hinzufügen.

obwohl das ist nicht einmal die Überlastung ich anrufe. Wenn ich die Zeile, in der die überladene Methode InverseTransform aufgerufen wird, auskommentiere, baut es gut auf, obwohl ich noch ein Objekt vom Typ ITransform instanziiere.

Warum? Und gibt es eine Möglichkeit, dies zu beheben, ohne überall einen Verweis auf System.Drawing hinzufügen zu müssen?

+0

Aus Neugier, könnten Sie die letzte Überladung in 'InverseTransform2' umbenennen und es erneut versuchen? Ich kenne die Antwort nicht, aber ich frage mich, ob es etwas mit der Überladungsauflösung zu tun hat. – dasblinkenlight

+0

Ist 'e.Location' spezifisch ein' System.Windows.Point' Objekt oder eine andere Klasse, die von 'System.Windows.Point' abgeleitet ist? –

+0

@dasblinkenlight: ja es hat mit Überladungsauflösung zu tun, mit verschiedenen Methoden Namen löst es, aber ich möchte nicht die Schnittstelle ändern – mtijn

Antwort

12

Der Compiler muss wissen, was ein System.Drawing.Point ist, um zu beweisen, dass es nicht die richtige Überladung ist (zB wenn es eine implizite Konvertierung hat).

+0

, aber selbst wenn ich (System-) Fenster zu System.Windows.Point spezifisch re-formiere, kompiliert es noch nicht. Warum beweisen Sie, dass es * kein System.Drawing.Point * ist wenn es * eindeutig ist * ein System.Windows.Point? – mtijn

+0

@mtijn: Sie haben die Schnittstelle so definiert: Es erwartet ein System.Drawing.Point: 'System.Drawing.Point InverseTransform (System.Drawing.Point Punkt);' Auch wenn Sie eine Besetzung haben, muss es immer noch wissen Was ist ein System.Drawing.Point? – Skalli

+0

Denkst du, dass der Compiler in diesem Fall schlauer sein könnte? Wenn "e.Location" als 'System.Windows.Point' (und nicht als Ableitung davon) bekannt ist, kann' System.Drawing.Point' nicht die spezifischste Überladung darstellen. Es könnte denkbar sein, dass dieser Teil der Überladungsauflösung übersprungen wird. –

4

Diese Methode verwendet etwas, das in System.Drawing definiert ist. Wenn Sie es auskommentieren, wird die Assembly nicht mehr versuchen, System.Drawing; Daher keine Anforderung.

Denken Sie daran, wenn Sie gehen, um Ihre Aktion auszuführen. .NET sagt ok Ich rufe den in dieser Assembly definierten Typen an und sucht nach dem entsprechenden Code, der ausgeführt werden soll. Es kann es nicht finden, also wirft es seine Hände auf und sagt, ich gebe auf, sag mir, wo es ist.

Machen Sie es sich zur Gewohnheit, auf jede DLL zu verweisen, die Sie möglicherweise verwenden.

+0

nein, es ruft die System.Windows.Point-Überladung der Methode und nicht das System.Drawing.Point ein – mtijn

+1

@mtijn: Es benötigt es für die Überladungsauflösung. Siehe meine Bearbeitung. – SLaks

+1

Yup wie @SLaks sagt, du benutzt es immer noch. – 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()); 
     } 
    } 
} 

Anstatt Ihnen zu sagen, was Sie nicht tun können ...

Ein Weg, dies zu beheben, mit sich bringen würde die Einführung IPoint Transformation (IPoint x) als die einzige Methode in der Schnittstelle zusammen mit IPoint Schnittstelle . Dies würde bedeuten, dass System.Drawing auch Ihrem IPoint entsprechen müsste.

Wenn Sie diese Ebene der Entkopplung möchten, kommt dynamisches Schlüsselwort in den Sinn, da Sie Drawing.Point nicht erhalten können, um eine Schnittstelle nach der Tat zu implementieren. Stellen Sie sicher, dass dieser Teil des Codes eine wirklich gute Unit-Test-Abdeckung aufweist, und erwarten Sie, dass die Leistung etwas langsamer ist.

Auf diese Weise müssten Sie System.Drawing nur in Baugruppen referenzieren, in denen Sie es tatsächlich verwenden.

EDIT Reflektor sagt, dass die Signatur von System.Drawing.Punkt ist

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

noch nicht dynamisch. Ich versuchte 'dynamische Transformation = ...' statt ' var transform = ... 'und das scheint zu kompilieren, aber ich habe es noch nicht ausgeführt. aber wenn ich die Schnittstelle sowieso ändern würde, hätte ich mit Generika denselben Effekt erzielen können, oder? – mtijn

+0

Wenn es sich um Perf handelt, versuchen Sie es mit FastMember. Wenn Sie der Meinung sind, dass Sie Compiler-Unterstützung benötigen, lesen Sie die Antworten von SLaks. – GregC

+1

Ich stimme zu, dass es gut wäre, wenn der Compiler Sie nicht aufgefordert hätte, eine Referenz hinzuzufügen, es sei denn, der Typ muss in die IL kompiliert werden. Dies würde das Rauschen auf Kosten des impliziten Compile-Prozesses reduzieren. – GregC

0

dies zu beheben (und Ihnen nicht zu viel Anrufe usw. wickeln)
Sie einfach eine Erweiterung Wrapper für die ‚Point‘ definieren könnte rufen Sie nur verwenden z.B.

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

... dass lib füttern die System.Drawing Referenz (wo die Erweiterung)
(und um zu vermeiden, Ihre ‚Wrapper lib‘ überall hinzufügen, wie dass die Niederlage der Zweck würde, legte es nur in einige allgemeine lib, die Sie bereits referenziert haben, im Zusammenhang mit dem Problem.Best ist, wenn Teil der 'Quelle' lib aber sagen, falls Sie das nicht ändern können ...

und rufen Sie dann über MyInverseTransform stattdessen.

2

Der einzige Unterschied zwischen Überladungen sind die Typen. Aus diesem Grund kann der Compiler nicht unterscheiden, welche Überladung Sie verwenden, ohne den Typ zu betrachten.

Da der Typ von der ausführenden Assembly nicht referenziert wird, kennt der Compiler den Typ nicht und benötigt einen direkten Verweis auf die Assembly, die die Typdefinition enthält.

Ich lief in diesem Problem selbst und wollte keinen direkten Verweis auf die Assembly, die den Typ enthält, hinzufügen. Ich fügte einfach eine Argument (boolean) zu einer der Methoden, so dass sie nicht mehr Überladungen voneinander sind. Der Compiler hat dann den Unterschied zwischen den Methoden verstanden, auch wenn sie den gleichen Namen haben, weil sie eine andere Anzahl von Argumenten haben. Es wurde kein Verweis mehr auf die Assembly mit dem Typ benötigt. Ich weiß, dass es keine ideale Lösung ist, aber ich konnte keine andere Lösung finden, da meine Methode ein Konstruktor war, so dass ich seine Signatur auf keine andere Weise ändern konnte.

Verwandte Themen