如何在C#中诊断和删除含糊不清的引用?

时间:2016-09-07 17:18:18

标签: c# visual-studio-2015

(请随意为此问题建议更准确的标题。)

在我的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 - 当我进入项目引用列表时,仅出现对Gamma的引用。据我了解,Omricon应该完全知道关于Alpha的 nothing ,更不用说它在同一个位置定义了一个类。只有Omricon无法构建 - 其他使用Alpha和Beta的项目工作正常,当我将Omricon切换回使用Alpha时,它也可以正常工作!

在我看来,正在维护对Alpha的引用,然后在其他地方。如何在我的代码中找到对Alpha的迷路引用,并将其删除?

请注意,我已尝试强制完全重建(建议this answer),同样的错误仍然出现,因此它与坏对象缓存无关。

编辑:澄清了倒数第二段

4 个答案:

答案 0 :(得分:15)

首先,你可能已经意识到:这是一个可怕的情况。如果您可以避免在两个不同的程序集中使用相同的命名类,并引用它们,则避免这种情况。它强烈表明您的应用程序存在架构缺陷。听起来我应该在第四个程序集中定义一个接口,然后让所有程序集同意使用该接口。

但是, 是一种在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实现都使用名称不冲突的类实现该接口。

答案 1 :(得分:1)

所以,在与队友进行了一些挖掘之后,我发现即使项目文件本身没有找到Alpha的引用,我们的一个.targets文件指示MSBuild添加一个项目引用我背后的阿尔法:

<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添加了另一个案例,现在情况正常。

(是的,@Eric,这样的事情是另一个证明这是一个可怕的情况。)

答案 2 :(得分:0)

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

既然你说Alpha和Beta已经成功构建并且在添加Gamma时问题就开始了,我怀疑Alpha和Beta是通过在构建另一个时禁用一个而单独构建的,反之亦然。与团队中的某个人核实他们过去是如何建立的。

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

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

答案 3 :(得分:-1)

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

你有几个选择:

  1. 使用不同的命名空间
  2. 好处是您没有这些冲突,并且可以通过其完全限定的命名空间指定您的类实例。

    <强>α

    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(...)
    
    1. 使用部分
    2. Partial Class Documentation

      当编译器将两个类合并在一起时,您必须合并项目并更改方法名称以避免冲突。怀疑这会按你想要的方式工作......

      <强>α

      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
          }
      }
      
      1. 使用策略模式(或带有接口的状态模式而不是具体类)
      2. 在这里,您将确定要使用的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
            }
        }