Frage Warum muss ich (manchmal) auf Baugruppen verweisen, die von der Baugruppe referenziert werden?


Ich habe eine Assembly A, die eine Schnittstelle mit einigen Überladungen definiert:

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

... und eine Assembly B, die A (das Binary, nicht das Projekt) referenziert und eine der Überladungen aufruft:

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 es die System.Windows.Point Überlastung der InverseTransform Methode, weil das der Typ der Eigenschaft ist Location im e.

Aber wenn ich B in der IDE baue, bekomme ich:

Fehler CS0012: Der Typ 'System.Drawing.Point' ist in einer Assembly definiert, auf 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 nicht einmal die Überlastung ist, die ich anrufe. Wenn ich die Zeile mit der überladenen Methode auszeichne InverseTransform heißt, es baut gut, obwohl ich noch ein Objekt des Typs instanziiere ITransform.

Warum? Und gibt es eine Möglichkeit, das zu beheben, ohne eine Referenz hinzufügen zu müssen? System.Drawing überall?


11
2018-04-18 13:45


Ursprung


Antworten:


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


12
2018-04-18 13:47



Diese Methode nutzt etwas, das in System.Drawing. Wenn Sie es auskommentieren, wird diese Assembly nicht mehr versuchen, zu verwenden System.Drawing; Daher keine Anforderung.

Stellen Sie sich das so vor, wenn Sie loslegen, um Ihre Aktion auszuführen. .NET sagt, 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.


4
2018-04-18 13:47



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 dir zu sagen, was du nicht kannst ...

Eine Möglichkeit, dies zu beheben, würde darin bestehen, IPoint-Transformation (IPoint x) als einzige Methode in Ihrer Schnittstelle zusammen mit der IPoint-Schnittstelle einzuführen. Dies würde bedeuten, dass System.Drawing auch Ihrem IPoint entsprechen müsste.

Wenn Sie diese Entkopplungsebene erreichen möchten, sollten Sie an ein dynamisches Schlüsselwort denken, da Sie Drawing.Point nicht dazu bringen können, eine nachträgliche Schnittstelle 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.

BEARBEITEN  Reflector sagt, dass die Signatur von System.Drawing.Point ist

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

2
2018-04-18 14:01



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 nicht von der ausführenden Assembly referenziert wird, kennt der Compiler den Typ nicht und benötigt einen direkten Verweis auf die Assembly, die die Typdefinition enthält.

Ich habe dieses Problem selbst ausgeführt und wollte keinen direkten Verweis auf die Assembly mit dem Typ 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 brauchte keinen Verweis mehr auf die Assembly, die den Typ enthielt. Ich weiß, dass es keine ideale Lösung ist, aber ich konnte keine andere Lösung finden, da meine Methode ein Konstruktor war, sodass ich seine Signatur nicht anders ändern konnte.


2
2017-08-20 17:36



Um dies zu beheben (und vorausgesetzt, Sie haben nicht zu viele Anrufe zum Umbrechen etc.)
Sie könnten einfach einen Erweiterungs-Wrapper für diesen 'Punkt'-Anruf definieren, den Sie nur z.

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

... füttern Sie diese lib (wo die Erweiterung ist) die System.Drawing-Referenz
(Und um zu vermeiden, dass Sie Ihre 'Wrapper-Bibliothek' überall hinzufügen müssen, da dies den Zweck zunichte machen würde, setzen Sie es einfach in eine allgemeine Bibliothek, auf die Sie bereits verwiesen haben, die mit dem Problem zusammenhängt falls du das nicht ändern kannst ...)

und ruf es dann via an MyInverseTransform stattdessen.


0
2018-04-18 17:41