【问题标题】:Should you use a partial class across projects?您应该跨项目使用部分类吗?
【发布时间】:2008-11-21 19:45:31
【问题描述】:

我有一个包含所有数据库逻辑的类库。我的 DAL/BLL。

我有几个 Web 项目将使用相同的数据库和类,所以我认为将数据层抽象到自己的项目中是个好主意。

但是,在为某些项目的类添加功能时,我想为某些类添加方法。

例如,我的数据层有 Product 和 SomeItem 对象:

// Data Access Layer project

namespace DAL {
  public class Product { 
     //implementation here 
  }

  public class SomeItem {
     //implementation here 
  }
}

在一个项目中,我想添加一个供不同内容项使用的接口,所以我有一个名为:

// This is in Web Project
namespace DAL {
  public partial class Product : ICustomBehaviour {

    #region ICustomBehaviour Implementation
       TheSharedMethod();
    #endregion
  }
}

使用 same 命名空间在单独的项目中编写部分类(创建依赖项)是个好主意吗?如果这是一个坏主意,我怎样才能让这种类型的功能发挥作用?

它似乎不想在编译时合并它们,所以我不确定我做错了什么。

【问题讨论】:

    标签: c# partial-classes


    【解决方案1】:

    您不能跨项目编写部分类。部分类是仅在编译时使用的语法糖——整个类型以单个程序集结束,即一个项目。

    (顺便说一句,您的原始 DAL 文件也必须声明该类是部分的。)

    【讨论】:

    • 那么,我可以在另一个项目的类中添加“接口”功能吗?
    • @Kent:不是晚上八点,不是:)
    • @Atomiton:您不能从不同的项目更改现有类,不。您可以从现有类派生并实现接口。不过,这也有一些缺点——最值得注意的是,您必须确保始终创建新类型的实例而不是基本类型的实例。
    • @Sabuncu:是的,虽然很少有用,但这是可能的。我不认为我希望它作为默认行为,因为这表明可能还有其他部分需要寻找。
    • @Amir:这不是真的在一个程序集中。它只是在两个程序集中包含相同的源代码,这不是一回事。
    【解决方案2】:

    使用 Visual Studio 2015 及更高版本可以跨项目拆分部分类:使用 shared projects(另请参阅 this MSDN blog)。

    对于我的情况,我需要以下内容:

    • 一个类库,它定义了一些被多个客户端应用程序用作接口的类。
    • 客户端应用程序。
    • 在数据库中创建表的设置应用程序。
      由于安装程序的限制,此设置必须是独立的。它不能引用 .NET 框架之外的任何程序集。设置应该将一些枚举常量插入到表中,因此理想情况下它应该引用类库。
      该设置可以导入共享项目。
    • 由于共享项目类似于复制粘贴代码,所以我想尽可能少地移动到共享项目中。

    以下示例演示了部分类和共享项目如何允许将类拆分到不同的项目中。

    在类库中,Address.cs:

    namespace SharedPartialCodeTryout.DataTypes
    {
        public partial class Address
        {
            public Address(string name, int number, Direction dir)
            {
                this.Name = name;
                this.Number = number;
                this.Dir = dir;
            }
    
            public string Name { get; }
            public int Number { get; }
            public Direction Dir { get; }
        }
    }
    

    类库是一个普通的 Visual Studio 类库。它导入 SharedProject,除此之外它的 .csproj 没有什么特别之处:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
      <PropertyGroup>
    <!-- standard Visual Studio stuff removed -->
        <OutputType>Library</OutputType>
    <!-- standard Visual Studio stuff removed -->
      </PropertyGroup>
    <!-- standard Visual Studio stuff removed -->
      <ItemGroup>
        <Reference Include="System" />
      </ItemGroup>
      <ItemGroup>
        <Compile Include="Address.cs" />
        <Compile Include="Properties\AssemblyInfo.cs" />
      </ItemGroup>
      <Import Project="..\SharedProject\SharedProject.projitems" Label="Shared" />
      <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    </Project>
    

    Address.Direction 在 SharedProject 中实现:

    namespace SharedPartialCodeTryout.DataTypes
    {
        public partial class Address
        {
            public enum Direction
            {
                NORTH,
                EAST,
                SOUTH,
                WEST
            }
        }
    }
    

    SharedProject.shproj 是:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup Label="Globals">
        <ProjectGuid>33b08987-4e14-48cb-ac3a-dacbb7814b0f</ProjectGuid>
        <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
      </PropertyGroup>
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
      <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
      <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
      <PropertyGroup />
      <Import Project="SharedProject.projitems" Label="Shared" />
      <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
    </Project>
    

    它的 .projiitems 是:

    <?xml version="1.0" encoding="utf-8"?>
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
        <HasSharedItems>true</HasSharedItems>
        <SharedGUID>33b08987-4e14-48cb-ac3a-dacbb7814b0f</SharedGUID>
      </PropertyGroup>
      <PropertyGroup Label="Configuration">
        <Import_RootNamespace>SharedProject</Import_RootNamespace>
      </PropertyGroup>
      <ItemGroup>
        <Compile Include="$(MSBuildThisFileDirectory)Address.Direction.cs" />
      </ItemGroup>
    </Project>
    

    普通客户端使用Address,包括Address.Direction

    using SharedPartialCodeTryout.DataTypes;
    using System;
    
    namespace SharedPartialCodeTryout.Client
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Create an Address
                Address op = new Address("Kasper", 5297879, Address.Direction.NORTH);
                // Use it
                Console.WriteLine($"Addr: ({op.Name}, {op.Number}, {op.Dir}");
            }
        }
    }
    

    常规客户端 csproj 引用类库并且 SharedProject:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
      <PropertyGroup>
    <!-- Removed standard Visual Studio Exe project stuff -->
        <OutputType>Exe</OutputType>
    <!-- Removed standard Visual Studio Exe project stuff -->
      </PropertyGroup>
    <!-- Removed standard Visual Studio Exe project stuff -->
      <ItemGroup>
        <Reference Include="System" />
      </ItemGroup>
      <ItemGroup>
        <Compile Include="Program.cs" />
        <Compile Include="Properties\AssemblyInfo.cs" />
      </ItemGroup>
      <ItemGroup>
        <None Include="App.config" />
      </ItemGroup>
      <ItemGroup>
        <ProjectReference Include="..\SharedPartialCodeTryout.DataTypes\SharedPartialCodeTryout.DataTypes.csproj">
          <Project>{7383254d-bd80-4552-81f8-a723ce384198}</Project>
          <Name>SharedPartialCodeTryout.DataTypes</Name>
        </ProjectReference>
      </ItemGroup>
      <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    </Project>
    

    DbSetup 仅使用枚举:

    DbSetup.csproj 不引用类库;它只导入 SharedProject:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
      <PropertyGroup>
    <!-- Removed standard Visual Studio Exe project stuff -->
        <OutputType>Exe</OutputType>
    <!-- Removed standard Visual Studio Exe project stuff -->
      <?PropertyGroup>
    <!-- Removed standard Visual Studio Exe project stuff -->
      <ItemGroup>
        <Reference Include="System" />
        <Reference Include="Microsoft.CSharp" />
      </ItemGroup>
      <ItemGroup>
        <Compile Include="Program.cs" />
        <Compile Include="Properties\AssemblyInfo.cs" />
      </ItemGroup>
      <ItemGroup>
        <None Include="App.config" />
      </ItemGroup>
      <Import Project="..\SharedProject\SharedProject.projitems" Label="Shared" />
      <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    </Project>
    

    总结:

    您可以跨项目拆分部分类吗?

    是的,使用 Visual Studio 的共享项目。

    使用相同的命名空间在单独的项目中编写部分类(创建依赖项)是个好主意吗?

    通常不会(请参阅其他答案);在某些情况下,如果您知道自己在做什么,它会很方便。

    【讨论】:

    • 对于 GUI 爱好者:通过 Visual Studio UI 添加共享代码项目作为参考有时会失败,因为 Visual Studio 根本不显示该选项。只需像在帖子中那样编辑 *.csproj 文件,它就会起作用。
    • 这个答案需要提升排名,因为接受的答案不再正确。让我们齐心协力!
    【解决方案3】:

    我无法回答您关于组织层的最佳方式的问题,但我可以尝试回答您关于如何最好地模拟部分类的问题。

    以下是一些想法:

    • 首先想到的是继承。它不一定总是最好的解决方案,但您可能别无选择,因为您可能需要能够让您的对象能够像基类一样被对待。
    • 组合也是一个不错的选择(即将类包装在另一个类中)。这可以让您更好地与 DAL 解耦,但实施起来可能很乏味。
    • 如果您真的只需要在现有类中添加一两个方法,您还可以考虑使用 extension method,但如果您使用过多,它们会很快创建意大利面条式代码。

    【讨论】:

      【解决方案4】:

      部分类必须存在于同一个程序集中。否则,编译器将如何决定将部分类合并到哪里?

      【讨论】:

      • 实际上,我认为一旦我将它们放在同一个命名空间中,它们就会编译成同一个程序集(本质上是为每个项目创建一个唯一的程序集)
      【解决方案5】:

      我同意 Jon Skeet 的回答。

      我认为无论如何处理这样的问题都不是一个好的选择。已经有很好的设计模式展示了分割代码层/层的最佳方法,这只是一个小的语法糖,以便微软可以将 WinForms/WebForms 设计器文件分开并防止人们破坏它们。

      【讨论】:

      • 有什么好的设计模式可以遵循吗?我的 DAL 是使用 LINQ 构建的,因此我对实际数据类的控制较少。
      【解决方案6】:

      我看不出这个方案不起作用的原因:

      两个文件包含存储机制(或其他一些功能)。它们指定继承但不包含业务逻辑:

      • ProductDataAccess.cs
      • ProductWeb.cs

      一个文件包含业务逻辑:

      • ProductBusinessLogic.cs

      现在创建两个项目:

      • WebProject 包含 ProductWeb.cs 和 ProductBusinessLogic.cs。
      • DataProject 包含 ProductDataAccess.cs 和 ProductBusinessLogic.cs

      两个项目都使用相同的业务逻辑。

      【讨论】:

        【解决方案7】:

        虽然我同意 Neil 关于 pre-linq 开发的观点,但我也希望我能够做到这一点,以便将业务逻辑从 Linq2SQL 设计器生成的部分类中分离出来。 例如:

        Northind.DAL (prj)
        -NorthindDataContext (EntityNamespace set to "Northwind.BLL")
        --Product() (Entity, partial class auto-generated)
        --Category() (Entity, partial class auto-generated)
        --Supplier() (Entity, partial class auto-generated)
        
        Northind.BLL (prj)
        -Product() : IMyCustomEnityInterface, BaseEntity (override OnValidate(), etc)
        -Category() : IMyCustomEnityInterface, BaseEntity (override OnValidate(), etc)
        -Supplier() : IMyCustomEnityInterface, BaseEntity (override OnValidate(), etc)
        

        不幸的是,我们不能这样做...实际上我很想知道在使用 LINQ 时推荐的分层/层的方法。

        【讨论】:

          【解决方案8】:

          不。你不能在不同的项目中编写部分类。因为编译器一次只获得一个项目进行编译,因此只扫描该项目中的类、方法、字段等列表。所以如果你有一些部分其他项目中的部分类,编译器找不到。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-09-19
            • 1970-01-01
            • 2018-11-13
            • 1970-01-01
            • 2011-03-06
            • 2013-10-29
            相关资源
            最近更新 更多