随笔-76  评论-222  文章-0  trackbacks-0
  2012年5月25日

  由于最近在项目中有一个需求需要用到字符串的拆分,然后将拆分后的元素逐个加到一个listview控件中。代码如下:

namespace InitalTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "1+2+3+4+5+6";

            //问题1:Split方法第一个参数是一个string[]对象,为什么有些方法当参数为string的时候不用new运算符呢?
            string[] sArrs = s.Split(new string[]{"+"},StringSplitOptions.RemoveEmptyEntries);

            //问题2:当遍历数组或集合的时候到底选用foreach还是for呢?
            foreach (string sArr in sArrs)
            {
                Console.WriteLine(sArr);
            }
            Console.ReadKey();

            //程序输出结果:
            /*
             1
             2
             3
             4
             5
             6
             */
        }
    }
}

问题1:为什么有些方法当参数为string对象的时候不用new操作符呢?

  原则上CLR要求所有对象都用new操作符来创建的。比如:Myclass mc=new Myclass();int i=new int();但是有些类型经常使用,所以编译器允许代码以简化的语法来操作它们,比如:int i=new int();可以简写为:int i=0;这种语法不仅增强了代码的可读性,而且生成的IL代码与使用new操作符时生成的IL代码是完全一样的。编译器将这种直接支持的类型称为基元类型。而string就是基元类型,所以创建string对象的时候可以使用简写语法。

问题2:当遍历数组或集合的时候到底使用for还是foreach呢?

  这个主要是看for和foreach的区别。
  共同点:for和foreach都可以用来遍历数组和集合。
  不同点:1,语法。foreach比for更简洁。
      2,语义。foreach比for更易懂。
      3,性能。foreach比for性能高。
  所以,当遍历数组或集合时,最好使用foreach语句。只要对象实现了IEumerable接口。

posted @ 2012-05-25 12:18 永远的麦子 阅读(226) 评论(1) 编辑
  2012年4月9日

一,实例构造器(构造函数)

  实例构造器是允许将类型的实例初始化为良好状态的一种特殊方法,它在类的每个新实例创建的时候执行。
  代码如下:

namespace ConstructorDemo1
{
    internal sealed class MyClass//字义类
    {
        DateTime TimeOfInstantiation;//声明字段

        public MyClass()//构造函数
        {
            TimeOfInstantiation = DateTime.Now;//初始化字段
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();//创建对象,同时执行类的实例构造函数
        }
    }
}

  注意:构造函数的执行顺序为:成员初始化--->基类构造函数调用--->构造函数体执行

二,类型构造器

  类型构造器,也称为静态构造器。它的作用是设置类型(注意不是类型的实例)的初始状态。
  代码如下:

internal sealed class MyClass
    {
        private static int s_x;//声明静态字段

        static MyClass()//类型构造函数
        {
            s_x = 10;//初始化静态字段
        }
    }

  注意:类型构造器中的代码只能访问类型的静态字段,并且它的常规用途就是初始化这些字段。

posted @ 2012-04-09 21:51 永远的麦子 阅读(212) 评论(0) 编辑
  2012年4月5日

一,类的可见性

  类有两种可见性。
  1,public,对所有程序集中的代码可见。
  2,internal,仅对定义它的程序集中的代码可见,对其它程序集中的代码不可见。
  注:如果没有显式声明类的可见性,C#编译器默认设置为internal。

二,类的成员的可访问性

  类的成员共有六种可访问性,这里总结了最常用的三种可访问性修饰符,它们的限制性从上到下,限制性由最大到最小排列。
  1,private,只能由定义它的类型中的方法访问。
  2,protected,与private相同,除了它允许它的派生类中的方法访问。
  3,public,没有访问限制。
  注:如果没有显式声明成员的可访问性,C#编译器默认设置为private。另外,任何成员想要被别人访问到,都必须在一个可见的类型内定义。

三,定义类的指导原则

  1,定义类时,除非已经确定将一个类作为基类使用,并允许派生类对其进行特化,否则总是显式地把它指定为sealed类。因为sealed不允许对其派生,这保证了类的安全性。
  2,在类的内部,数据字段最好定义为private。这样只能由类内部的方法才能访问,也保证了对象的安全性。
  3,在类的内部,方法,属性和事件最好字义为private和非虚。如果要公开类型的某些功能,可以将方法,属性和事件定义为public,否则最好不要公开。
  注:OOP的一条格言:当事情变得过于复杂时,就搞更多的类型出来。当一个算法的实现开始变得复杂时,我会定义一些辅助类来封闭独立的功能。

posted @ 2012-04-05 16:55 永远的麦子 阅读(145) 评论(0) 编辑
  2012年4月2日

  值类型是比引用类型更"轻型"的一种类型,因为它们不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用。但在许多情况下,都需要获取对值类型的一个实例引用。为了将一个值类型转换成一个引用类型,要使用一个名为装箱(boxing)的机制。

一,装箱

  装箱即将一个值类型转换成一个引用类型,下面是总结对值类型的一个实例进行装箱时内部发生的事情:

  1,在托管堆中分配内存。内存量是值类型的各个字段需要的内存量加上托管堆的所有对象都有的两个额外成员(类型对象指针和同步块索引)需要的内存量。
  2,将值类型的字段复制到新分配的堆内存。
  3,返回对象的地址(或称指针)。现在,这个地址是对一个对象的引用,值类型现在是一个引用类型。

  例如:代码如下:

namespace ValueTypeDemo2
{
//声明一个值类型
struct Point
{
public int x, y;
}
class Program
{
static void Main(string[] args)
{
ArrayList a = new ArrayList();
Point p;//分配一个Point(不在堆中分配)

for (int i = 0; i < 10; i++)
{
p.x = p.y = i;//初始化值类型中的成员
a.Add(p);//对值类型进行装箱,并将引用添加到ArrayList中,因为Add()方法需要的是一个Object对象
}
}
}
}

二,拆箱

  拆箱实际上就是获取一个指向包含在一个对象中的原始值类型(数据字段)的指针,往往会紧接着拆箱操作后发生一次字段复制的操作,将堆中的字段复制到基于栈的值类型的实例中。
  例如:代码如下:

Point p=(Point)a[0];

  CLR实际上分两步完成这个复制操作:
  1,获取已装箱的Point对象中的各个Point字段的址,这个过程称为拆箱(unboxing)。
  2,将这些字段包含的值从堆中复制到基于栈的值类型的实例中。注意,这个过程不属于拆箱的范围。

  下面是一个已装箱值类型实例在拆箱时内部发生的事情:
  1,如果包含"对已装箱值类型实例的引用"的变量为Null,就抛出一个NullReferenceException异常。
  2,如果引用指向的对象不是期待的值类型的一个已装箱实例,就抛出一个InvalidCastException异常。
  3,返回一个指向包含在一个对象中的原始值类型的指针。

下面是一个综合实例,代码如下:

namespace ValueTypeDemo3
{
//声明一个值类型
struct Point
{
public int x, y;
}
class Program
{
static void Main(string[] args)
{
Point p;//创建Point的实例,在栈上分配
p.x = p.y = 1;//初始化值类型的成员

object o = p;//对p进行装箱,o引用已装箱的实例
p = (Point)o;//对o进行拆箱,将字段从已装箱的实例中复制到栈变量中
}
}
}

  生成的IL代码如下图,可以看到程序发生了一次装箱和一次拆箱:

posted @ 2012-04-02 12:14 永远的麦子 阅读(141) 评论(0) 编辑

  下面是FCL中值类型和引用类型的区别,用表格总结如下:

 

值类型

引用类型

内存分配

线程栈

托管堆

垃圾回收

不考虑

考虑

表示形式

未装箱和已装箱

总是已装箱

是否可以作为基类

不能作为基类,不能有虚方法

可以作为基类,也能有虚方法

初始化的值

0

Null

复制

逐字段复制(深拷贝)

只复制内存地址(浅拷贝)

操作时影响

只影响当前对象,不会影响另一个对象

这个引用所对应的所有对象

 
  下面是一个实际的例子详细说明了值类型和引用类型在内存中的布局,代码如下:  
namespace ValueTypeDemo1
{
//值类型
struct SomeVal
{
public int x;
}
//引用类型
class SomeRef
{
public int x;
}
class Program
{
static void Main(string[] args)
{
SomeVal v1 = new SomeVal();//在线程栈上分配
SomeRef r1 = new SomeRef();//在托管堆上分配

v1.x = 5;//在栈上修改
r1.x = 5;//提取指针

Console.WriteLine(v1.x);//输出"5"
Console.WriteLine(r1.x);//同样输出"5"

SomeVal v2 = v1;//在栈上分配并深拷贝成员
SomeRef r2 = r1;//只复制引用(指针)

v1.x = 9;//v1.x会更改,v2.x不会更改
r1.x = 8;//r1.x和r2.x都会更改

Console.WriteLine(v1.x);//输出"9"
Console.WriteLine(v2.x);//输出"5"
Console.WriteLine(r1.x);//输出"8"
Console.WriteLine(r2.x);//输出"8"
}
}
}
  
  下面是程序在内存中的布局,如图所示:

 

posted @ 2012-04-02 10:01 永远的麦子 阅读(106) 评论(0) 编辑
  2012年3月26日

一,对象创建的原理

  CLR要求所有的对象都用new操作符来创建,如下代码:

namespace NewobjectDemo1
{
class Employee
{
private string Name;
//构造函数
public Employee(string name)
{
Name = name;
}
}
class Program
{
static void Main(string[] args)
{
Employee e = new Employee("Mcgrady");//使用new操作符创建对象
}
}
}

  编译后的IL代码:


  注:new操作符在IL中的对应的代码为newobj。

  实际上new操作符帮我们做了以下事件:

  1,计算类型及其所有基类型所需要的字节数并依此分配内存大小。

  2,初始化对象的"类型对象指针"和"同步块索引"成员。

  3,调用类型的实例构造器,向其传入在对new 调用中指定的任何实参(上例中就是字符串"Mcgrady")。

二,两种类型转换方法

  方法一:隐式转换和显式(或叫强制)转换

      从派生类型向它的基类型转换叫隐式转换,而从基类型向它的某个派生类型转换叫显式转换。如下代码:

namespace NewobjectDemo1
{
//该类型隐式派生自Object
class Employee
{
}
class Program
{
static void Main(string[] args)
{
//隐式转换,因为new返回一个Employee对象,而Object是Employee的基类
Object o = new Employee();

//显式转换,因为Employee派生自Object
Employee e=(Employee)o;
}
}
}

  

  方法二:使用is和as操作符来转型

      is检查一个对象是否兼容于指定的类型并返回一个bool值:true或false;as操作符执行实际的转换。如下代码:

namespace NewobjectDemo1
{
//该类型隐式派生自Object
class Employee
{
}
class Program
{
static void Main(string[] args)
{
object o = new object();

//检查对象o是否兼容于类型Employee
if (o is Employee)//如果对象引用为null,is操作符总是返回false,注意is操作符永远不会抛出异常
{
Employee e = o as Employee;//等价于:Employee e=(Employee)o

//在使用变量e之前最好检查引用是否为null
if (e != null)
{
//有if语句中使用e
e.ToString();
}
}
}
}
}
posted @ 2012-03-26 21:49 永远的麦子 阅读(177) 评论(2) 编辑
  2012年3月24日
摘要: CLR(Common Language Runtime,公共语言运行时)是.NET Framework框架中最核心的内容,所谓CLR的执行模型,简单地讲就是解释在.NET平台下代码是如何编译和运行的。用一幅图概括如下:下面是各个阶段的工作。1,将源代码编译成托管模块 Microsoft已创建了针对各个语言的编译器,编译时编译器会去分析源代码和检查代码的语法,如果没有问题则能顺利通过编译,最终生成一个托管模块(managed module),托管模块由四部分组成,如下图所示。 1> PE32文件头:如果使用PE32格式,表明文件在windows的32位和64位版本都能运行,如果使用PE3.阅读全文
posted @ 2012-03-24 14:56 永远的麦子 阅读(176) 评论(0) 编辑
  2012年3月3日
摘要: 命名空间System.Collections.Generic中有两个非常重要,而且常用的泛型集合类,它们分别是Dictionary<TKey,TValue>字典和List<T>列表。Dictionary字典通常用于保存键/值对的数据,而List列表通中用于保存可通过索引访问的对象的强类型列表。下面来总结一下,用代码来演示怎么初始化,增加,修改,删除和遍历元素。Dictionary<TKey,TValue>字典 代码如下:namespace DictionaryDemo1{ class Program { static void Main(string[] a阅读全文
posted @ 2012-03-03 18:02 永远的麦子 阅读(1442) 评论(4) 编辑
  2012年2月18日
摘要: 前面已经提到了,要想在程序多次运行间保存数据就必须使用独立存储。任何安装在Windows Phone 7中的程序都可以访问自身的永久磁盘存储区域,这个区域称为独立存储。下面这个项目,将轻击的总数作为临时数据,也就是作为页面状态的一部分。将背景颜色作为应用程序设置被所有实例共享。 MainPage.xaml XAML代码:<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinit阅读全文
posted @ 2012-02-18 10:41 永远的麦子 阅读(1007) 评论(1) 编辑
  2012年2月15日
摘要: 上一个项目实现的功能是如何从源页面传递数据到目标页面,但是,当回到源页面时,如何才能返回数据,实现数据共享呢?这个就是这一篇文章要解决的问题,而且解决这个问题有几个方案,总结如下。这里共享的数据是页面背景颜色。方案一:使用App类来存储共享数据 MainPage.xaml XAML代码:<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <TextBlock Text="Navigate to Second Page!" Hor阅读全文
posted @ 2012-02-15 21:25 永远的麦子 阅读(1155) 评论(9) 编辑
仅列出标题  下一页