2015-04-30 13 views
5

Niedawno natknąłem się na dziwne zagadnienie, którego nie mogłem wyjaśnić i byłbym szczęśliwy, gdyby ktoś mógł wyjaśnić, dlaczego tak się dzieje.Utworzenie obiektu kończy się niepowodzeniem, gdy używa się przeciążonego konstruktora.

Kwestia Napotkałem się następująco:

Mam interfejs, który jest realizowany, tak:

namespace InterfaceTwo 
{ 
    public interface IA { } 
} 

namespace InterfaceTwo 
{ 
    public class A : IA { } 
} 

a inny interfejs, który jest realizowany w innym projekcie, tak jak poniżej:

namespace InterfaceOne 
{ 
    public interface IB { } 
} 

namespace InterfaceOne 
{ 
    public class B : IB { } 
} 

mam obiekt, który wykorzystuje te interfejsy w jego konstruktorów, jak tak:

using InterfaceOne; 
using InterfaceTwo; 

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

     public TheMainObject(IB iaObj) { } 
    } 
} 

I wreszcie, mam klasę, która agreguje powyższy cel, jak w przykładzie:

using InterfaceTwo; 
using MainObject; 

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

dziwne, ten kod nie zostanie skompilowany z powodu następującego błędu:

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

ja również odkryłem, że jeśli zmienię jedno z przeciążeń, aby zawierał dodatkowy parametr - kompiluje ... Co sprawiło, że pomyślałem, że problem może być związany z jakimś problemem z odbiciem, który kompilator działa.

Dzięki,

Barak.

+1

kliknij prawym przyciskiem myszy projekt i upewnij się, że zestaw jest poprawnie odwołany, dodając odwołanie do pliku-> przebuduj i zobacz, co się stanie – maximdumont

+1

Musisz dodać odniesienie do swojego eksploratora rozwiązań wizualnych (w projekcie testowym) do utworzonego pliku assemby przez interfejsOne. Zobacz tutaj: https://msdn.microsoft.com/en-us/library/7314433t%28v=vs.90%29.aspx – ZivS

+0

To nie jest kwestia referencji, ponieważ nie chcę, aby ReferenceTest wiedział o interfejs IB lub klasa B. Wspomniałem również, że "odkryłem, że jeśli zmienię jedno z przeciążeń, aby zawierał dodatkowy parametr - to kompiluje ...". Zakładam, że problem jest związany z tym, że jeden z przeciążeń o tej samej ilości parametrów ma IB w nim i dla niego, podczas kompilacji, musi również znać IB. – BarakH

Odpowiedz

0

Zobacz this o wyjaśnienie podobny problem, że ja spotkałem. Zacytować odpowiedź z linkiem:

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.

w przykładzie, powinno być oczywiste, co przeciążać używasz, ale kompilator nie jest wystarczająco inteligentny i będzie nadal próbować porównać oba przeciążeń, dlatego zarówno referencje są wymagane.

Być może najłatwiejszym - ale nie najładniejszym - rozwiązaniem (jeśli nie chcesz dołączyć brakującego odniesienia - co może być dobrym powodem, aby tego nie robić) jest dodanie dodatkowego atrapa parametru, co sprawi, że będzie oczywiste kompilator, który przeciążasz, do którego dzwonisz; lub przekształcenie dwóch konstruktorów TheMainObject na dwie metody o różnych nazwach, np. TheMainObjectA(IA iaObj) i TheMainObjectB(IB ibObj) - tj. Unikając całkowitego przeciążenia.

Innym możliwym rozwiązaniem jest użycie słowa kluczowego dynamic (dla .NET 4.0 i wyżej), choć niektórzy ludzie mogą zniechęcić do tego, gdyż może to prowadzić do błędów runtime, jeśli nie jesteś ostrożny:

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

ten sposób, kompilator nie generuje błędu, ponieważ parametr obj jest oceniany w czasie wykonywania - Twój oryginalny kod będzie działał. Jeśli zdecydujesz się użyć tego rozwiązania, rozważ także sprawdzenie pod kątem RuntimeBinderException, aby uniknąć przypadkowego dostępu do nieprawidłowych (nieistniejących) elementów dynamicznego typu.

+0

Tak jak ja, dziękuję bardzo! Innym dobrym rozwiązaniem (zaproponował mój przyjaciel) jest stworzenie interfejsu dla rodziców, który będzie nieco bardziej wymuszający przekazywany typ. Ale to tylko wtedy, gdy oba typy mają wspólną płaszczyznę i oba typy są dostępne do edycji. Ponieważ w moim przypadku nie chciałem edytować tych interfejsów, zdecydowałem się użyć "Object" jako typu parametru, a następnie bezpiecznie spróbuj go rzucić. Myślę, że wraz z twoim rozwiązaniem zobaczę, jakie są plusy i minusy używania "dynamicznego" zamiast przekazywania obiektu. Jeszcze raz dziękuję! – BarakH

6

Problem z zależnością od przestrzeni nazw. Komunikat o błędzie dość papka stwierdzono, że: Twój TheMainObject zależy InterfaceOne i musi być prawidłowo odwoływać

ten nie jest bezpośrednio związany z przeciążeniem konstruktora ...

Aktualizacja: Jest bardziej zachowanie kompilatora . Aby określić, które przeciążone metody do używania, kompilator musi

  1. sprawdzanie wszystkich metod o tej samej nazwie i tej samej liczbie parametrów, aby sprawdzić, czy wszystkie typy parametrów są odniesione
  2. następnie wybrać jedną metodę, która odpowiada rozmówcy typ parametru (jawnie lub niejawnie).

Można sprawdzić, etap 1 i etap 2 są oddzielone od następnego kodu:

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 
     } 
    } 
} 

Powyższy kod zestawia ponieważ TheMainObject(IB obj, int x) nie jest kandydatem do new TheMainObject(a). Jeśli jednak konstruktor jest zdefiniowana jako

public TheMainObject(IB obj) { } 

lub

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

wymagane jest odniesienie do InterfaceTwo.IB.

Możesz uniknąć tego rodzaju sprawdzania referencji, wywołując konstruktora w czasie wykonywania, ale jest to podatna na błędy i powinieneś być ostrożny. Na przykład:

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

Zrobiłem trochę więcej badań i znalazłem następujące zasoby. Zasadniczo krok poszerzania/zwężania musi wiedzieć o wszystkich typach. (Wersja VB służy jedynie jako odniesienie, ponieważ specyfikacja C# dotyczy VS.Net 2003).

Overload Resolution C#

Overload Resolution Visual Basic

+0

Rozwiązałem powyższy problem i nie mogę go tutaj skopiować ... Przeczytaj mój komentarz do odpowiedzi ZivS i Ravingheaven. – BarakH

+0

Myślę, że to zachowanie kompilatora. gdy wywołujemy TheMainObject (a), kompilator sprawdza wszystkie konstruktory za pomocą pojedynczego parametru, a następnie znajduje dopasowanie według typu. Może dlatego, gdy aktualizujesz konstruktor do TheMainObject (IB iaObj, int x), błąd kompilacji zniknął. Ale jeśli zrobisz drugi parametr opcjonalny: TheMainObject (IB iaObj, int x = 0), błąd powróci. – Tzu

+0

btw, możesz użyć odbicia, aby uniknąć tej kontroli odniesienia podczas kompilacji.ale powinieneś zachować ostrożność, jeśli to zrobisz: 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}); – Tzu

Powiązane problemy