什么时候适合使用C#部分类?

时间:2010-08-30 15:38:43

标签: c# class architecture

我想知道是否有人能够概述我为什么要使用它们以及我在这个过程中会获得什么好处。

23 个答案:

答案 0 :(得分:382)

部分类的最大用途是使代码生成器/设计者的生活更轻松。部分类允许生成器简单地发出他们需要发出的代码,并且他们不必处理用户对文件的编辑。用户同样可以通过使用第二个分类来自由地使用新成员来注释该类。这为分离关注点提供了一个非常清晰的框架。

更好的方法是看看设计师在部分课程之前的运作方式。 WinForms设计人员会吐出一个区域内的所有代码,其中包含有关不修改代码的强烈措辞。它必须插入各种启发式方法才能找到生成的代码以供以后处理。现在它可以简单地打开designer.cs文件,并且高度自信它只包含与设计器相关的代码。

答案 1 :(得分:236)

另一个用途是分割不同接口的实现,例如:

partial class MyClass : IF1, IF2, IF3
{
    // main implementation of MyClass
}


partial class MyClass
{
    // implementation of IF1
}

partial class MyClass
{
    // implementation of IF2
}

答案 2 :(得分:150)

除了其他答案......

我发现它们有助于作为重构神级的踏脚石。如果一个类有多个职责(特别是如果它是一个非常大的代码文件)那么我发现将每个责任的1x部分类添加为组织然后重构代码的第一遍是有益的。

这有很大帮助,因为它可以帮助使代码更具可读性而不会实际影响执行行为。它还可以帮助确定何时责任易于重构或与其他方面紧密纠缠在一起。

但是 - 要清楚 - 这仍然是错误的代码,在开发结束时,您仍然希望每个类有一个责任( NOT 每个分类) 。这只是一块踏脚石:)

答案 3 :(得分:76)

  1. 多个开发人员使用部分类,多个开发人员可以在同一个类上工作 容易。
  2. 代码生成器部分类主要由代码生成器使用来保存
  3. 分开了不同的顾虑
  4. 部分方法使用部分类您也可以定义部分方法,开发人员可以简单地定义方法,而其他开发人员可以实现该方法。
  5. 仅部分方法声明即使代码只使用方法声明进行编译,如果实现 该方法的目的不是编译器可以安全地删除那一块 代码并且不会发生编译时错误。

    验证点4.只需创建一个winform项目并在Form1构造函数后面包含这一行并尝试编译代码

    partial void Ontest(string s);
    
  6. 在实施部分课程时,需要考虑以下几点: -

    1. 在部分课程的每个部分使用部分关键字。
    2. 部分类的每个部分的名称应该相同,但部分类的每个部分的源文件名可以不同。
    3. 分部类的所有部分都应位于同一名称空间中。
    4. 分部类的每个部分应该在同一个程序集或DLL中,换句话说,您不能在不同的类库项目的源文件中创建部分类。
    5. 分部类的每个部分必须具有相同的可访问性。 (即:私人,公共或受保护的)
    6. 如果在部分类上继承类或接口,则该部分类的所有部分都会继承它。
    7. 如果部分课程的一部分被密封,则整个课程将被密封。
    8. 如果部分类的一部分是抽象的,则整个类将被视为抽象类。

答案 4 :(得分:55)

一个很好的用途是将生成的代码与属于同一类的手写代码分开。

例如,由于LINQ to SQL使用部分类,您可以编写自己的某些功能实现(如多对多关系),并且在重新生成代码时,这些自定义代码不会被覆盖

WinForms代码也是如此。所有Designer生成的代码都放在一个您通常不接触的文件中。您手写的代码放在另一个文件中。这样,当您在Designer中更改某些内容时,您的更改不会被吹走。

答案 5 :(得分:19)

Partial Class确实用于自动代码生成,一次使用可以维护一个可能有数千行代码的大型类文件。你永远不会知道你的班级最终可能有10万行,你不想创建一个具有不同名称的新类。

public partial class Product
{
    // 50 business logic embedded in methods and properties..
}

public partial class Product
{
    // another 50 business logic embedded in methods and properties..
}
//finally compile with product.class file.

另一种可能的用途可能是,不止一个开发人员可以在同一个类上工作,因为它们存储在不同的地方。人们可能会笑,但你永远不知道它有时可能很少。

Product1.cs

public partial class Product
{
    //you are writing the business logic for fast moving product
}

Product2.cs

public partial class Product
{
    // Another developer writing some business logic...
}

希望它有意义!

答案 6 :(得分:12)

在处理大型课程时保持一切尽可能干净,或者在团队中工作时,您可以在不覆盖(或始终提交更改)的情况下进行编辑

答案 7 :(得分:11)

部分类的主要用途是使用生成的代码。如果查看WPF(Windows Presentation Foundation)网络,可以使用标记(XML)定义UI。该标记被编译为部分类。您可以使用自己的部分类填写代码。

答案 8 :(得分:11)

部分类跨多个文件。

How can you use the partial modifier on a C# class declaration?

使用partial,您可以将一个类物理地分成多个文件。

这通常由代码生成器完成。

示例

使用普通的C#类,您不能在同一项目中的两个单独文件中声明一个类。

但是使用partial修饰符,你可以。

如果一个文件是通常编辑的,另一个是机器生成的或很少编辑的,那么这很有用。

An Example will clear your concept.

class Program
{
    static void Main()
    {
    A.A1();
    A.A2();
    }
}

//Contents of file A1.cs: C#

using System;

partial class A
{
    public static void A1()
    {
    Console.WriteLine("A1");
    }
}

//Contents of file A2.cs: C#

using System;

partial class A
{
    public static void A2()
    {
    Console.WriteLine("A2");
    }
}

Output

A1
A2

此处需要部分内容。

If you remove the partial modifier, you will get an error containing this text: [The namespace '<global namespace>' already contains a definition for 'A'].

提示 要解决此问题,您可以使用partial关键字,也可以更改其中一个类名。

How does the C# compiler deal with partial classes?

如果您反汇编上述程序,您将看到文件A1.cs和A2.cs被删除。

你会发现A级存在。

IL反汇编程序 所以: A类将在同一代码块中包含方法A1和A2。这两个类合并为一个。

A1.cs和A2.cs的编译结果:C#

internal class A
{
    // Methods
    public static void A1()
    {
    Console.WriteLine("A1");
    }

    public static void A2()
    {
    Console.WriteLine("A2");
    }
}

<强>摘要

部分类可以简化某些C#编程情况。

在创建Windows窗体/ WPF程序时,它们通常在Visual Studio中使用。

机器生成的C#代码是独立的。

或者您可以找到整个说明here

答案 9 :(得分:8)

如果你有一个足够大的类,不适合有效的重构,将它分成多个文件有助于保持组织有序。

例如,如果您有一个包含讨论论坛和产品系统的站点的数据库,并且您不想创建两个不同的提供者类(与代理类不同,只是为了清楚) ,您可以在不同的文件中创建单个部分类,例如

MyProvider.cs - 核心逻辑

MyProvider.Forum.cs - 专门针对论坛的方法

MyProvider.Product.cs - 产品方法

这只是让事情井然有序的另一种方式。

另外,正如其他人所说,它是将方法添加到生成的类的唯一方法,而不会冒下次重新生成类时销毁添加项的风险。这对于模板生成的(T4)代码,ORM等等非常方便。

答案 10 :(得分:6)

我看到的另一个用途是,

扩展关于数据访问逻辑的大抽象类,

我有各种名为Post.cs,Comment.cs,Pages.cs ...

的文件
in Post.cs 

public partial class XMLDAO :BigAbstractClass
{
// CRUD methods of post..
}


in Comment.cs 

public partial class XMLDAO :BigAbstractClass
{
// CRUD methods of comment..
}

in Pages.cs 

public partial class XMLDAO :BigAbstractClass
{
// CRUD methods of Pages..
}

答案 11 :(得分:6)

作为预编译器指令的替代方法。

如果您使用预编译器指令(即#IF DEBUG),那么您最终会看到与您的实际版本代码混合的一些看起来很难看的代码。

您可以创建一个单独的部分类来包含此代码,并将整个部分类包装在一个指令中,或者省略该代码文件被发送到编译器(实际上也是如此)。

答案 12 :(得分:6)

服务引用是另一个例子,其中部分类可用于将生成的代码与用户创建的代码分开。

您可以“扩展”服务类,而不会在更新服务引用时覆盖它们。

答案 13 :(得分:4)

部分类使得仅通过添加源文件就可以向适当设计的程序添加功能。例如,可以设计文件导入程序,以便可以通过添加处理它们的模块来添加不同类型的已知文件。例如,主文件类型转换器可以包含一个小类:

Partial Public Class zzFileConverterRegistrar
    Event Register(ByVal mainConverter as zzFileConverter)
    Sub registerAll(ByVal mainConverter as zzFileConverter)
        RaiseEvent Register(mainConverter)
    End Sub
End Class

希望注册一种或多种文件转换器的每个模块都可以包含以下内容:

Partial Public Class zzFileConverterRegistrar
    Private Sub RegisterGif(ByVal mainConverter as zzFileConverter) Handles Me.Register
        mainConverter.RegisterConverter("GIF", GifConverter.NewFactory))
    End Sub
End Class

请注意,主文件转换器类不是“公开”的 - 它只是暴露了一个加载模块可以挂钩的小存根类。存在命名冲突的轻微风险,但如果每个加载项模块的“注册”例程都是根据它处理的文件类型命名的,那么它们可能不会造成问题。如果有人担心这样的事情,可以在注册子程序的名称中粘贴一个GUID。

修改/附录 需要明确的是,这样做的目的是提供一种方法,通过这种方法,各种单独的类可以让主程序或类知道它们。主文件转换器将对zzFileConverterRegistrar执行的唯一操作是创建它的一个实例并调用registerAll方法,该方法将触发Register事件。任何想要挂钩该事件的模块都可以执行任意代码以响应它(这就是整个想法)但是不能通过不正确地扩展zzFileConverterRegistrar类来执行任何操作,而不是定义一个名称与其他名称匹配的方法。 。一个不正确编写的扩展肯定有可能打破另一个不正确编写的扩展,但对于那些不希望他的扩展被破坏而只是简单地编写它的人来说,解决方案就是这样。

可以在不使用分部类的情况下,在主文件转换器类中的某处有一些代码,如下所示:

  RegisterConverter("GIF", GifConvertor.NewFactory)
  RegisterConverter("BMP", BmpConvertor.NewFactory)
  RegisterConverter("JPEG", JpegConvertor.NewFactory)

但添加另一个转换器模块需要进入转换器代码的那一部分并将新转换器添加到列表中。使用部分方法,不再需要 - 所有转换器都将自动包含在内。

答案 14 :(得分:3)

部分类最近帮助了源代码控制,其中多个开发人员添加到一个文件中,其中新方法被添加到文件的相同部分(由Resharper自动化)。

这些推送到git导致合并冲突。我发现无法告诉合并工具将新方法作为完整的代码块。

这方面的部分类允许开发人员坚持使用他们文件的一个版本,我们可以在以后手动合并它们。

示例 -

  • MainClass.cs - 保存字段,构造函数等
  • MainClass1.cs - 开发人员实施的新代码
  • MainClass2.cs - 是他们新代码的另一个开发人员类。

答案 15 :(得分:3)

大多数人都说partial只应该用于具有生成代码文件或接口的类。我不同意,这就是原因。

举一个例子,让我们看一下C#System.Math类......那是。我不会尝试将70多种方法全部放入同一个代码文件中。维持这将是一场噩梦。

将每个数学方法放入单独的部分类文件中,并将所有代码文件放入项目中的Math文件夹中,组织将更加清晰。

对于具有大量不同功能的许多其他类,同样可以/将会成立。例如,用于管理PrivateProfile API的类可能会通过在单个项目文件夹中拆分为一组干净的部分类文件而受益。

就个人而言,我还将大多数人称之为“帮助”或“实用程序”类的内容拆分为每个方法或方法功能组的单独部分文件。例如,在一个项目中,字符串助手类有近50种方法。即使使用区域,这将是一个长期笨拙的代码文件。使用每个方法的单个部分类文件进行维护要容易得多。

在执行此操作时,我会小心使用部分类并使整个项目中的所有代码文件布局保持一致。例如将任何类公共枚举和类私有成员放入文件夹中的Common.cs或类似命名的文件中,而不是将它们分散到文件中,除非它们仅特定于包含它们的部分文件。

请记住,当您将一个类拆分为单独的文件时,您也无法使用文本编辑器拆分栏来同时查看当前文件的两个不同部分。

答案 16 :(得分:3)

来自MSDN

1.在编译时,合并部分类型定义的属性。例如,请考虑以下声明:

[SerializableAttribute]
partial class Moon { }

[ObsoleteAttribute]
partial class Moon { }

它们等同于以下声明:

[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }

以下内容与所有部分类型定义合并:

  • XML评论

  • 接口

  • 泛型参数属性

  • 类属性

  • 成员

2.另外,嵌套的部分类也可以是部分的:

partial class ClassWithNestedClass
{
    partial class NestedClass { }
}

partial class ClassWithNestedClass
{
    partial class NestedClass { }
}

答案 17 :(得分:1)

以下列出了部分类的一些优点。

您可以将UI设计代码和业务逻辑代码分开,以便于阅读和理解。例如,您正在使用Visual Studio开发Web应用程序并添加新的Web表单,然后有两个源文件,&#34; aspx.cs&#34;和#34; aspx.designer.cs&#34; 。这两个文件与partial关键字具有相同的类。 &#34; .aspx.cs&#34; class具有业务逻辑代码,而#34; aspx.designer.cs&#34;具有用户界面控件定义。

使用自动生成的源时,可以将代码添加到类中,而无需重新创建源文件。例如,您正在使用LINQ to SQL并创建DBML文件。现在,当您拖放表时,它会在designer.cs中创建一个部分类,并且所有表列都具有该类中的属性。您需要此表中的更多列来绑定UI网格,但您不希望向数据库表添加新列,以便您可以为此类创建一个单独的源文件,该文件具有该列的新属性,这将是一个局部的课程。这确实会影响数据库表和DBML实体之间的映射,但您可以轻松获得额外的字段。这意味着您可以自己编写代码,而不会弄乱系统生成的代码。

多个开发人员可以同时为该类编写代码。

通过压缩大型课程,您可以更好地维护应用程序。假设您有一个具有多个接口的类,因此您可以根据接口实现创建多个源文件。很容易理解和维护一个实现的接口,源文件具有部分类。

答案 18 :(得分:1)

每当我有一个包含任何重要大小/复杂度的嵌套类的类时,我将该类标记为partial并将嵌套类放在单独的文件中。我使用以下规则命名包含嵌套类的文件:[class name]。[嵌套类名] .cs。

以下MSDN博客解释了如何使用具有嵌套类的部分类来实现可维护性:http://blogs.msdn.com/b/marcelolr/archive/2009/04/13/using-partial-classes-with-nested-classes-for-maintainability.aspx

答案 19 :(得分:1)

我知道这个问题真的很陈旧,但我只想补充我对部分课程的看法。

我个人使用部分类的一个原因是我正在为程序创建绑定,特别是状态机。

例如,OpenGL是一个状态机,有的方法都可以全局更改,但是,根据我的经验绑定类似于OpenGL的东西,那里有很多方法,类可以轻松超过10k LOC。

部分课程将为我解决这个问题帮助我快速找到方法。

答案 20 :(得分:0)

部分类主要是为了帮助代码生成器而引入的,所以我们(用户)不会在每次重新生成时都失去对所生成的类的所有工作/更改,比如ASP.NET的.designer.cs类,几乎所有新的生成代码的工具LINQ,EntityFrameworks,ASP.NET使用生成代码的部分类,因此我们可以利用Partial类和方法安全地添加或更改这些生成代码的逻辑,但在向生成的代码添加内容之前要非常小心如果我们打破构建,使用Partial类会更容易,但如果我们引入运行时错误则会更糟。有关详细信息,请查看此http://www.4guysfromrolla.com/articles/071509-1.aspx

答案 21 :(得分:0)

我感到不安的是,这些帖子中的“凝聚力”一词没有出现(直到现在)。 我也感到不安的是,有人认为启用或鼓励庞大的类和方法在某种程度上是一件好事。 如果您想了解和维护基于代码的“部分”内容,那就糟了。

答案 22 :(得分:-1)

我注意到在答案中找不到的两种用法。

分组课程项目

一些开发人员使用注释来分隔类的不同“部分”。例如,一个团队可能使用以下约定:

public class MyClass{  
  //Member variables
  //Constructors
  //Properties
  //Methods
}

对于局部类,我们可以更进一步,并将这些部分拆分为单独的文件。按照惯例,团队可以在每个文件后缀相应的部分。因此,在上面,我们将具有以下内容:MyClassMembers.cs,MyClassConstructors.cs,MyClassProperties.cs,MyClassMethods.cs。

正如所提到的其他答案一样,是否值得拆分班级可能取决于这种情况下班级的规模。如果很小,将所有内容都放在一个大师班中可能会更容易。但是,如果这些部分中的任何一个太大,则可以将其内容移到单独的局部类中,以使主类保持整洁。在这种情况下,惯例可能是在标题以下部分之后写上“见偏类”之类的注释:

//Methods - See partial class

管理使用语句/命名空间的范围

这可能是罕见的情况,但是您要使用的库中的两个函数之间可能存在名称空间冲突。在单个类中,最多可以为其中一个使用using子句。对于另一个,您需要一个完全限定的名称或别名。对于部分类,由于每个名称空间和using语句列表都不相同,因此可以将两组函数分成两个单独的文件。