C#编码简单性之泛型篇(如何编写简短的C#代码,随时更新)

简介:

这是编码简单性系列中的其中一篇,之前几篇包括代码篇/函数篇/语义篇。 因为要积累案例,会随时更新。

 

之前提到:编码简单性的“心法”就是:只要屏幕上有任何两部分代码看上去相似,则一定有合并办法。而说起相似,没有比switch - case的各段代码更相似的了。如果细数一下自己产品中最长的函数,里边几乎肯定的有一个switch - case,或者一堆if -else if(两者其实等同)。一般各段代码看似相同,又有点不同,既不能变成函数,也不能变成类,怎么办呢?

解决臭长的switch-case的最好方法,就是泛型(在C++时代叫做模板)。泛型不好学,但是却很重要。02年左右的时候曾经作为过程改进人员进行代码审查,无意中发现一段在pagedown的时候做波浪状起伏的代码,观察发现其中有65个函数其实可以缩减为1个函数(没错,65:1),其内容是在5种int常数下,处理13种不同的变量,而处理方法完全相同。这段代码共有4000行,已经耗时一个月,编程者月薪7000(那是10年前,7000元可以购买5平米小产权房,或2平米大产权房);当天下午它们就变成了1个函数,长度不足55行。据此推断,每年因为不能灵活使用泛型而造成的经济损失,可能达到亿元以上。


案例1 2011-05-27 一个简单泛型类

下面一段代码很简短,显得很不值得改造,但其实除了Display之外还有很多函数,其中一些case体很长,而且各种type日后层出不穷,因此不得不进行改造。

Display的目的,是让各种各样的udc们以自己的方式显示器Value中的数值:

 
  1.  
  2. [csharp] view plaincopy  
  3. 01.            
  4. 02.public static MvcHtmlString Display(this HtmlHelper htmlHelper, UDC udc)    
  5. 03.        {    
  6. 04.            switch (udc.Type)    
  7. 05.            {    
  8. 06.                case "Text100":    
  9. 07.                    return new MvcHtmlString((udc.Value != null) ? udc.Value.ToString() : "NULL");    
  10. 08.                case "Text20":    
  11. 09.                    return new MvcHtmlString((udc.Value != null) ? udc.Value.ToString() : "NULL");    
  12. 10.                case "Date":    
  13. 11.                    return new MvcHtmlString(((DateTime)udc.Value).ToShortDateString());    
  14. 12.                default: return new MvcHtmlString(string.Format("Unknown UDC type: {0}", udc.Type));    
  15. 13.            }    
  16. 14.        }    

为了能够拆开这个函数,需要声明

 
  1.  
  2. [csharp] view plaincopy  
  3. 01.        
  4. 02.public interface IUDC    
  5. 03.    {    
  6. 04.        ……    
  7. 05.    
  8. 06.        MvcHtmlString Display { get; }    
  9. 07.        ……    
  10. 08.    
  11. 09.    }    

然后让一个类(这个类一般都存在了)继承这个接口,并实现其方法:

 
  1.  
  2. [csharp] view plaincopy  
  3. 01.        
  4. 02.public partial class UDCText100 : IUDC     
  5. 03.    {    
  6. 04.        ……    
  7. 05.    
  8. 06.        public MvcHtmlString Display { get { return new MvcHtmlString((Value != null) ? Value.ToString() : "NULL"); } }    
  9. 07.        ……    
  10. 08.    
  11. 09.    }    

而显示方式也从@Html.Display(udc)变成@udc.Display。

这段代码整个修改后,原来的5个switch-case只剩下1个,代码明显更内聚了,也就是每次增加一个类型,基本只需要在一个地方(partial class)内完成修改,就可以了;修改的成果在编译时就能确认是否充分和正确(而在其中switch-case中漏掉一个类型,只有在运行的时候case找不到才知道)。案例2 2011-05-29 泛型函数

 
  1.  
  2. [csharp] view plaincopy  
  3. 01.            
  4. 02.private IUDC GetOrDefaultUDC<T, V>(Table<T> table, ..., V defaultValue) where T : class, IUDC, new()    
  5. 03.        {    
  6. 04.            var t = table.SingleOrDefault(...);    
  7. 05.            if (t == null)    
  8. 06.            {    
  9. 07.                t = new T();    
  10. 08.                ...    
  11. 09.                t.OValue = defaultValue;     
  12. 10.                ...    
  13. 11.    
  14. 12.            }    
  15. 13.            return t;    
  16. 14.        }    

这个泛型函数从一类表中取出一条记录,如果没有,则创建它。由于创建的时候需要new 一个新的纪录,而且新纪录依据table的不同,某些字段的缺省值也不同。用传统的方法需要编写很多函数,而且每当出现一种新的这类表,又需要编写新的函数。本文开头提到的的那个65个函数就是因此而产生的。

处理这类情况的心法是:如果发现由于类型的差异一些看起来很像的代码无法合并为函数,那么就应该用泛型了

这种情况下用泛型需要掌握两种主要技术:new()和Ixxx(某Interface)。new的目的是让T可以创建(如果函数中不创建就不需要了),而Interface的目的是保证编译时刻就能确保未来的新类型依然可以使用这个函数(C++的时代只有用到新的类型的时候才可以确认)。


 

这里只谈泛型的应用思想,关于泛型的应用技术,在MSDN有一篇很好的文章可供参考:http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx。此文将整个C#泛型的发展历程从头到尾讲了一遍。



本文转自火星人陈勇 51CTO博客,原文链接:http://blog.51cto.com/cheny/1100096

相关文章
|
1月前
|
C# Windows
C#通过代码实现快捷键编辑
C#通过代码实现快捷键编辑
|
3月前
|
存储 安全 编译器
C# 11.0中的泛型属性:类型安全的新篇章
【1月更文挑战第23天】C# 11.0引入了泛型属性的概念,这一新特性为开发者提供了更高级别的类型安全性和灵活性。本文将详细探讨C# 11.0中泛型属性的工作原理、使用场景以及它们对现有编程模式的改进。通过深入了解泛型属性,开发者将能够编写更加健壮、可维护的代码,并充分利用C#语言的最新发展。
|
3月前
|
开发框架 .NET 编译器
C# 10.0中Lambda表达式的改进:更简洁、更灵活的代码编写体验
【1月更文挑战第21天】随着C#语言的不断发展,Lambda表达式作为一种简洁、高效的函数式编程工具,在C# 10.0中迎来了重要的改进。本文将详细探讨C# 10.0中Lambda表达式的新特性,包括参数类型的推断增强、自然类型的Lambda参数以及Lambda表达式的属性改进等。这些改进不仅简化了Lambda表达式的编写过程,还提升了代码的可读性和灵活性,为开发者带来了更优质的编程体验。
|
3月前
|
C# 开发者
C# 10.0中的文件范围命名空间:简化代码组织的新方式
【1月更文挑战第18天】C# 10.0引入了文件范围的命名空间,这是一种新的语法糖,用于更简洁地组织和管理代码。文件范围命名空间允许开发者在每个文件的基础上定义命名空间,而无需显式使用花括号包裹整个文件内容。本文将深入探讨文件范围命名空间的工作原理、使用场景以及它们为C#开发者带来的便利。
|
4月前
|
存储 人工智能 C#
【Unity 3D】C#中数组、集合、栈、队列、哈希表、字典的讲解(附测试代码)
【Unity 3D】C#中数组、集合、栈、队列、哈希表、字典的讲解(附测试代码)
36 0
|
4月前
|
IDE C# 开发工具
C# | 多线程批量下载文件(创建N个线程同时批量下载文件,只需要几行代码而已)
批量下载文件时使用多线程可以有效缩短完成时间,本文将讲解如何使用C#+CodePlus扩展库快速完成多线程的文件下载。 大部分代码由IDE自动生成,需要我们自己编写的代码正好**10行**。也就是说,只需要10分钟,就可以手撸一个多线程的批量下载器。
83 0
C# | 多线程批量下载文件(创建N个线程同时批量下载文件,只需要几行代码而已)
|
1月前
|
存储 安全 Java
34.C#:listT泛型集合
34.C#:listT泛型集合
16 1
|
1月前
|
开发框架 安全 .NET
C# .NET面试系列三:集合、异常、泛型、LINQ、委托、EF!
<h2>集合、异常、泛型、LINQ、委托、EF! #### 1. IList 接口与 List 的区别是什么? IList 接口和 List 类是C#中集合的两个相关但不同的概念。下面是它们的主要区别: <b>IList 接口</b> IList 接口是C#中定义的一个泛型接口,位于 System.Collections 命名空间。它派生自 ICollection 接口,定义了一个可以通过索引访问的有序集合。 ```c# IList 接口包含一系列索引化的属性和方法,允许按索引访问、插入、移除元素等。 由于是接口,它只定义了成员的契约,而不提供具体的实现。类似于 IEnumera
149 2
|
2月前
|
数据采集 JSON 前端开发
从代码到内容:使用C#和Fizzler探索Instagram的深处
Instagram是一个流行的社交媒体平台,拥有数亿的用户和海量的图片和视频内容。如果您想要从Instagram上获取一些有用的信息或数据,您可能需要使用爬虫技术来自动化地抓取和分析网页内容。本文将介绍如何使用C#和Fizzler这两个强大的工具,来实现一个简单而高效的Instagram爬虫,从代码到内容,探索Instagram的深处。
|
3月前
|
存储 传感器 监控
工业相机如何实现实时和本地Raw格式图像和Bitmap格式图像的保存和相互转换(C#代码,UI界面版)
工业相机如何实现实时和本地Raw格式图像和Bitmap格式图像的保存和相互转换(C#代码,UI界面版)
29 0