2015-04-30 10 views
5

Ich bin kürzlich auf eine seltsame Frage gestoßen, die ich nicht erklären konnte und ich wäre froh, wenn jemand klären könnte, warum das passiert.Objektinstanzierung schlägt fehl, wenn überladener Konstruktor verwendet wird

Das Problem, das ich erlebt habe sich wie folgt:

ich eine Schnittstelle, die implementiert ist, etwa so:

namespace InterfaceTwo 
{ 
    public interface IA { } 
} 

namespace InterfaceTwo 
{ 
    public class A : IA { } 
} 

und eine andere Schnittstelle, die in einem anderen Projekt umgesetzt wird, etwa so:

namespace InterfaceOne 
{ 
    public interface IB { } 
} 

namespace InterfaceOne 
{ 
    public class B : IB { } 
} 

ich habe ein Objekt, das diese Schnittstellen in seiner Konstrukteuren verwendet, etwa so:

using InterfaceOne; 
using InterfaceTwo; 

namespace MainObject 
{ 
    public class TheMainObject 
    { 
     public TheMainObject(IA iaObj) { } 

     public TheMainObject(IB iaObj) { } 
    } 
} 

Und schließlich habe ich eine Klasse, die das oben genannte Ziel aggregiert, etwa so:

using InterfaceTwo; 
using MainObject; 

namespace ReferenceTest 
{ 
    public class ReferenceTest 
    { 
     public void DoSomething() 
     { 
      var a = new A(); 
      var theMainObject = new TheMainObject(a); 
     } 
    } 
} 

Merkwürdig ist, wird dieser Code nicht mit dem folgenden Fehler kompiliert:

The type 'InterfaceOne.IB' is defined in an assembly that is not referenced.
You must add a reference to assembly 'InterfaceOne, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
c:\users\harry.baden\documents\visual studio 2013\Projects\ReferenceTest\ReferenceTest\ReferenceTest.cs 11 13 ReferenceTest

ich auch festgestellt, dass, wenn ich eine der Überladungen zu ändern, um einen zusätzlichen Parameter zu enthalten - es kompiliert ... Was hat mich denken, dass das Problem mit einer Art von Reflexion Problem, die der Compiler ausgeführt wird verbunden sein könnte.

Danke,

Barak.

+1

Klicken Sie mit der rechten Maustaste auf Projekt und stellen Sie sicher, dass die Assembly korrekt referenziert wird, indem Sie auf refu- ren-> rebuild verweisen und sehen, was passiert – maximdumont

+1

Sie müssen eine Referenz in Ihrem Visual Studio Solution Explorer (unter Ihrem Testprojekt) zur Assemby-Datei hinzufügen von interfaceOne. Schauen Sie hier: https://msdn.microsoft.com/en-us/library/7314433t%28v=vs.90%29.aspx – ZivS

+0

Dies ist kein Problem der Referenzierung, da ich nicht möchte, dass ReferenceTest darüber informiert die Schnittstelle IB oder die Klasse B. Ich erwähnte auch, dass - "Ich fand auch, dass, wenn ich eine der Überladungen zu ändern, um einen zusätzlichen Parameter zu enthalten - es kompiliert ...". Ich gehe davon aus, dass das Problem mit der Tatsache zusammenhängt, dass eine der Überladungen mit der gleichen Menge an Parametern IB in sich hat und daher während des Kompilierens IB ebenfalls wissen muss. – BarakH

Antwort

0

this für eine Erklärung von einem ähnlichen Problem finden, das ich gestoßen. Zu zitieren die Antwort aus dem Link:

The C# standard specifies that overload resolution (section 7.5.3) is performed by comparing each matching signature to determine which is a better fit. It doesn't say what happens when a reference is missing, so we must infer that it still needs to compare those unreferenced types.

In Ihrem Beispiel, sollte es klar sein, was überlasten Sie verwenden, aber der Compiler ist nicht intelligent genug, und werden versuchen, noch beide Überlastungen zu vergleichen, weshalb beide Referenzen sind erforderlich.

Vielleicht ist die einfachste - aber nicht die schönste - Lösung (wenn Sie die fehlende Referenz nicht einfügen möchten - was Sie aus gutem Grund nicht tun können) ist, einen zusätzlichen Dummy-Parameter hinzuzufügen, der das offensichtlich macht der Compiler, den du überlädst; oder Transformieren der zwei TheMainObject-Konstruktoren in zwei Verfahren mit unterschiedlichen Namen, z. TheMainObjectA(IA iaObj) und TheMainObjectB(IB ibObj) - d. H. Überlastung insgesamt vermeiden.

Eine andere mögliche Lösung ist es, die dynamic Schlüsselwort (für .NET 4.0 und höher) zu verwenden, obwohl einige Leute davon abgeraten, diese könnten als es zu Laufzeitfehlern führen kann, wenn man nicht aufpasst:

public class TheMainObject 
{ 
    public TheMainObject(dynamic obj) 
    { 
     if (obj is IA) 
     { 
      // work with IA ... 
     } 
     else if (obj is IB) 
     { 
      // work with IB ... 
     } 
     else 
     { 
      // exception ... 
     } 
    } 
} 

Diese So erzeugt der Compiler keinen Fehler, da der obj Parameter zur Laufzeit ausgewertet wird - Ihr ursprünglicher Code wird funktionieren. Wenn Sie diese Lösung verwenden, sollten Sie auch die Option RuntimeBinderException prüfen, um einen versehentlichen Zugriff auf ungültige (nicht vorhandene) Elemente des dynamischen Typs zu vermeiden.

+0

Genau wie ich, vielen Dank! Eine andere feine Lösung (mein Freund schlug vor) ist, eine Eltern-Schnittstelle für beide zu machen, die ein bisschen mehr Durchsetzung als der übergebene Typ sein wird. Aber nur, wenn beide Typen eine gemeinsame Basis haben und beide Typen zur Bearbeitung verfügbar sind. Da ich in meinem Fall diese Schnittstellen nicht bearbeiten wollte, entschied ich mich, "Object" als Parametertyp zu verwenden und dann sicher zu versuchen, es zu konvertieren. Ich denke, mit Ihrer Lösung werde ich auch sehen, welche Vor- und Nachteile es gibt, wenn Sie "dynamisch" verwenden, anstatt ein Objekt zu passieren. Danke nochmal! – BarakH

6

Namespace Abhängigkeitsproblem. Die Fehlermeldung ziemlich Matsch sagte es: Ihr TheMainObject auf InterfaceOne abhängig und müssen ordnungsgemäß

verwiesen werden

dies nicht direkt Überlastung Konstruktor im Zusammenhang ...

Update: Es ist eher ein Compiler Verhalten . Um zu ermitteln, welche Methode überlastet zu verwenden, muss der Compiler

  1. Prüfung alle Methoden mit dem gleichen Namen und die gleiche Anzahl der Parameter, um zu sehen, ob alle Parametertypen referenziert werden
  2. dann eine Methode wählen, die der Anrufer Spiele Parametertyp (explizit oder implizit).

Wir überprüfen Schritt 1 und 2 sind mit dem folgenden Code getrennt:

using InterfaceOne; 
using InterfaceTwo; 
namespace MainObject 
{ 
    public class TheMainObject 
    { 
     public TheMainObject(IA obj) { } 
     public TheMainObject(IB obj, int x) { } 
    } 
} 

using InterfaceTwo; 
using MainObject; 
namespace ReferenceTest 
{ 
    public class ReferenceTest 
    { 
     public static void DoSomething() 
     { 
      var a = new A(); 
      var theMainObject = new TheMainObject(a); //no error 
     } 
    } 
} 

Der obige Code kompiliert, weil TheMainObject(IB obj, int x) kein Kandidat für die new TheMainObject(a) ist. Jedoch, wenn der Konstruktor als

public TheMainObject(IB obj) { } 

oder

public TheMainObject(IB obj, int x = 0) { } 

ein Verweis auf InterfaceTwo.IB definiert ist erforderlich.

Sie können diese Art von Referenz überprüfen, indem Sie den Konstruktor zur Laufzeit aufrufen, aber das ist fehleranfällig und Sie sollten vorsichtig sein. Zum Beispiel:

public static void DoSomething() 
{ 
    var a = new A(); 
    TheMainObject theMainObject = null; 
    var ctor = typeof (TheMainObject).GetConstructor(new[] {typeof (IA)}); 
    if (ctor != null) { 
     theMainObject = (TheMainObject) ctor.Invoke(new object[] {a}); 
    } 
} 

Ich habe ein wenig mehr Forschung und fand die folgenden Ressourcen. Grundsätzlich muss der Typ erweitern/Verengung Schritt über alle beteiligten Typen wissen. (Die VB-Version dient nur als Referenz, da die C# -Spezifikation für VS.Net 2003 ist).

Overload Resolution C#

Overload Resolution Visual Basic

+0

Ich habe dieses Problem oben angesprochen und ich kann es nicht scheinen, um es hier zu kopieren ... Bitte lesen Sie meinen Kommentar zu ZivS und Ravingheaven Antworten. – BarakH

+0

Ich denke, es ist ein Compiler-Verhalten. Wenn Sie TheMainObject (a) aufrufen, überprüft der Compiler alle Konstruktoren mit einem einzelnen Parameter und sucht dann eine Übereinstimmung nach Typ. Vielleicht deshalb, wenn Sie den Konstruktor auf TheMainObject aktualisieren (IB iaObj, int x), ist der Kompilierfehler verschwunden. Aber wenn Sie den zweiten Parameter optional machen: TheMainObject (IB iaObj, int x = 0), ist der Fehler wieder da. – Tzu

+0

BTW, Sie können Reflektion verwenden, um diese Referenzprüfung während der Kompilierung zu vermeiden.aber Sie sollten vorsichtig sein, wenn Sie dies tun: var a = new A(); TheMainObject theMainObject = null; var ctor = typeof (TheMainObject) .GetConstructor (neu [] {typeof (IA)}); if (ctor! = Null) theMainObject = (TheMainObject) ctor.Invoke (neues Objekt [] {a}); – Tzu

Verwandte Themen