C#的局部类设计不好吗?[关闭]


69

我想知道为什么“部分类”概念甚至存在于C#/ VB.NET中。我正在开发一个应用程序,我们正在读一本(实际上非​​常好)与我们在工作中实现的开发平台有关的书。在这本书中,作者提供了围绕平台API的大型代码库/包装器,并解释了他在教授有关平台开发的不同主题时如何开发它。

总之,长话短说-他在各处使用局部类作为伪造C#(IMO)多重继承的方法。为什么他不只是将这些类分成多个类并使用合成,这超出了我的范围。他将拥有3个“部分类”文件来构成他的基类,每个文件带有3-500行代码……并在他的API中多次执行此操作。

您认为这是合理的吗?如果是我,我将遵循SRP并创建多个类来处理所需的不同行为,然后创建一个将这些类的实例作为成员的基类(例如,composition)。为什么MS甚至将部分类放入框架中?他们删除了在C#的每个作用域级别扩展/折叠所有代码的能力(C ++允许这样做),因为显然这只是在允许不良习惯-局部类是IMO。我想我的问题是:您能否向我解释何时有合法理由使用局部类?

编辑:我知道对于Web / WinForms没有其他选择。但是除此之外呢?MS为什么不只是将一些不同的关键字用于将代码生成的类粘合在一起?还是真的有一个值得考虑的合法设计方案?

我的意思不是说这是一条咆哮/战争的话题。老实说,我希望在这里学习一些东西。什么时候应该在代码设计中使用局部类?简单的问题,无需关闭

谢谢



回复:您的编辑-生成的代码仍然是代码。编译器如何区分生成的partial关键字和程序员编写的关键字?
亚伦诺特,2010年

2
杰夫,那是一个岁的线程(请记住我们现在有一个新版本的.NET / VS),我在搜索时找不到...。我也在我的线程中解释了我正在查看的代码的独特情况,并且问它是否滥用了“部分”-您链接到的线程在哪里谈论这些主题?没有一个傻瓜,当我在谈论我碰到一个特定的方案..
dferraro

7
.Net中不存在部分类。他们在C#中执行。这是编译时的事情。
Dykam 2010年

1
来自C ++和Java,我认为它们在很大程度上是不必要的。我知道很多人说过,它们对于分离自动生成的代码和自定义扩展很有用,但是有一些简单的替代方法可以解决此问题,但被滥用的可能性要小得多。
WKS 2012年

Answers:


100

您可以向我解释何时有合法理由使用局部类吗?

最合理和有用的原因之一是鼓励分离自动生成的代码和您自己的自定义扩展。例如,从某种设计器中自动生成一个表单代码是很常见的,但是您通常希望向其添加自己的特定行为。这样,如果您重新生成自动代码部分,就不会碰到具有特定扩展名的部分。

就是说,有太多的好事是可能的。一些技巧:

  • 不要partial为了存在而上课partial

  • 不要将局部类放在彼此之间的任何地方。如果您必须跳到项目中一个完全不相关的部分来查看课程的另一半,那么您可能做错了。

  • 不要将其partial用作掩盖类大小的技术。如果您partial因班级太大而与班级分手,则应重新考虑“单一责任原则”

  • 如果partial同一类有三个或三个以上的片段,则几乎可以保证您滥用部分片段。二是合理性的典型上限,通常用于从手写代码中分割自动生成的代码。

总之,长话短说-他在各处使用局部类作为伪造C#(IMO)多重继承的方法。为什么他不只是将班级分成多个班级并使用合成,这超出了我的范围。他将拥有3个“部分类”文件来构成他的基类,每个文件带有3-500行代码……并在他的API中多次执行此操作。

是的,绝对是对partial


6
@dferraro:我不同意。有时在类中具有嵌套类型是完全有效的,并且优良作法是为该嵌套类型提供自己的文件。
杰夫·耶茨

11
@dferraro»代码就是代码。编译器无法分辨出生成的代码与没有特殊指定的人所键入的代码之间的区别-这是更多的工作,而不是更少。此外,作为一般规则,语言可以为您提供更大的灵活性,并且不强加特定的最佳做事方式,这是最好的选择。好的设计的一部分是要知道您不可能提前预知所有情况,并且知道何时违反规则。
约翰·费米内拉

11
@defarraro:多重继承未从.NET中删除-从未添加。
杰夫·耶茨

4
“合并括号”是一种IDE功能,而不是一种语言功能。另外,由于功能与复杂性之间的权衡太大,因此不包括多重继承,而不是因为没有多重继承必然是一个很好的限制。最后,在一个较为个人而言,我觉得有很多事情可以站在要如政府,违背了你的说法。
约翰·费米内拉

6
@dferraro:对于错别字和我不专业的不成熟,我深表歉意。感谢您提供示例。
杰夫·耶茨

14

我会(并且愿意)使用部分类有两个原因。

  1. 分隔代码的自动生成部分(例如WinForms设计器代码或T4输出)。
  2. 在允许嵌套类型拥有自己的文件的同时仍实现设计所需的封装。

更新
我可以看到有些人不相信我的第二点,所以让我举一个例子。的ListViewItemCollection框架中。它被正确嵌套在下面,ListView因为它仅供ListView,但为了使维护更加容易,我将通过使用部分类将其提供给自己。我认为这并不是不良的设计或滥用partial关键字。

有关更多讨论,请检查此重复的问题:C#中的局部类


2
由于第二个原因,我也使用了局部变量。
Dykam 2010年

嗯...以前从未想过,将它用于嵌套类。但是我离题了……嵌套类型有多复杂?实际上,两个类都应该足够简单以适合于一个文件IMO!= P
dferraro,2010年

我发生了很多事情,我需要继承父类并且仅在功能上有所不同的类。父类公开返回其自身类型的方法。
Dykam 2010年

1
@dferraro:“嵌套类型多久出现一次?” 我见过一个自定义集合类,其中的嵌套Enumerator类发挥了很多作用。不小 否则,我同意,99%很简单。
kevinarpe13年

11

局部类的另一合法用途是帮助减少WCF中的“整体Web服务”混乱。您想将其分解为逻辑功能组,但又不想创建大量单独的服务实例/端点(大概是因为它们共享状态,资源等)。

解决方案?让服务实现多个接口,并在其自己的局部类中实现每个接口。然后将配置中的不同端点映射到相同的物理实现。它使项目更具可维护性,但是您仍然只有一个物理端点。

在某些情况下,由于SRP,我认为这种方法是一种不好的做法,但是,当您通常使用WCF服务或Web服务时,它并不是那么简单。您必须在内部设计需求与外部消耗需求之间取得平衡。


正是我想要的。我正在考虑将每个操作的服务和适配器文件分成1个(出于合并和分离的原因)。
Sellorio

这个线程确实让我担心将巨大的WCF服务分解为带有局部文件的不同文件。如果我添加更多的端点,将会增加复杂性,并且我认为它会带来一些性能成本。我没有其他解决方法。在那儿?
Joel McBeth

7

一种较不常用的用法是将一个巨大的类拆分为多个单独的物理文件,以从源代码管理的角度简化工作。我刚刚加入了一个项目,其中包含一些庞大的Web服务类,这些类运行到成千上万的代码行,并具有与几种不同业务功能相关的方法。

来自不同功能分支的合并是一场噩梦,因为不同的团队在同一文件中同时进行了无关的更改。我不能在不进行一些重大更改的情况下拆分Web服务,但是将类拆分为部分类可以完全保留行为,并消除了许多合并问题。

我绝对不鼓励将以上内容作为设计选择,但这对我们来说是一个不错的快速胜利,并且表明部分代码并非一直都是邪恶的...


1
它们可以说是邪恶的,因为可以做出非常非常糟糕的决定的事物都是邪恶的。如果他们帮助您管理庞大的班级,那么它们还使人们能够编写庞大的班级,否则他们可能会这样做。
jwg 2013年

1
正是我想要的。我正在考虑将每个操作的服务和适配器文件分成1个(出于合并和分离的原因)。
Sellorio

我同意。有时您无法重构庞大的类,因此能够通过将其拆分为较小的文件来使用它,这将有所帮助。源代码控制就是一个很好的例子,另一个是系统加载的大文件可以放在非最佳的开发人员机器上。使用一些插件,使用c#中的数千行文件可能会非常慢。
user1568891 2016年

4

过去,我以许多不同的方式使用过部分类。随着我对编程的更多了解,尤其是“偏爱组成而不是继承”的概念,我可以很容易地看到垂直继承和局部类的过度使用都减少了。

除了自动生成的代码外,我想不到部分类的良好使用。即使您使用EF,并且需要不同的元数据,他们甚至也不建议将部分数据用于元数据。实际上,如果您尝试在另一个局部中重复任何属性(仅添加元数据),则会出现编译器错误。

我们对重构和SOC(关注分离)的了解越多,我们的课程就会变得越小越集中。默认情况下,它们已被重新使用,随着时间的流逝,它们变得防弹并易于测试。只是对庞大的计划说不。亨利·福特(Henry Ford)在1900年代初就了解了这一概念,程序员在100年后开始学习它。

可以的时候使用合成...


2

我完全同意约翰的回答。但我会更进一步。

  • 不要让您的课程不完整。

我能想到的,我认为“良好设计”的部分类的唯一用途是使用自动生成的代码。几乎可以肯定,将其他任何用途都不必要地拆分了您的班级。(实际上,我可以看到Jeff关于嵌套类第二点可能是有效的用法)

就我个人而言,我认为您读的这本书听起来像是糟糕的设计,但确实考虑到他可能只是在使用部分类,因此他可以一次仅演示部分代码,而不是一次演示整个类。



2

您可以向我解释何时有合法理由使用局部类吗?

Visual Studio的最新版本使用部分类将自动生成的设计器代码与您自己的代码分开。

一个ASP.NET示例:

  • Page.aspx
  • Page.aspx.cs <-您的代码
  • Page.aspx.Designer.cs <-包含自动生成的代码的局部类。

一个WinForms示例:

  • Form1.resx
  • Form1.cs <-您的代码
  • Form1.Designer.cs <-包含自动生成的代码的局部类

1
但这并没有明确使用它们-一切都在幕后。我认为,问题的意图是确定在开发环境的自动行为范围之外执行此操作的时间。
David T. Macknet,2010年

2

我使用了部分类来“物理地”将静态数据访问方法与活动记录体系结构中的业务类属性和方法分开。例如,我们并排有Company和CompanyData子类。优点是一个文件是POCO,另一个文件仅包含数据访问方法。这是删除对旧版应用程序中存储库类的数据访问的垫脚石。我认为这是一种合理的用法,它肯定会使重构过程变得更聪明。


2

局部类的另一个好用法是在实现Abstract工厂模式时。使根工厂对象成为局部对象,然后将实际工厂方法与工厂实例化的类放在同一文件中。

编辑:部分类也很好地与配置文件进行交互的类。将包含配置参数的代码放在实际使用配置参数的代码附近。


2

在探究部分类的好处时,偶然发现了这个线程。我正在将Java EE应用程序转换为基于Silverlight的.NET应用程序。我在视图层中遇到了以下代码:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.225
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

...

 public partial class Wwcusts04d : System.Windows.Controls.Page {

现在,如果部分页面本身是自动生成的,那么对其进行维护有什么用?另外,里面的代码只是将各种控件链接到它们的名称。我不承认有关于Silverlight的知识,但是这东西不更适合xaml吗?


1

.Net框架中存在部分类,用于让Visual Studio设计人员(例如Asp.Net设计人员和Windows Forms设计人员)生成您的类的代码/混乱,同时将生成的代码保存在单独的文件中。

(请参见.NET局部类与继承

如果您执行类似的操作(生成的代码需要与用户编写的代码共存),那么您可能还会发现部分类很有用,但是我不认为Microsoft曾经打算将部分类作为一种语言概念对除Visual Studio团队。

并不是说使用Partial类是不好的设计-只是您可能不会对它们有用。


@Kragen:例如,Microsoft模式和实践在Web服务软件工厂(codeplex.com/sf)中使用部分类。它在此处用于将生成的代码与手写的代码分开。DSL工具包生成的DSL也使用它们。
约翰·桑德斯

4
-1用于强调。它是一项足以更改C#语法的重要功能。这至少意味着存在多个原因。
罗伯特·戴维斯

@Robert-Microsoft开发人员在某处的博客/文章中介绍了部分类的设计。我会尝试找到它的
贾斯汀2010年

请参阅上面的我的回答,以了解它们在哪些方面非常有用。
David T. Macknet,2010年

1

我在VB.Net中使用了两次局部类,两次都是在极少数情况下需要后期绑定的情况。只需创建一个局部类,然后在顶部关闭Option Strict Off。


您可以为此提供一个代码段吗?另外:您难道不是简单地声明了什么变量,Object而不是声明了什么?
David T. Macknet 2010年

有一个称为PropertyGridView的内部控件,它是PropertyGrid控件的一部分。您可以使用PropertyGrid的Controls集合属性来访问它。在PropertyGridView中是一个名为GetScrollOffset的公共属性。通过将PGV视为对象,并禁用选项Strict,我可以获得滚动偏移量。即。pgv作为对象= pg.Controls(2); (严格禁止);scrollOffset = pgv.GetScrollOffset。
Jules

1

只是为了补充前面提到的将生成的代码与自定义代码分开的答案,我发现部分类对于扩展强类型数据集很有用。


1

关于此主题有很多讨论,很多人说:1)使用部分类是不好的设计; 2)用于自动生成的代码; 3)不应代替继承。

不过,我遇到的情况是,局部类看起来非常方便:我正在构建一系列应用程序,这些应用程序最终将集成到套件中。它们都有一个主要的表单,它将提供一些功能以及几个共享的组件(例如,一个显示报告的表单)。虽然我可以定义一个基类并从中继承一个基类,但是当需要将所有应用程序组合到该产品的“企业”版本中时,这将意味着大量的返工。

因此,局部类非常有用,因为我可以很简单地将各种局部类包含到组合版本中,同时仍然允许我构建产品的各个独立版本。如果要尝试使用继承来完成此任务,那么我将不得不让每个组件调用其自己的通用组件版本(例如InvoiceReportViewer,PurchasingReportsViewer等),而不是简单地调用ReportsViewer并知道Visual Studio已集成对我来说所有的一切。


0

要考虑的另一件事是,部分类会强制您创建包含相同类名的不同文件名。例如,您拥有FactoryClass,并且正在创建它的部分版本;例如,Factory.designer.cs,Factory.data.cs以及所有这些文件都具有名为FactoryClass的类。

如果您导航到此问题;有一个最好的做法定义为:

但是,最佳实践是为每个文件定义一个类,并为文件指定与要定义的类(或结构等)相同的名称。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.