c# 类型的字段和方法设计建议
作者:一只独行的猿 发布时间:2022-09-23 22:20:44
1、不要为抽象类提供公开的构造方法
抽象类可以有构造方法,但是抽象类不能实例化。如果编程人员没有制定构造方法,编译器会自动生成一个默认的protected构造方法。下面是一个标准的简单抽象类:
abstract class MyAbstractClass
{
protected MyAbstractClass( ) { }
}
抽象类的构造方法不应该是public或internal的。抽象类设计的本意是只能让子类继承,而不是用于生成实例对象。如果抽象类是public或者internal的,它对于其他类型来说就是可见的,而这是不必要的,多余的。抽象类只需对子类可见即可。
2、可见字段应该重构为属性
字段与属性有本质的区别,属性是方法。如下面的Person类型:
class Person
{
public string Name { get; set; }
}
编译器针对属性Name编译后,会生成一个字段和两个方法。
属性相对于字段有如下优势:
1)可以为属性添加代码。属性是方法,所以可以在方法内对设置或获取属性的过程进行编写代码控制。如事件支持等。
2)可以让属性支持线程安全。要让属性变成线程安全的,可以让类型自身去实现。如果让字段支持线程安全,就只有依靠调用者本身实现。
3)属性得到VS编译器支持,能实现自动属性的功能。自动属性的特点在LINQ中应用十分广泛,在匿名类型中,它只能实现只读的自动属性,但字段不支持。
4)从设计的角度(面向对象),公开的字段也应该使用属性。改变字段的状态,类型不会被通知到;而改变属性的值,类型支持则会被通知。
综上,如果一个类型存在一个可见字段,那么它应该被重构为属性。如果某个属性只对内部可见,但不涉及上面4点,则建议使用字段。
3、区别对待override和new
override和new使类型体系因为继承而呈现出多态性。多态是“面向对象语言”的三个重要特性之一。多态要求子类具有与基类方法同名的方法,而override和new的有如下作用:
1)如果子类中的方法前面带有new关键字,则该方法被定义为独立于基类的方法。
2)如果子类中的方法前面带有override关键字,则子类的对象将调用该方法,而不是调用基类的方法。
如果,对于父类的方法在子类中使用了new关键字,则两个方法相互独立。此时,使用子类类型的对象调用方法时,程序执行的将是子类类型new的方法代码;而如果将子类类型转换为父类类型后,对象调用方法时将执行的是父类的方法代码。
如果使用了override关键字重写方法,那么不论子类类型的对象是否转换为父类类型,调用方法时都将执行的是子类的代码。
如果对于子类中,声明与父类相同函数名称的方法,但并不使用关键字new和override。编译器在编译后会提出警告,但不影响程序运行。此时,编译器会默认为是new的效果,所以输出和显示设置与new的效果一样。
4、避免在构造方法中调用虚成员
在构造方法中调用虚成员会出现意想不到的错误。
class Program
{
static void Main(string[] args)
{
Chinese chinese = new Chinese();
}
}
class Person
{
public Person()
{
InitSkin();
}
protected virtual void InitSkin()
{
//省略
}
}
class Chinese:Person
{
Rece Rece;
public Chinese():base()
{
Rece = new Rece() { Name = "赵铭" };
}
protected override void InitSkin( )
{
Console.WriteLine(Rece.Name);
}
}
class Rece
{
public string Name { get; set; }
}
运行该示例,会出现NullReferenceException:未将对象引用设置到对象的实例。
在调用代码中,需要创建一个Chinese的实例对象chinese。由于Chinese类型有基类Person,所以运行时首先调用基类的构造方法。在基类的构造方法中,构造函数会调用InitSkin虚方法。在程序运行时,调用的是子类的InitSkin方法。在子类的InitSkin方法中又在使用子类的Rece变量。但这个时候,子类的构造函数还没调用,因此Rece变量未实例化,但是InitSkin方法又在使用Rece变量,导致错误。
5、成员应优先考虑公开的基类型或接口
类型成员在优先考虑公开基类型或接口,会使得类型支持更多的应用场合。
FCL中的集合类型根据功能划分有List<T>、Dictionary<TKey, TValue>、HashSet<T>等。例如,需要清空集合中的元素,返回空集合的方法Empty,如果不返回基类型或者接口的情况下,就要求我们为每个集合类型都实现该方法。但是,在FCL中实现了一个静态类型Enumerable,代码如下:
public static IEnumerable<TResult> Empty<TResult>()
{
return EmptyEnumerable<TResult>.Instance;
}
使用了泛型接口IEnumerable,所以所有集合子类都可以不实现自己的Empty方法,做到项目的灵活应用。
6、重写时不应使用子类参数
重写时,如果使用了子类参数,可能会偏离设计者的预期目标。
如存在以下继承体系:
class Employee
{
}
class Manager:Employee
{
}
class Salary
{
public void SetSalary(Employee e)
{
Console.WriteLine("职员被设置了薪水。");
}
}
class ManagerSalary:Salary
{
public void SetSalary(Manager e)
{
Console.WriteLine("经理被设置了薪水。");
}
}
类型ManagerSalary中的SetSalary方法重写了Salary中的相同方法,但是参数采用了一个子类的参数。现在在程序中调用代码如下:
public static void Main()
{
ManagerSalary m = new ManagerSalary();
m.SetSalary(new Employee());
}
设计者的本意时为经理设置对应的薪水,但是实际调用的代码却设置了员工的薪水。因此,在重写时使用子类参数有一定的风险。正确的方法时仍旧使用Employee类型参数,让编译器提醒我们要使用关键字new。
来源:https://www.cnblogs.com/pilgrim/p/9181398.html


猜你喜欢
- 页面拖动到最后一页 再向下滑动回复到 第一页,第一页向前滑动回到 最后一页同时,底部红色小圆点随着页面的滑动距离比例随时改变位置布局:<
- 环境准备JDK 1.8,Springboot 2.1.3.RELEASE,spring-boot-starter-aop.2.1.4.REL
- 前言在实际开发当中,Spring中bean的属性直接赋值用的不是太多,整理这方面的资料,做一个小结,以备后续更深入的学习。通过配置文件的方式
- 如果对共享的可变数据的访问不能同步,其后果非常可怕,即使这个变量是原子可读写的。下面考虑一个线程同步方面的问题。对于线程同步,Java类库提
- JNI简介JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C
- 一、直接看效果二、直接上代码1.自定义控件部分package com.susan.project.myapplication;import
- 这篇文章主要介绍了java通过实例了解值传递和引用传递,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 背景:有时候string类型的数据取出来是个很标准的key、value形式,通过Gson的可以直接转成map使用方式:Gson gson =
- 通过MAVEN完成 Mybatis 逆向工程1. POM文件中添加插件在 pom 文件的build 标签中 添加 plugin 插件和 数据
- 前面我们讲到了Spring在进行事务逻辑织入的时候,无论是事务开始,提交或者回滚,都会触发相应的事务事件。本文首先会使用实例进行讲解Spri
- 本文实例为大家分享了java读取excel文件的具体代码,供大家参考,具体内容如下方式一:借用package com.ij34.util;/
- 公钥加密算法,也就是 非对称加密算法,这种算法加密和解密的密码不一样,一个是公钥,另一个是私钥:公钥和私钥成对出现公开的密钥叫公钥,只有自己
- 本文实例为大家分享了Java实现简单抽牌游戏的具体代码,供大家参考,具体内容如下Main类package com.company;impor
- 介绍POI提供API给Java程序对Microsoft Office格式档案读和写的功能。POI可以操作的文档格式有excel,word,p
- 本文主要介绍了java8 stream自定义分组求和并排序的实现,分享给大家,具体如下: public static void
- 前言开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除就是将数据标记为删除,而并非真的物理删除(非DELETE操作),查询时需要携带状
- Java File类 mkdir 不能创建多层目录File f = new File("/home/jp/Upload"
- 本文实例讲述了C#线程同步的三类情景,分享给大家供大家参考。具体分析如下:C# 已经提供了我们几种非常好用的类库如 BackgroundWo
- 目录前言实现思路实测前言需求 导出Excel:本身以为是一个简单得导出,但是每行得记录文件中有一列为图片url,需要下载所有记录行对应得图片
- 1、任何的高并发,请求总是会有一个顺序的2、java的队列的数据结构是先进先出的取值顺序3、BlockingQueue类(线程安全)(使用方