Perché (a volte) devo fare riferimento agli assiemi a cui fa riferimento l’assemblaggio che faccio riferimento?

Ho un assembly A che definisce un’interfaccia con alcuni overload:

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

… e un assembly B che fa riferimento a A (il binario, non il progetto) e chiama uno degli overload:

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

Per essere precisi, chiama il sovraccarico System.Windows.Point del metodo InverseTransform , perché questo è il tipo di proprietà Location in e .

Ma quando costruisco B nell’IDE ottengo:

errore CS0012: il tipo ‘System.Drawing.Point’ è definito in un assembly a cui non viene fatto riferimento. È necessario aggiungere un riferimento all’assembly ‘System.Drawing, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a’.

anche se non è nemmeno il sovraccarico che sto chiamando. Quando commento la riga in cui viene chiamato il metodo sovraccarico InverseTransform , si sviluppa bene anche se sto ancora ITransform un’istanza di un object di tipo ITransform .

Perché? E c’è un modo per risolvere questo problema senza dover aggiungere un riferimento a System.Drawing ovunque?

Il compilatore deve sapere che cosa è un System.Drawing.Point al fine di dimostrare che non è il sovraccarico corretto (ad esempio, se ha una conversione implicita).

Questo metodo utilizza qualcosa definito in System.Drawing . Se si rimuove il commento, tale assembly non tenterà più di utilizzare System.Drawing ; quindi, nessun requisito.

Pensaci in questo modo, quando vai per eseguire la tua azione. NET dice ok Sto facendo una chiamata a questo ragazzo definito in questo assembly e cerca il codice appropriato da eseguire. Non riesce a trovarlo, quindi getta le sue mani e dice che mi arrendo e dimmi dov’è.

Prendi l’abitudine di fare riferimento a tutte le DLL che potresti potenzialmente utilizzare.

 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()); } } } 

Invece di dirti cosa non puoi fare …

Un modo per risolvere questo problema comporta l’introduzione di IPoint Transform (IPoint x) come unico metodo nell’interfaccia, insieme all’interfaccia IPoint. Ciò significherebbe che System.Drawing dovrebbe essere conforms anche al tuo IPoint.

Se si desidera tale livello di disaccoppiamento, viene in mente una parola chiave dynamic, dal momento che non è ansible ottenere Drawing.Point per implementare un’interfaccia dopo il fatto. Assicurati solo di avere una copertura di test unitaria davvero eccezionale su questa parte di codice, e aspettati che funzioni un po ‘più lentamente.

In questo modo, dovresti solo fare riferimento a System.Drawing solo negli assiemi in cui lo stai effettivamente utilizzando.

EDIT Reflector dice che la firma di System.Drawing.Point è

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

L’unica differenza tra i sovraccarichi sono i tipi. Questo è il motivo per cui il compilatore non può distinguere quale sovraccarico stai usando senza guardare il tipo.

Poiché il tipo non è referenziato dall’assembly in esecuzione, il compilatore non conosce il tipo e ha bisogno di un riferimento diretto all’assembly che contiene la definizione del tipo.

Ho eseguito questo problema personalmente e non volevo aggiungere un riferimento diretto all’assembly che contiene il tipo. Ho semplicemente aggiunto un argomento (booleano) a uno dei metodi in modo che non siano più sovraccarichi l’uno dell’altro. Il compilatore ha quindi compreso la differenza tra i metodi, anche se hanno lo stesso nome, perché hanno una quantità diversa di argomenti. Non è più necessario un riferimento all’assembly che contiene il tipo. So che non è una soluzione ideale, ma non sono riuscito a trovare un’altra soluzione in quanto il mio metodo era un costruttore quindi non potevo modificare la sua firma in nessun altro modo.

Per risolvere questo (e fornendo non si hanno troppe chiamate per avvolgere ecc.)
potresti semplicemente definire un wrapper di estensione per quella chiamata ‘Punto’ che stai usando solo per es

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

… feed lib (dove l’estensione è) il riferimento System.Drawing
(e per evitare di dover aggiungere la tua ‘wrapper lib’ ovunque in quanto ciò vanificherebbe lo scopo, basta metterlo in una libreria comune a cui si è già fatto riferimento, in relazione al problema. La cosa migliore è se parte della lib ‘source’ ma dicendo nel caso non sia ansible cambiarlo) …

e chiamalo quindi tramite MyInverseTransform .