我最近偶然发现了一个我无法解释的奇怪问题,如果有人能澄清其原因,我会很高兴。
我遇到的问题如下:
我有一个实现的接口,如下所示:
namespace InterfaceTwo
{
public interface IA { }
}
namespace InterfaceTwo
{
public class A : IA { }
}
另一个在不同项目中实现的接口,如下所示:
namespace InterfaceOne
{
public interface IB { }
}
namespace InterfaceOne
{
public class B : IB { }
}
我有一个对象在其构建器中使用这些接口,如下所示:
using InterfaceOne;
using InterfaceTwo;
namespace MainObject
{
public class TheMainObject
{
public TheMainObject(IA iaObj) { }
public TheMainObject(IB iaObj) { }
}
}
最后,我有一个聚合上述对象的类,如下所示:
using InterfaceTwo;
using MainObject;
namespace ReferenceTest
{
public class ReferenceTest
{
public void DoSomething()
{
var a = new A();
var theMainObject = new TheMainObject(a);
}
}
}
奇怪的是,这段代码不会编译,并出现以下错误:
类型' InterfaceOne.IB'在未引用的程序集中定义 您必须添加对程序集的引用> 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
我还发现,如果我更改其中一个重载以包含一个额外的参数 - 它确实编译...让我觉得问题可能与编译器运行的某种反射问题有关。
谢谢,
巴拉。
答案 0 :(得分:6)
命名空间依赖问题。错误信息非常简单地说:你的TheMainObject依赖于InterfaceOne,必须正确引用
这与构造函数重载没有直接关系......
<强>更新强> 它更像是一种编译器行为。要确定要使用哪个重载方法,编译器必须
我们可以使用以下代码验证步骤1和步骤2:
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
}
}
}
上面的代码会编译,因为TheMainObject(IB obj, int x)
不是new TheMainObject(a)
的候选者。但是,如果构造函数定义为
public TheMainObject(IB obj) { }
或
public TheMainObject(IB obj, int x = 0) { }
需要对InterfaceTwo.IB的引用。
您可以通过在运行时调用构造函数来避免这种引用检查,但这是容易出错,您应该谨慎。例如:
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});
}
}
我做了一些研究,发现了以下资源。基本上,类型扩展/缩小步骤需要知道所涉及的所有类型。 (VB版本仅供参考,因为C#规范适用于VS.Net 2003)。
答案 1 :(得分:1)
有关我遇到的类似问题的解释,请参阅this。引用链接中的答案:
C#标准规定通过比较每个匹配的签名来确定哪个更合适,从而执行重载决策(第7.5.3节)。它没有说明缺少引用时会发生什么,所以我们必须推断它仍然需要比较那些未引用的类型。
在你的例子中,你应该明白你正在使用什么超载,但是编译器不够智能,并且仍然会尝试比较两个重载,这就是为什么两个引用都是必需的。
也许最简单 - 但不是最漂亮的 - 解决方案(如果你不想包含缺失的参考 - 你可能有充分的理由不这样做)是添加一个额外的虚拟参数,有效地使其显而易见你正在调用超载的编译器;或者将两个TheMainObject
构造函数转换为两个具有不同名称的方法,例如: TheMainObjectA(IA iaObj)
和TheMainObjectB(IB ibObj)
- 即完全避免超载。
另一种可能的解决方案是使用dynamic
关键字(适用于.NET 4.0及更高版本),尽管有些人可能不鼓励这样做,因为如果你不小心它会导致运行时错误:
public class TheMainObject
{
public TheMainObject(dynamic obj)
{
if (obj is IA)
{
// work with IA ...
}
else if (obj is IB)
{
// work with IB ...
}
else
{
// exception ...
}
}
}
这样,编译器不会生成错误,因为在运行时评估obj
参数 - 您的原始代码将起作用。如果您选择使用此解决方案,还应考虑检查RuntimeBinderException以避免意外访问动态类型的无效(不存在)成员。