如何避免带.resx文件的C#嵌套类生成同名.resources文件?
问题描述
在Visual Studio 2022中,为带有.resx文件的C#类生成.resources文件时,工具仅会使用命名空间+外部类名作为输出文件名。如果同一外部类中嵌套多个带有.resx文件的类,会生成完全同名的.resources文件,触发MSB3577错误(资源文件重复定义)。
现寻求解决方法:是否可以在.csproj文件中通过配置参数指定.resources文件的输出名称?或者只能在涉及.resx文件的场景下避免使用嵌套类?
背景
选择嵌套窗体类的原因:
现有一个名为SpecialTrackBar的C#控件,该控件接受DataItem对象集合,因此配套存在DataItem、DataItemCollection类,用于编辑集合的对话框为DataItemCollectionEditorDialog。
不想将这些类放在与SpecialTrackBar相同的命名空间中(避免与其他控件类冲突),也不想使用SpecialTrackBarDataItemCollectionEditorDialog这类过长的类名。因此采用嵌套类形式SpecialTrackBar.DataItemCollectionEditorDialog作为折中方案,同时希望能有其他替代命名方案。
复现步骤
- 在Visual Studio 2022中,创建名为
NestedResTest的.NET Framework 4.6.2 Windows Forms控件库项目。 - 添加两个Windows窗体
Dialog1和Dialog2,自动生成对应文件:Dialog1.cs、Dialog1.Designer.cs、Dialog1.resx及Dialog2的配套文件,此时Dialog1和Dialog2类均位于NestedResTest命名空间下。 - 将MSBuild项目生成输出详细程度设置为「诊断」(路径:工具→选项→项目和解决方案→生成并运行)。
- 生成项目,此时生成成功。在生成输出中搜索包含
OutputResources=的行,可看到两个.resources文件:NestedResTest.Dialog1.resources NestedResTest.Dialog2.resources - 编辑
Dialog1.cs和Dialog1.Designer.cs,将Dialog1类放入分部类InnerClass中。 - 重新生成项目,生成成功。搜索
OutputResources=行,可看到:NestedResTest.InnerClass.resources NestedResTest.Dialog2.resources - 编辑
Dialog2.cs和Dialog2.Designer.cs,将Dialog2类放入分部类InnerClass中。 - 再次生成项目,生成失败并触发MSB3577错误。搜索
OutputResources=行,可看到两个完全同名的.resources文件:NestedResTest.InnerClass.resources
解决方案
方法1:在.csproj中手动指定.resources输出名称
可以通过修改项目文件(.csproj)中的EmbeddedResource节点,为每个.resx文件指定唯一的OutputResourceName,避免文件名冲突。
示例配置:
<!-- Dialog1.resx 对应的配置 --> <EmbeddedResource Include="Dialog1.resx"> <DependentUpon>Dialog1.cs</DependentUpon> <Generator>ResXFileCodeGenerator</Generator> <OutputResourceName>NestedResTest.InnerClass.Dialog1.resources</OutputResourceName> </EmbeddedResource> <!-- Dialog2.resx 对应的配置 --> <EmbeddedResource Include="Dialog2.resx"> <DependentUpon>Dialog2.cs</DependentUpon> <Generator>ResXFileCodeGenerator</Generator> <OutputResourceName>NestedResTest.InnerClass.Dialog2.resources</OutputResourceName> </EmbeddedResource>
配置后,生成的.resources文件会分别命名为NestedResTest.InnerClass.Dialog1.resources和NestedResTest.InnerClass.Dialog2.resources,避免重复。
同时需要注意:修改对应窗体的Designer.cs文件中ResourceManager的初始化代码,将原来的typeof(InnerClass)改为typeof(Dialog1)或typeof(Dialog2),确保资源能正确加载。
方法2:避免在带.resx的类中使用嵌套结构
如果不想修改项目配置,最直接的方式是将带.resx的窗体类移出嵌套结构,改为直接放在命名空间下,或者使用其他命名方案替代嵌套类。
替代命名方案
如果想保留简洁的类名同时避免命名冲突,可以尝试以下几种方案:
- 子命名空间拆分:将相关类放在子命名空间中,例如
SpecialTrackBar.Dialogs.DataItemCollectionEditorDialog,既避免与同级别控件类冲突,类名也无需过长。 - 代码别名简化:在使用类的代码文件中通过
using别名简化类名,例如:
这样在代码中可以直接使用using DataItemEditorDialog = YourNamespace.SpecialTrackBarDataItemCollectionEditorDialog;DataItemEditorDialog,而类本身的完整名称仍能避免冲突。 - 文件夹对应命名空间:将配套类放在与
SpecialTrackBar同名的文件夹下,自动对应子命名空间,保持项目结构清晰的同时避免命名冲突。
内容的提问来源于stack exchange,提问作者Grahama

