如何在c#中诊断和删除不明确的引用

本文关键字:删除 不明确 引用 诊断 | 更新日期: 2023-09-27 18:06:15

(请随意提出更准确的标题)

在我的Visual Studio 2015解决方案中,我有三个项目(让我们称之为Alpha, Beta和Gamma),它们或多或少是相同的东西,但不同之处在于它们定义了不同的后端。这两个项目都将一个类热插到同一个名称空间中:

α

:

namespace SharedNamespace {
    public class SharedClass {
        // implement SharedClass using Alpha's backend
    }
}
β

:

namespace SharedNamespace {
    public class SharedClass {
        // implement SharedClass using Beta's backend
    }
}

γ:

namespace SharedNamespace {
    public class SharedClass {
        // implement SharedClass using Gamma's backend
    }
}

几个项目使用这个热插拔类,每个引用Alpha、Beta或Gamma。其中一个(我们叫它Omricon)用来引用Alpha,但现在引用Gamma:

// ...
SharedNamespace.SharedClass sharedClass;
sharedClass.DoThing();
// ...

当我尝试构建Omricon时,c#编译器给出错误CS0433:

The type 'SharedClass' exists in both 'Alpha, Version=0.0.0.0 (etc)' 
and 'Gamma, Version=0.0.0.0 (etc)'

然而,Omricon 在构建时仅引用Gamma -当我进入项目引用列表时,只显示对Gamma的引用。据我所知,Omricon应该对Alpha一无所知,更不用说它在相同的位置定义了一个类。只有Omricon无法构建-其他使用Alpha和Beta的项目工作得很好,当我将Omricon切换回使用Alpha时,它也工作得很好!

在我看来,对Alpha的引用被维护了,然后,在其他地方。我如何才能找到对Alpha的引用,无论它位于我的代码中的任何地方,并将其删除?

请注意,我已经尝试强制完全重建(正如这个答案所建议的),并且仍然出现相同的错误,因此它与坏的对象缓存无关。

编辑:澄清倒数第二段

如何在c#中诊断和删除不明确的引用

首先,正如你可能已经意识到的那样:这是一个可怕的情况。如果可以避免在两个不同的程序集中使用相同命名空间中的相同命名类,并同时引用它们,则避免这种情况。它强烈地指示了应用程序中的架构缺陷。在我看来,你应该在第四个程序集中定义一个接口,然后让所有程序集同意使用该接口。

然而,c#中有一种方法来处理这种情况。

编译Omicron时,应该给Alpha.dll, Beta.dll和Gamma.dll一个引用别名:

/reference:AlphaDLL=Alpha.DLL /reference:BetaDLL=Beta.DLL ... etc

然后在Omicron中输入:

extern alias AlphaDLL;
extern alias BetaDLL;
extern alias GammaDLL;

在一个文件中,然后在那个文件中你可以写:

AlphaDLL::SharedNamespace.SharedClass

,以消除歧义。

但是,同样,不会遇到这种情况。相反,创建一个SharedClass实现的接口,并让Alpha、Beta和Gamma实现都使用一个名称不冲突的类来实现该接口。

因此,在与队友进行了一些挖掘之后,我发现即使在项目文件本身中没有找到对Alpha的引用,我们的一个.targets文件正在指导MSBuild在我背后添加对Alpha的项目引用:

<Choose>
    <When Condition=" <!-- needs beta --> ">
        <ItemGroup>
            <ProjectReference Include="$(absdtSln)path'to'Beta">
                ...
            </ProjectReference>
        </ItemGroup>
    </When>
    <Otherwise>
        <ItemGroup>
            <ProjectReference Include="$(absdtSln)path'to'Alpha">
                ...
            </ProjectReference>
        </ItemGroup>
    </Otherwise>
</Choose>

(我认为,这是为了使引用Alpha和Beta的项目不必手动执行,因为我正在尝试这样做,并且在我正在测试的项目上显式地完成)。

我为Gamma添加了另一个case,现在一切正常了。

(是的,@Eric,像这样的东西再次证明了这是一个可怕的情况)

具有相同名称的类可以存在于不同的项目中,但它们不能属于同一个命名空间。

既然你说Alpha和Beta已经成功构建并且问题在添加Gamma时开始,我怀疑Alpha和Beta在构建另一个时通过禁用一个单独构建,反之亦然。向你的团队中熟悉他们过去是如何构建的人咨询一下。

我认为设置背后的原因是这样你创建了两个dll (Alpha和Beta)具有相同的类名,所以它们可以以相同的方式调用和使用。这会创建两个具有相同签名的dll来做不同的事情。

你的信息在某种程度上证实了我的怀疑,因为你得到了α和γ的问题,但不是β。我认为当你添加Gamma时,Beta是在准备构建Alpha时禁用的。

正如其他人在评论中所说,你所做的是不可能的…您不能在完全相同的命名空间中有两个具有完全相同名称的类(除非它们是部分)。

这里有几个选项:

  1. 使用不同的命名空间

这样做的好处是没有这些冲突,并且可以通过它们的完全限定命名空间来指定类实例。

α

:

namespace AlphaNamespace {
    public class SharedClass {
        // implement SharedClass using Alpha's backend
    }
}
β

:

namespace BetaNamespace {
    public class SharedClass {
        // implement SharedClass using Beta's backend
    }
}

γ:

namespace GammaNamespace {
    public class SharedClass {
        // implement SharedClass using Gamma's backend
    }
}

在这里,omicron会使用gamma作为

var gamma = new GammaNamespace.SharedClass(...)

  • 使用泛音
  • 部分类文档

    您必须合并项目并更改方法名称,以避免编译器将两个类合并在一起时发生冲突。我怀疑这会像你想的那样工作…

    α

    :

    namespace SharedNamespace {
        public partial class SharedClass {
            // implement SharedClass using Alpha's backend
        }
    }
    
    β

    :

    namespace SharedNamespace {
        public partial class SharedClass {
            // implement SharedClass using Beta's backend
        }
    }
    

    γ:

    namespace SharedNamespace {
        public partial class SharedClass {
            // implement SharedClass using Gamma's backend
        }
    }
    

  • 使用策略模式(或使用接口而不是具体类的状态模式)
  • 在这里,您将决定您希望使用的BaseClass的哪个扩展(Alpha、Beta或Gamma),并通过依赖注入或某种逻辑决定将其交换出来。

    namespace SharedNamespace {
        public class BaseClass{
            // place method abstractions here
        }
    }
    
    namespace SharedNamespace {
        public class AlphaClass : BaseClasse {
            // implement BaseClass using Alpha's backend
        }
    }
    namespace SharedNamespace {
        public class BetaClass : BaseClasse {
            // implement BaseClass using Beta's backend
        }
    }
    namespace SharedNamespace {
        public class GammaClass : BaseClasse {
            // implement BaseClass using Gamma's backend
        }
    }