c#的default、using和yield使用小结

一、default的应用场合

1、switch...case
在switch语句中,如果没有任何case表达式与开关值匹配,则控制传递给跟在可选default标签后的语句。如果没有default标签,则控制传递到switch以外。
对于c#,为了便于维护,建议default项最好要写。
示例代码:

ContractedBlock.gif ExpandedBlockStart.gif Code
        private void PgInitial(orderType)
        {
            
switch (orderType)
            {
                
default:
                    
break;
                
case -1:
                    
this.rblOrderType.SelectedIndex = 0;
                    
break;
                
case 11:
                    
this.rblOrderType.SelectedIndex = 1;
                    
break;
                
case 111:
                    
this.rblOrderType.SelectedIndex = 2;
                    
break;
                
case 1111:
                    
this.rblOrderType.SelectedIndex = 3;
                    
break;
                
case 11111:
                    
this.rblOrderType.SelectedIndex = 4;
                    
break;

            }
  
        }

2、在泛型类和泛型方法中,在预先未知以下情况时,如何将默认值分配给参数化类型T:
(1)T是引用类型还是值类型;
(2)如果T为值类型,则它是数值还是结构。
示例代码:

     public   class  GenericClass < T >
    {
        
public   static  T GetDefaultT < T > (T obj)
        {
            obj 
=   default (T);  // 将默认值分配给参数化类型T
             return  obj;
        }
    }

备注:  给定参数化类型T的一个变量obj,只有当T为数值类型而且不是结构时,语句t = 0才能正常使用;只有当T为引用类型时,语句obj= null才有效。
使用default关键字,对于数值类型会返回零,对于引用类型会返回空,对于结构,此关键字将返回初始化为零或空的每个结构成员,具体取决于这些结构是值类型还是引用类型。
看下面的代码:

ContractedBlock.gif ExpandedBlockStart.gif Code
        static void Main(string[] args)
        {
            Console.WriteLine(
default(int));//0
            Console.WriteLine(default(string)); //null
            Console.WriteLine(default(string== null); //true
            Console.WriteLine(default(char)); //0
            Console.WriteLine(default(char== 0); //true
            Console.WriteLine(default(int?== null); //default<Nullable> == null //true
            Console.WriteLine(default(double?== null); //true
            Console.ReadKey();
        }

二、using的应用场合
1、直接引入命名空间
using System.Data.SqlClient; //引入命名空间
2.using别名。using + 别名 = 包括详细命名空间信息的具体的类型。
这种做法有个好处就是当同一个cs引用了两个不同的命名空间,但两个命名空间都包括了一个相同名字的类型的时候。当需要用到这个类型的时候,就每个地方都要用详细命名空间的办法来区分这些相同名字的类型。而用别名的方法会更简洁,用到哪个类就给哪个类做别名声明就可以了。
注意:并不是说两个名字重复,给其中一个用了别名,另外一个就不需要用别名了,如果两个都要使用,则两个都需要用using来定义别名的。
例如我们用以下语句引入using System.Data.SqlClient命名空间:
using SqlServer=using System.Data.SqlClient;
这时我们就可以用 SqlServer表示using System.Data.SqlClient命名空间,给程序书写带来方便。
3、using语句定义一个范围,在该范围内处理对象。
当年刚学asp.net那会,曾经对照着书本,吭哧吭哧动手写了很多类似下面的代码:

ContractedBlock.gif ExpandedBlockStart.gif Code
        /// <summary>
        
///对sqlserver数据库进行操作
        
/// </summary>
        
/// <param name="filePath"></param>
        void DoSomething(string connString)
        {
            
using (SqlConnection cn = new SqlConnection(connString))
            {
                
//do something code here
            }
        }

直到遇到SqlHelper,才省了很多力气花在业务逻辑上,当年真是...,呵呵。
言归正传,什么时候适合用using呢?当在某个代码段中使用了类的实例,而希望无论因为什么原因,只要离开了这个代码段就自动调用这个类实例的Dispose方法。要达到这样的目的,用try...finally是可以的,但用using看起来更方便和简洁。看下面的代码:

ContractedBlock.gif ExpandedBlockStart.gif Code
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.IO;

namespace MyCsStudy
{
    
class Program
    {
        
/// <summary>
        
/// 读取文件(using)
        
/// </summary>
        
/// <param name="filePath"></param>
        static void UsingReadFile(string filePath)
        {
            
using (StreamReader sr = new StreamReader(filePath)) //using
            {
                
// do something
                
//sr.Close(); //有了using,最后这个Close可以不写,写了也无妨
                Console.WriteLine("using");
            }
        }

        
/// <summary>
        
/// 读取文件(tryfinally)
        
/// </summary>
        
/// <param name="filePath"></param>
        static void TryReadFile(string filePath)
        {
            StreamReader sr 
= null;
            
try
            {
                sr 
= new StreamReader(filePath);
                
// do something
                Console.WriteLine("tryfinally");
            }
            
finally
            {
                sr.Close();
            }
        }

        
static void Main(string[] args)
        {
            
string path = @"D:\Program Files\Microsoft SQL Server\MSSQL10.SQL2008\sql.txt";
            UsingReadFile(path);
            TryReadFile(path);
            Console.Read();
        }
    }
}

 我们看到,using确实比try...finally少几行代码,不过,它们的性能怎么样呢?看两个方法各自生成的对应的IL:
(1)使用using

(2)使用try...finally

原来它们的性能几乎是一样的。上面的两段IL可以作为using和try...finally性能的参照。如果你在程序中经常try...catch或者using,对性能的影响是很明显的。
需要引起注意的是,using实例化一个对象的时候,对应的类必须实现IDisposable接口,比如我们延续上面的代码,增加一个如下方法:

         static   void  Test()
        {
            
using  (Program program  =   new  Program())  // 编译器报错,Program类必须要实现IDisposable接口
            {
                
// do something
            }
        }

正像注释写的那样,类Program必须实现IDisposable接口,写成class Program : IDisposable{...}这样就可以了。
三、yield的应用
1、迭代器块中用于向枚举数对象提供值或发出迭代结束信号。
它的形式如下:
yield return <expression>;或者yield break;
下面看一个示例: 

ContractedBlock.gif ExpandedBlockStart.gif Code
using System;
using System.Collections;
using System.Collections.Generic;

namespace MyCsStudy
{
    
/// <summary>
    
/// 程序员,继承接口IEnumerable,实现Programer自己的迭代器
    
/// </summary>
    public class Programer : IEnumerable
    {
        
string[] strNameArr = new string[] { };//存程序员名
        public Programer(params string[] inputNames)
        {
            strNameArr 
= new string[inputNames.Length];
            inputNames.CopyTo(strNameArr, 
0);
        }

        
public  IEnumerator GetEnumerator()
        {
            
foreach (string s in strNameArr)
            {
                
yield return s; //通过yield return输出遍历程序员名称结果集
            }
        }
    }

    
class Program
    {
        
static void Main(string[] args)
        {
            
string[] programers = new string[] { "jeffery zhao""dudu""terrylee""jeffwong" };
            Programer cnPrograms 
= new Programer(programers);

            
//下面注释部分的写法我们非常熟悉
            
//IEnumerator e = cnPrograms.GetEnumerator();
            
//while (e.MoveNext())
            
//{
            
//    Console.WriteLine(e.Current);
            
//}

            
//foreach in 和上面的注释代码实质是等价的
            foreach (string s in cnPrograms)
            {
                Console.WriteLine(s);
            }

            Console.ReadLine();
        }
    }
}

2、几个要注意的地方 
(1)、计算表达式并以枚举数对象值的形式返回;expression 必须可以隐式转换为迭代器的 yield 类型。
(2)、yield 语句只能出现在iterator 块中,该块可用作方法、运算符或访问器的体。
这类方法、运算符或访问器的体受以下约束的控制:
a、不允许不安全块。
b、方法、运算符或访问器的参数不能是 ref 或 out。
c、yield 语句不能出现在匿名方法中。
(3)、当和expression一起使用时,yield return语句不能出现在catch块中或含有一个或多个catch子句的 try 块中。

3、最后,为了深入理解迭代,我们有必要介绍一下.net两个和迭代关联密切的接口:IEnumerable和IEnumerator
(1)、IEnumerable和IEnumerator元数据对比
a、IEnumerator

ContractedBlock.gif ExpandedBlockStart.gif Code
using System;
using System.Runtime.InteropServices;

namespace System.Collections
{
    
// 摘要:
    
//     支持对非泛型集合的简单迭代。
    [ComVisible(true)]
    [Guid(
"496B0ABF-CDEE-11d3-88E8-00902754C43A")]
    
public interface IEnumerator
    {
        
// 摘要:
        
//     获取集合中的当前元素。
        
//
        
// 返回结果:
        
//     集合中的当前元素。
        object Current { get; }

        
// 摘要:
        
//     将枚举数推进到集合的下一个元素。
        
//
        
// 返回结果:
        
//     如果枚举数成功地推进到下一个元素,则为 true;如果枚举数越过集合的结尾,则为 false。
        bool MoveNext();
        
//
        
// 摘要:
        
//     将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
        void Reset();
    }
}

b、IEnumerable

ContractedBlock.gif ExpandedBlockStart.gif Code
using System.Runtime.InteropServices;

namespace System.Collections
{
    
// 摘要:
    
//     公开枚举数,该枚举数支持在非泛型集合上进行简单迭代。
    [ComVisible(true)]
    [Guid(
"496B0ABE-CDEE-11d3-88E8-00902754C43A")]
    
public interface IEnumerable
    {
        
// 摘要:
        
//     返回一个循环访问集合的枚举数。
        
//
        
// 返回结果:
        
//     可用于循环访问集合的 System.Collections.IEnumerator 对象。
        [DispId(-4)]
        IEnumerator GetEnumerator();
    }
}

(2)、说明
IEnumerator:提供在普通集合中遍历的接口,有Current,MoveNext(),Reset(),其中Current属性返回的是object类型,另外两个是实例方法;
IEnumerable: 暴露一个IEnumerator,支持在普通集合中的遍历;
IEnumerator<T>:继承自IEnumerator,有Current属性,返回的是T类型;
IEnumerable<T>:继承自IEnumerable,暴露一个IEnumerator<T>,支持在泛型集合中遍历。

(3)总结
a、通过分析(1)中的源码,你也可以从这两个接口的用词选择上,看出其不同:IEnumerable是一个声明式的接口,声明实现该接口的类是“可枚举(enumerable)”的,但并没有说明如何实现iterator;IEnumerator是一个实现式的接口,IEnumerator类就是一个iterator。
b、一个集合要支持foreach方式的遍历,必须实现IEnumerable接口(即必须以某种方式返回IEnumerator对象);
c、IEnumerator对象具体实现了iterator(通过内部的MoveNext(),Reset(),Current)。
d、IEnumerable和IEnumerator通过IEnumerable的GetEnumerator()方法建立了连接,客户端可以通过IEnumerable的GetEnumerator()得到IEnumerator对象,所以从实质上来讲,将GetEnumerator()看作IEnumerator对象的工厂方法也未尝不可。

 


 


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值