C# 使用重载构造函数时对象实例化失败

C# 使用重载构造函数时对象实例化失败,c#,.net,C#,.net,我最近偶然发现了一个我无法解释的奇怪问题,如果有人能澄清为什么会发生这种情况,我会很高兴 我遇到的问题如下: 我有一个实现的接口,如下所示: namespace InterfaceTwo { public interface IA { } } namespace InterfaceTwo { public class A : IA { } } namespace InterfaceOne { public interface IB { } } namespace I

我最近偶然发现了一个我无法解释的奇怪问题,如果有人能澄清为什么会发生这种情况,我会很高兴

我遇到的问题如下:

我有一个实现的接口,如下所示:

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);
        }
    }
}
以及另一个在不同项目中实现的接口,如下所示:

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);
        }
    }
}
我有一个对象在其构造函数中使用这些接口,如下所示:

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);
        }
    }
}
最后,我有一个类聚合了上述对象,如下所示:

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

我还发现,如果我将其中一个重载更改为包含一个额外的参数,它会编译。。。是什么让我想到这个问题可能与编译器正在运行的某种反射问题有关

谢谢


Barak。

命名空间依赖性问题。错误消息pretty mush说:您的主题对象依赖于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
            }
        }
    }
    
    上述代码之所以编译,是因为
    主对象(IB obj,int x)
    不是
    新主对象(a)
    的候选对象。但是,如果构造函数定义为

    public TheMainObject(IB obj) { }
    

    需要参考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.NET2003)

    有关我遇到的类似问题的解释,请参见。引用链接中的答案:

    C#标准规定,通过比较每个匹配签名来确定哪一个更适合,从而执行过载解析(第7.5.3节)。它没有说明当引用丢失时会发生什么,所以我们必须推断它仍然需要比较那些未引用的类型

    在您的示例中,您使用的重载应该很明显,但是编译器不够聪明,仍然会尝试比较两个重载,这就是为什么需要两个引用

    也许最简单但不是最漂亮的解决方案(如果您不想包含缺少的引用,您可能有很好的理由不这样做)是添加一个额外的伪参数,有效地使编译器清楚地知道您正在调用的重载;或者将两个
    主对象
    构造函数转换为两个名称不同的方法,例如
    主对象A(IA iaObj)
    主对象B(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
    参数是在运行时计算的-您的原始代码可以工作。如果选择使用此解决方案,还应考虑检查以避免意外访问动态类型的无效(不存在)成员。< /P>右击项目,并通过添加对文件->重建的引用来确保正确地引用程序集,并查看在VisualStudio解决方案资源管理器中添加引用所需的内容。(在您的测试项目下)到interfaceOne创建的assemby文件。看这里:这不是引用的问题,因为我不想让ReferenceTest知道接口IB或类B。我还提到-“我还发现,如果我更改其中一个重载以包含一个额外参数,它会编译…”。我假设这个问题与一个具有相同数量参数的重载中包含IB这一事实有关,因此,在编译时,它也需要知道IB。我已经在上面解决了这个问题,我似乎无法将其复制到这里……请阅读我对ZivS和Ravingheaven回复的评论。我认为这是一种编译器行为。当您调用主对象(a)时,编译器使用单个参数检查所有构造函数,然后按类型查找匹配项。这可能就是为什么当您将构造函数更新为主对象(IB iaObj,int x)时,编译错误消失的原因。但是如果您将第二个参数设置为可选:主对象(IB iaObj,int x=0),错误再次出现。顺便说一句,您可以在编译时使用反射来避免此引用检查。但是如果这样做,您应该小心: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});是的,编译器的行为正是我所想的。你想把它作为评论发布,这样我就可以把它标记为答案吗?正如我所想,非常感谢!另一个很好的解决方案(我的朋友建议)就是为这两种类型都创建一个父接口,这对于所传递的类型来说会更加强制