c# 类型的字段和方法设计建议

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。

以上就是c# 字段和方法设计建议的详细内容,更多关于c# 字段和方法的资料请关注我们其它相关文章!

(0)

相关推荐

  • C# 调用腾讯即时通信 IM的示例

    IM SDK API 概述 https://cloud.tencent.com/document/product/269/33543 /// <summary> /// IM SDK 初始化. /// </summary> /// <param name="sdk_app_id"></param> /// <param name="json_sdk_config"></param> /// &l

  • C# List引用类型克隆的3种方法

    前言 有时候我们想克隆一个List去做别的事,而不影响原来的List,我们直接在list后面加上小点点,发现并没有Clone这样的扩展函数.这时候就只有自己扩展了. 尝试了三种方式,测试都通过了,至于性能方面我还没有做测试. 下面话不多说了,来一起看看详细的介绍吧 一.反射 public static List<T> Clone<T>(this List<T> list) where T : new() { List<T> items = new List&

  • C# 实现简易的串口监视上位机功能附源码下载

    实现上位机和下位机之间的通信,通常使用的是串口通信,接下来实现一个通过上位机和串口调试助手来完成串口通信测试. 首先创建一个WInfrom窗体应用工程文件,创建过程可参考  https://www.jb51.net/article/150973.htm 在创建好的工程下面,通过工具箱中已有的控件完成界面的搭建,如下图所示,为了方便初学者容易看懂程序,下图将控件的命名一并标注出来: 直接进入正题,将完整的工程代码黏贴出来: using System; using System.Collection

  • C# 进行图片压缩的示例代码(对jpg压缩效果最好)

    直接上代码 public static class ImageCompress { /// <summary> /// 图片压缩 /// </summary> /// <param name="imagePath">图片文件路径</param> /// <param name="targetFolder">保存文件夹</param> /// <param name="qualit

  • 详解c# 深克隆与浅克隆

    前言 我们都知道memberwiseclone 会将浅克隆. 什么是浅克隆?如何深克隆呢? 正文 public class good{ private good(){ oneclass=new class{ int id=8; string name='id'; } } private static good __good; private static good __good=new good(); public good createinstance() { return __good.me

  • 浅析C#的复制和克隆

    本文浅析了C#的复制和克隆技术,对于有需要的朋友可以参考下. 在C#中,用HashTable,DataTable等实现复制和克隆,下面直接看例子: HashTable ht = null; ht = new HashTable(); foreach(string s in ht) { //... } //上面遍历的时候需要修改HashTable中的键值,一般会报异常,提示您的集合已修改XXX什么的,因为foreach遍历的时候,in 后面的集合不可更改 //这个时候应该我想到了,应该在便利之前复

  • 详解 c# 克隆

    克隆方法是原型设计模式中必须使用的方式,它将返回一个与当前对象数据一致的对象.正如其名,犹如一个模子雕刻而出.克隆类型分为两种:浅克隆.深克隆. 1.浅克隆 浅克隆方式是最简单.最直接的方式.只需要类实现接口ICloneable(在命名空间System.Runtime.InteropServices下)的Clone方法,在方法中使用加入对当前类的MemberwiseClone()方法即可.在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象:如果原型对象的成员变量是引用类型,则将引用

  • c# Parallel类的使用

    Parallel类是对线程的抽象,提供数据与任务的并行性.类定义了静态方法For和ForEach,使用多个任务来完成多个作业.Parallel.For和Parallel.ForEach方法在每次迭代的时候调用相同的代码,而Parallel.Invoke()方法允许同时调用不同的方法.Parallel.ForEach()方法用于数据的并行性,Parallel.Invoke()方法用于任务的并行性. 1.For()方法 For()方法用于多次执行一个任务,可以并行运行迭代,但迭代的顺序并没指定.Fo

  • C# 设置防火墙的创建规则

    对于某些程序,我们只允许它使用某些特定端口.网络类型或者特定IP类型等信息.这时候,需要使用到防火墙里面的"高级设置",创建某些特定的入站或者出栈规则,以规避其程序使用允许端口等意外的信息. 下面以创建出站规则为例,编写一条出站规则,规避除允许规则以外的通过防火墙.创建规则时,会使用到接口INetFwRule,其有关介绍参照MSDN文档. 创建规则的方法: /// <summary> /// 为WindowsDefender防火墙添加一条通信端口出站规则 /// </

  • c# 类型的字段和方法设计建议

    1.不要为抽象类提供公开的构造方法 抽象类可以有构造方法,但是抽象类不能实例化.如果编程人员没有制定构造方法,编译器会自动生成一个默认的protected构造方法.下面是一个标准的简单抽象类: abstract class MyAbstractClass { protected MyAbstractClass( ) { } } 抽象类的构造方法不应该是public或internal的.抽象类设计的本意是只能让子类继承,而不是用于生成实例对象.如果抽象类是public或者internal的,它对于

  • c# 字段和方法设计建议

    1.不要为抽象类提供公开的构造方法 抽象类可以有构造方法,但是抽象类不能实例化.如果编程人员没有制定构造方法,编译器会自动生成一个默认的protected构造方法.下面是一个标准的简单抽象类: abstract class MyAbstractClass { protected MyAbstractClass( ) { } } 抽象类的构造方法不应该是public或internal的.抽象类设计的本意是只能让子类继承,而不是用于生成实例对象.如果抽象类是public或者internal的,它对于

  • Django中模型Model添加JSON类型字段的方法

    本文实例讲述了Django中模型Model添加JSON类型字段的方法.分享给大家供大家参考.具体如下: Django里面让Model用于JSON字段,添加一个JSONField自动类型如下: class JSONField(models.TextField): __metaclass__ = models.SubfieldBase description = "Json" def to_python(self, value): v = models.TextField.to_pytho

  • 使用JDBC4.0操作XML类型的字段(保存获取xml数据)的方法

    在 Java SE 6 所提供的诸多新特性和改进中,值得一提的是为 Java 程序提供数据库访问机制的 JDBC 版本升级到了 4.0, 这个以 JSR-221 为代号的版本 , 提供了更加便利的代码编写机制及柔性 , 并且支持更多的数据类型 . JDBC 4.0 的新特性分为下述四类: 1. 驱动及连接管理 2. 异常处理 3. 数据类型支持 4. API 的变化 以上说这么都不是本文要讲的重点内容,下面给大家介绍jdbc4.0操作xml类型数据的方法,具体详情如下所示: 在JDBC4.0推出

  • c#使用dynamic类型优化反射的方法

    什么是dynamic类型? 微软给出的官方文档中这样解释:在通过 dynamic 类型实现的操作中,该类型的作用是绕过编译时类型检查. 改为在运行时解析这些操作. dynamic 类型简化了对 COM API(例如 Office Automation API).动态 API(例如 IronPython 库)和 HTML 文档对象模型 (DOM) 的访问.在大多数情况下,dynamic 类型与 object 类型的行为类似. 但是,如果操作包含 dynamic 类型的表达式,那么不会通过编译器对该

  • PHP操作MySQL中BLOB字段的方法示例【存储文本与图片】

    本文实例讲述了PHP操作MySQL中BLOB字段的方法.分享给大家供大家参考,具体如下: 1.MySQL中BLOB字段类型 BLOB类型的字段用于存储二进制数据. MySQL中,BLOB是个类型系列,包括:TinyBlob.Blob.MediumBlob.LongBlob,这几个类型之间的唯一区别是在存储文件的最大大小上不同. MySQL的四种BLOB类型 TinyBlob:  最大 255字节 Blob:      最大 65K MediumBlob:最大 16M LongBlob:  最大

  • joomla实现注册用户添加新字段的方法

    本文实例讲述了joomla实现注册用户添加新字段的方法.分享给大家供大家参考,具体如下: joomla原来的注册很简单,为了添加自己想要的字段,比如电话. 在数据库添加这一字段"telphone" 修改libraries\joomla\database\table\user.php 在JTableUser这个类添加新的变量: var $telphone = null; 页面的修改 修改components\com_user\views\register\tmpl\default.php

  • Mybatis实现自定义类型转换器TypeHandler的方法

    先给大家简单介绍下mybatis MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 此文其实是java操作Oracle类型XMLType总结二:使用Mybatis附带的一篇小结. Mybatis实现自定义的转换器,

  • Oracle 实现 一个关键字 匹配多个 字段的方法

    有这么一个需求,满足只有一个输入框的条件下,支持不同数据列的搜索结果. 说白了,就是这个 输入框  既可以用来 搜索姓名,也可以搜索 年龄,地址等. 分析: 一般情况下,我们的一个输入框对应 数据库 的某一列信息的搜索,比如,要搜索姓名为 '李' 姓开头的,那么对应的 sql 就是 where name like '李%' 但是,现在输入框有了不确定性,不知道用户到底会输入什么,导致我们后台的 sql 不知道该如何对应了, 比如,用户输入 18,很明显,用户是想搜索 年龄为 18 的用户,如果

  • 关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法

    #在切换路由时,组件会被复用,不过,这也意味着组件的生命周期钩子不会再被调用. 解决办法有两种,1简单地 watch (监测变化) $route 对象: const User = { template: '...', watch: { '$route' (to, from) { // 对路由变化作出响应... } } } 2.使用 2.2 中引入的 beforeRouteUpdate 导航守卫: const User = { template: '...', beforeRouteUpdate

随机推荐