[你必须知道的.NET]第九回:品味类型---值类型与引用类型(中)-规则无边

 发布日期:2007.5.28 作者:Anytao

©2007 Anytao.com ,原创作品,转贴请注明作者和出处。

 

接上回[第八回:品味类型---值类型与引用类型(上)-内存有理]的探讨,继续我们关注值类型和引用类型的话题。

本文将介绍以下内容:

  • 类型的基本概念 
  • 值类型深入
  • 引用类型深入
  • 值类型与引用类型的比较及应用

  

1. 引言

上回[第八回:品味类型---值类型与引用类型(上)-内存有理]的发布,受到大家的不少关注,我们从内存的角度了解了值类型和引用类型的所以然,留下的任务当然是如何应用类型的不同特点在系统设计、性能优化等方面发挥其作用。因此,本回是对上回有力的补充,同时应朋友的希望,我们尽力从内存调试的角度来着眼一些设计的分析,这样就有助于对这一主题进行透彻和全面的理解,当然这也是下一回的重点。

从内存角度来讨论值类型和引用类型是有理有据的,  而从规则的角度来了解值类型和引用类型是无边无际的。本文旨在从上文呼应的角度,来把这个主题彻底的融会贯通,无边无迹的应用,还是来自反复无常的实践,因此对应用我只能说以一个角度来阐释观点,但是肯定不可能力求全局。因此,我们从以下几个角度来完成对值类型与引用类型应用领域的讨论。 

2. 通用规则与比较

通用有规则:

  • string类型是个特殊的引用类型,它继承自System.Object肯定是个引用类型,但是在应用表现上又凸现出值类型的特点,那么究竟是什么原因呢?例如有如下的一段执行:

 

 简单的说是由于string的immutable特性,因此每次对string的改变都会在托管堆中产生一个新的string变量,上述string作为参数传递时,实际上执行了s=s操作,在托管堆中会产生一个新的空间,并执行数据拷贝,所以才有了类似于按值传递的结果。但是根据我们的内存分析可知,string在本质上还是一个引用类型,在参数传递时发生的还是按址传递,不过由于其特殊的恒定特性,在函数内部新建了一个string对象并完成初始化,但是函数外部取不到这个变化的结果,因此对外表现的特性就类似于按值传递。至于string类型的特殊性解释,我推荐Artech的大作《深入理解string和如何高效地使用string》。

另外,string类型重载了==操作符,在类型比较是比较的是实际的字符串,而不是引用地址,因此有以下的执行结果:

            string aString = "123";
            
string bString = "123";
            Console.WriteLine((aString 
== bString)); //显示为true,等价于aString.Equals(bString);
            string cString = bString;
            cString 
= "456";
            Console.WriteLine((bString 
== cString)); //显示为false,等价于bString.Equals(cString);
  • 通常可以使用Type.IsValueType来判断一个变量的类型是否为值类型,典型的操作为: 
    public struct MyStructTester
    { }

    
public class isValueType_Test
    {
        
public static void Main()
        {
            MyStructTester aStruct 
= new MyStructTester();
            Type type 
= aStruct.GetType();
            
if (type.IsValueType)
            {
                Console.WriteLine(
"{0} belongs to value type.", aStruct.ToString());
            }
 
        }
    }
  • .NET中以操作符ref和out来标识值类型按引用类型方式传递,其中区别是:ref在参数传递之前必须初始化;而out则在传递前不必初始化,且在传递时必须显式赋值。
  • 值类型与引用类型之间的转换过程称为装箱与拆箱,这值得我们以专门的篇幅来讨论,因此留待后文详细讨论这一主题。
  • sizeof()运算符用于获取值类型的大小,但是不适用于引用类型。
  • 值类型使用new操作符完成初始化,例如:MyStruct aTest = new MyStruct(); 而单纯的定义没有完成初始化动作,此时对成员的引用将不能通过编译,例如: 
MyStruct aTest;
Console.WriteLine(aTest.X); 
  • 引用类型在性能上欠于值类型主要是因为以下几个方面:引用类型变量要分配于托管堆上;内存释放则由GC完成,造成一定的CG堆压力;同时必须完成对其附加成员的内存分配过程;以及对象访问问题。因此,.NET系统不能由纯粹的引用类型来统治,性能和空间更加优越和易于管理的值类型有其一席之地,这样我们就不会因为一个简单的byte类型而进行复杂的内存分配和释放工作。Richter就称值类型为“轻量级”类型,简直恰如其分,处理数据较小的情况时,应该优先考虑值类型。
  • 值类型都继承自System.ValueType,而System.ValueType又继承自System.Object,其主要区别是ValueType重写了Equals方法,实现对值类型按照实例值比较而不是引用地址来比较,具体为:
char a = 'c';
char b = 'c';
Console.WriteLine((a.Equals(b))); 
//会返回true; 
  • 基元类型,是指编译器直接支持的类型,其概念其实是针对具体编程语言而言的,例如C#或者VB.NET,通常对应用.NET Framework定义的内置值类型。这是概念上的界限,不可混淆。例如:int对应于System.Int32,float对应于System.Single。

比较出真知:

  • 值类型继承自ValueType(注意:而System.ValueType又继承自System.Object);而引用类型继承自System.Object。
  • 值类型变量包含其实例数据,每个变量保存了其本身的数据拷贝(副本),因此在默认情况下,值类型的参数传递不会影响参数本身;而引用类型变量保存了其数据的引用地址,因此以引用方式进行参数传递时会影响到参数本身,因为两个变量会引用了内存中的同一块地址。
  • 值类型有两种表示:装箱与拆箱;引用类型只有装箱一种形式。我会在下节以专门的篇幅来深入讨论这个话题。
  • 典型的值类型为:struct,enum以及大量的内置值类型;而能称为类的都可以说是引用类型。 struct和class主要的区别可以参见我的拙作《第四回:后来居上:class和struct》来详细了解,也是对值类型和引用类型在应用方面的有力补充。
  • 值类型的内存不由GC(垃圾回收,Gabage Collection)控制,作用域结束时,值类型会自行释放,减少了托管堆的压力,因此具有性能上的优势。例如,通常struct比class更高效;而引用类型的内存回收,由GC来完成,微软甚至建议用户最好不要自行释放内存。
  • 值类型是密封的(sealed),因此值类型不能作为其他任何类型的基类,但是可以单继承或者多继承接口;而引用类型一般都有继承性。 
  • 值类型不具有多态性;而引用类型有多态性。
  • 值类型变量不可为null值,值类型都会自行初始化为0值;而引用类型变量默认情况下,创建为null值,表示没有指向任何托管堆的引用地址。对值为null的引用类型的任何操作,都会抛出NullReferenceException异常。
  • 值类型有两种状态:装箱和未装箱,运行库提供了所有值类型的已装箱形式;而引用类型通常只有一种形式:装箱。

3. 对症下药-应用场合与注意事项

现在,在内存机制了解和通用规则熟悉的基础上,我们就可以很好的总结出值类型和引用类型在系统设计时,如何作出选择?当然我们的重点是告诉你,如何去选择使用值类型,因为引用类型才是.NET的主体,不必花太多的关照就可以赢得市场。

3.1 值类型的应用场合

  • MSDN中建议以类型的大小作为选择值类型或者引用类型的决定性因素。数据较小的场合,最好考虑以值类型来实现可以改善系统性能;
  • 结构简单,不必多态的情况下,值类型是较好的选择;
  • 类型的性质不表现出行为时,不必以类来实现,那么用以存储数据为主要目的的情况下,值类型是优先的选择;
  • 参数传递时,值类型默认情况下传递的是实例数据,而不是内存地址,因此数据传递情况下的选择,取决于函数内部的实现逻辑。值类型可以有高效的内存支持,并且在不暴露内部结构的情况下返回实例数据的副本,从安全性上可以考虑值类型,但是过多的值传递也会损伤性能的优化,应适当选择;
  • 值类型没有继承性,如果类型的选择没有子类继承的必要,优先考虑值类型;
  • 在可能会引起装箱与拆箱操作的集合或者队列中,值类型不是很好的选择,因为会引起对值类型的装箱操作,导致额外内存的分配,例如在Hashtable。关于这点我将在后续的主题中重点讨论。 

3.2 引用类型的应用场合

  • 可以简单的说,引用类型是.NET世界的全值杀手,我们可以说.NET世界就是由类构成的,类是面向对象的基本概念,也是程序框架的基本要素,因此灵活的数据封装特性使得引用类型成为主流;
  • 引用类型适用于结构复杂,有继承、有多态,突出行为的场合;
  • 参数传递情况也是考虑的必要因素;    

4. 再论类型判等

类型的比较通常有Equals()、ReferenceEquals()和==/!=三种常见的方法,其中核心的方法是Equals。我们知道Equals是System.Object提供的虚方法,用于比较两个对象是否指向相同的引用地址,.NET Framework的很多类型都实现了对Equals方法的重写,例如值类型的“始祖”System.ValueType就重载了Equal方法,以实现对实例数据的判等。因此,类型的判等也要从重写或者重载Equals等不同的情况具体分析,对值类型和引用类型判等,这三个方法各有区别,应多加注意。

4.1 值类型判等

  • Equals,System.ValueType重载了System.Object的Equals方法,用于实现对实例数据的判等。
  • ReferenceEquals,对值类型应用ReferenceEquals将永远返回false。
  • ==,未重载的==的值类型,将比较两个值是否“按位”相等。

4.2 引用类型判等 

  • Equals,主要有两种方法,如下  
public virtual bool Equals(object obj);
public static bool Equals(object objA, object objB);

 一种是虚方法,默认为引用地址比较;而静态方法,如果objA是与objB相同的实例,或者如果两者均为空引用,或者如果objA.Equals(objB)返回true,则为true;否则为false。.NET的大部分类都重写了Equals方法,因此判等的返回值要根据具体的重写情况决定。 

  • ReferenceEquals,静态方法,只能用于引用类型,用于比较两个实例对象是否指向同一引用地址。
  • ==,默认为引用地址比较,通常进行实现了==的重载,未重载==的引用类型将比较两个对象是否引用地址,等同于引用类型的Equals方法。因此,很多的.NET类实现了对==操作符的重载,例如System.String的==操作符就是比较两个字符串是否相同。而==和equals方法的主要区别,在于多态表现上,==是被重载,而Equals是重写。

有必要在自定义的类型中,实现对Equals和==的重写或者重载,以提高性能和针对性分析。 

5. 再论类型转换

类型转换是引起系统异常一个重要的因素之一,因此在有必要在这个主题里做以简单的总结,我们不力求照顾全面,但是追去提纲挈领。常见的类型转换包括:

  • 隐式转换:由低级类型项高级类型的转换过程。主要包括:值类型的隐式转换,主要是数值类型等基本类型的隐式转换;引用类型的隐式转换,主要是派生类向基类的转换;值类型和引用类型的隐士转换,主要指装箱和拆箱转换。
  • 显示转换:也叫强制类型转换。但是转换过程不能保证数据的完整性,可能引起一定的精度损失或者引起不可知的异常发生。转换的格式为, 
(type)(变量、表达式)

例如:int a = (int)(b + 2.02);

  • 值类型与引用类型的装箱与拆箱是.NET中最重要的类型转换,不恰当的转换操作会引起性能的极大损耗,因此我们将以专门的主题来讨论。
  • 以is和as操作符进行类型的安全转换,详见本人拙作《第一回:恩怨情仇:is和as》。
  • System.Convert类定义了完成基本类型转换的便捷实现。
  • 除了string以外的其他类型都有Parse方法,用于将字符串类型转换为对应的基本类型;
  • 使用explicit或者implicit进行用户自定义类型转换,主要给用户提高自定义的类型转换实现方式,以实现更有目的的转换操作,转换格式为,
static 访问修饰操作符 转换修饰操作符 operator 类型(参数列表);

 例如:

public Student
{
    
//
    
    
static public explicite opertator Student(string name, int age)
    {
        
return new Student(name, age);
    }

    
//
}

其中,所有的转换都必须是static的。  

6. 结论

现在,我们从几个角度延伸了上回对值类型和引用类型的分析,正如本文开头所言,对类型的把握还有很多可以挖掘的要点,但是以偏求全的办法我认为还是可取的,尤其是在技术探求的过程中,力求面面俱到的做法并不是好事。以上的几个角度,我认为是对值类型和引用类型把握的必经之路,否则在实际的系统开发中常常会在细小的地方栽跟头,摸不着头脑。

品味类型,我们以应用为要点撬开值类型和引用类型的规矩与方圆。

品味类型,我们将以示例为导航,开动一个层面的深入分析,下回《第十回:品味类型---值类型与引用类型(下)-应用征途》我们再见。

  

参考文献

(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming

(USA)David Chappell, Understanding .NET

  

温故知新

[开篇有益]

[第一回:恩怨情仇:is和as]

[第二回:对抽象编程:接口和抽象类]

[第三回:历史纠葛:特性和属性]

[第四回:后来居上:class和struct]

[第五回:深入浅出关键字---把new说透]

[第六回:深入浅出关键字---base和this]

[第七回:品味类型---从通用类型系统开始]

[第八回:品味类型---值类型与引用类型(上)-内存有理]

©2007 Anytao.com

原创作品,转贴请注明作者和出处,留此信息。

本贴子以现状提供且没有任何担保,同时也没有授予任何权利。
This posting is provided "AS IS" with no warranties, and confers no rights.

posted @ 2007-05-28 23:13 Anytao 阅读(6276) 评论(59)  编辑 收藏 所属分类: A DotNet

  回复  引用  查看    
#1楼 2007-05-29 01:12 | Artech      
坐个沙发,看你这个系列,明显感到一篇比一篇写得好,我觉得你是一个很会归纳的人。归纳需要能力,其实一个个的知识点单独拿出来讲,觉得很简单,但是并不是每个人都能把你知道的归纳起来。

从你的文章中,看得出来你是深得Jeffery Richter的《Applied .NET Framework Programing》和《CLR via C#》的精髓呀。

不过有个建议,你的标题太大,到不如改成“你必须知道的.NET Framework”,更加贴切些(个人意见,仅供参考:))
  回复  引用    
#2楼 2007-05-29 01:23 | valentine [未注册用户]
恩,呵呵 学到了不少东西啊
  回复  引用  查看    
#3楼 2007-05-29 07:11 | 菌哥      
总结得很全面,收藏!
  回复  引用  查看    
#4楼 [楼主]2007-05-29 08:47 | Anytao      
@Artech
《Applied .NET Framework Programing》有过拜读, 《CLR via C#》确实还没有接触, 不过你的建议很好有时间一定拿来看看.
关于标题的问题, 有建议的朋友都有过这个顾虑, 不过我的想法是这个系列将继续下去, 初步的计划当然是Fraemwork部分,但是不排除其它方面的可能。当然,主要是和我的方向统一。况且,已经有了10回的基础,改变题目实在有些难以下手,望理解。
对于这个系列,我的想法其实也很简单,就是希望通过对最基础的.NET知识层面上,在一定的归纳基础上渗透我的理解和分析,然后达到更深入的理解。至少我希望在这些最基础的层面上,老瓶装新酒,做点不一样的出来。我自己也在这个过程中,逐渐认识到不足,同时领略到更多。

感谢你的支持和建议。

  回复  引用  查看    
#5楼 [楼主]2007-05-29 08:52 | Anytao      
@valentine
感谢支持。
  回复  引用  查看    
#6楼 [楼主]2007-05-29 08:52 | Anytao      
@菌哥
感谢支持。常来看看。
  回复  引用    
#7楼 2007-05-29 08:57 | teana [未注册用户]
好文。。一直关注你的整个系列。。
  回复  引用    
#8楼 2007-05-29 08:58 | blindsniper [未注册用户]
写得很好了,一直在拜读你的这个系列,收获蛮大
顺便说一句,Artech的文章也很赞呐
  回复  引用  查看    
#9楼 [楼主]2007-05-29 09:04 | Anytao      
@teana
呵呵,希望不辜负你的期望,继续努力中...
  回复  引用  查看    
#10楼 [楼主]2007-05-29 09:09 | Anytao      
@blindsniper
是的,很有同感,Artech的文章我每篇必读,作为研究和提高是很好的素材,很有深度和技巧,建议关注。
当然也感谢你的关注和继续。
  回复  引用    
#11楼 2007-05-29 09:53 | Siwei [未注册用户]
受教了!而且正如Artech所说,你的文章越来越好,如果有可能的话希望能集结成书出版,让更多的人看到你的作品。 :D
  回复  引用  查看    
#12楼 2007-05-29 09:58 | 装配脑袋      
第一个string例子的解释是不正确的,建议LZ重新审视一下……
  回复  引用  查看    
#13楼 2007-05-29 13:02 | Adrian.      
装配脑袋 说的对, 第一个例子的解释的确有问题
ChangeMsg的参数传的的确是地址
但是方法体中的
str = "blah";
(产生了一个新的String对象, 地址和Main中的那个myMsg不同)
会把ChangeMsg中的参数str地址(其实str就是个指向String对象的指针)改变了, 也就是说这个改变根本不会影响到Main中的myMsg.
如果要改变Main中myMsg地址的内容, 在参数前加个ref就哦了, 这样传进来的str参数可以理解为一个指向myMsg指针的指针.......

  回复  引用  查看    
#14楼 [楼主]2007-05-29 13:33 | Anytao      
@装配脑袋
string示例的目的,是为了说明string作为引用类型在参数传递时所表现出的值类型特性,因此下面的解释是针对这一点而展开的,不知我是表述不明还是理解有误,望指正。
  回复  引用  查看    
#15楼 2007-05-29 13:45 | 装配脑袋      
String是纯粹的引用类型,因此在进行赋值(=操作)和传递参数时是绝对不会复制一份的,只有“引用”本身会赋值和传递。任何引用类型,如果在按值传递参数后执行str = xxxx这样的操作都不会影响到原本的对象。因为“引用”本身是按值传递的。所以你有两个地方有问题
1、对按值传递的引用类型作用的误解
2、对string在传递时是否会进行复制的误解

PS。前面那个Adrian.说的很对哦,这个例子其实没有体现出string的不可变特性(而且真说明白是需要一些技巧的;)
  回复  引用  查看    
#16楼 2007-05-29 13:53 | 妖居      
一通读下来,受益非浅啊。非常感谢。
  回复  引用  查看    
#17楼 [楼主]2007-05-29 13:54 | Anytao      
@Adrian.
我想您所说的都没错,可是我发现你的观点和我对这个示例的解释是一样的,如果以ref修饰符来作用于ChangeMsg,那又如何说明引用类型在参数传递时的值类型表现呢?
不过,作为对这个问题的补充,以ref方式来传递参数正好补充了这一观点。因为,默认情况下,引用类型是以ref方式来传递的,而string类型在默认情况下表现出了不同的结果,必须以ref方式来实现才能得出修改参数的目的,因此正说明了其特殊性,不知我们的观点是否有冲突。
  回复  引用  查看    
#18楼 [楼主]2007-05-29 14:05 | Anytao      
@装配脑袋
其实,该示例的目的并非着眼于string类型的恒定性,而是string类型在参数传递时表现出的特殊性。
我想string作为引用类型,在参数传递时的理解是:
1 按地址传递,而不是值传递;
2 由于string的恒定性,每次对string的操作都相当于new了一个新的string。
以上的进一步说明,可否作为上述示例的补充:-)

  回复  引用  查看    
#19楼 2007-05-29 14:08 | 装配脑袋      
@Anytao

我认为你对ref在引用类型上的作用还是没有弄得特别清楚。引用类型默认并非以ref方式来传递,而是按值传递的,但因为他们是“引用”类型,所以传递的其实是引用而不是对象本身。所以可以在接受参数的一方使用引用改变对象本身。
然而,str = "xxx"这样的语句并不具有通过引用改变对象的作用,它是一个对引用的赋值,改变也是引用。

很重要的一点就是:ref对于引用类型和值类型的作用是相同的,都是不带ref安值传递,带ref按地址传递。只要能理解这句话,就完全理解本文第一个例子的问题了。
  回复  引用  查看    
#20楼 2007-05-29 14:10 | 装配脑袋      
String的各种操作会产生一个新的String,这句话没错。但str = "xxxx"这样的语句并不是String的操作,只是引用赋值而已,对任何引用类型都是一样的。只有str的各种成员方法才叫“string的操作”

Button b1 = New Button();
b1.Text = "A";
ChangeButton(b1); //调用完之后b1并不会发生任何变化

............

void ChangeButton(Button b)
{
  b = New Button();
  b.Text = "B";
}

您会发现,“可变”的引用类型Button用在你的例子中,和String的行为是一样的。原因就是对引用的赋值操作并不会影响到对象本身。
  回复  引用  查看    
#21楼 [楼主]2007-05-29 14:18 | Anytao      
@装配脑袋
“引用类型默认并非以ref方式来传递,而是按值传递的,但因为他们是“引用”类型,所以传递的其实是引用而不是对象本身。”其实在概念上,称作按值传递但传递的是地址,与按引用传递就是一个意思吧;-)

str="XXX"的说法,我确实理解不够,还要再看看,谢谢指点。
  回复  引用  查看    
#22楼 2007-05-29 14:23 | 装配脑袋      
@Anytao
这么说好理解一点
不带ref传递的是值,而引用类型的变量,其值就是引用,所以传递了引用。
带ref传递的是引用,所以对引用类型的变量来说,传递了引用的引用。
  回复  引用  查看    
#23楼 [楼主]2007-05-29 14:32 | Anytao      
@装配脑袋
呵呵,的确是清楚了很多,见解很有深度哈,感谢你的指点,希望以后能经常切磋。
  回复  引用  查看    
#24楼 [楼主]2007-05-29 14:45 | Anytao      
@装配脑袋
public class HelloWorld
{
private char a;

public char A
{
get { return a; }
set { a = value; }
}

}
public class TestString
{
public static void Main(string[] args)
{
HelloWorld hw = new HelloWorld();
hw.A = 'a';
ChangeHello(hw);
Console.WriteLine(hw.A);

}

public static void ChangeHello(HelloWorld hw)
{
hw.A = 'b';
Console.WriteLine(hw.A);
}
}

按照刚才的示例,我发现HelloWorld和string表现出来的结果还是有区别的??

Result:
b
b

  回复  引用  查看    
#25楼 [楼主]2007-05-29 14:50 | Anytao      
@妖居
呵呵,谢谢。
  回复  引用  查看    
#26楼 2007-05-29 15:13 | 装配脑袋      
hw.A = 'b';
Console.WriteLine(hw.A);

关键就在这里,你没有为hw赋值,而是为其成员赋值了。这是两个完全不同的概念。
str = "asdfsd"; 这句话可没有涉及到半点成员
  回复  引用  查看    
#27楼 2007-05-29 15:32 | Anytao      
@装配脑袋
问题是ChangeHello之后,已经改变了原hw的引用,从而导致输出结果的不同。
  回复  引用  查看    
#28楼 2007-05-29 15:41 | 装配脑袋      
hw和hw所引用的对象是完全不同的东西

hw = New HelloWorld();
hw = anything;

这都是改变的hw

hw.SomeMethod();
hw.A = B;

这都是改变的hw所引用的对象
  回复  引用  查看    
#29楼 2007-05-29 15:44 | Anytao      
@装配脑袋
发出回复就发现理解错了,见谅。所言甚是。
  回复  引用  查看    
#30楼 2007-05-29 16:09 | 装配脑袋      
@Anytao

共同学习,共同学习。
其实String的不可变就体现在这里,String类没有任何成员可以改变内存中的String对象实例。(然而如果你动用unsafe的话,就会发现String是可以变动的,那样和HelloWorld就更加一致了)

  回复  引用    
#31楼 2007-05-29 17:01 | blindsniper [未注册用户]
装配脑袋的这个例子好像也是有问题哦

Button b1 = New Button();
b1.Text = "A";
ChangeButton(b1); //调用完之后b1并不会发生任何变化

............

void ChangeButton(Button b)
{
  b = New Button();
  b.Text = "B";
}

调用完之后b1并不会发生任何变化的原因是在ChangeButton里头执行了
b=new Button();使得b指向了新的引用,所以修改Text的时候才不会对b1产生影响啊,如果去掉b=new Button(),是可以改变b1的Text的吧,这个跟string的不变型有区别哦
  回复  引用    
#32楼 2007-05-29 17:15 | blindsniper [未注册用户]
嗯,没法自宫...
看完后面的讨论发现我错了
  回复  引用  查看    
#33楼 2007-05-30 14:28 | Anders06      
>>>string示例的目的,是为了说明string作为引用类型在参数传递时所表现出的值类型特性

就如 "装配脑袋" 说的这里根本没有体现string的值类型特性,看上象而已.
把str = "22"; 理解成str = new String("22")来对待有利于更好的理解.

以前认为 引用类型是默认按引用传值的,所以ref加不加无所谓,,实际上两者是有区别的 " 装配脑袋 " 已经很好的说明了这一点了

有兴趣可以看看我很早以前写的string的文章
http://www.cnblogs.com/anders06/archive/2006/06/17/428227.html
可能写的不咋滴,没引起大家注意啊:(

  回复  引用    
#34楼 2007-05-30 16:12 | db661音乐站 [未注册用户]
看完后面的讨论发现我错了

  回复  引用    
#35楼 2007-05-30 16:12 | db661音乐站 [未注册用户]
所言甚是。
  回复  引用    
#36楼 2007-05-30 22:55 | AloneSword [未注册用户]
积累多学习 多学习
  回复  引用    
#37楼 2007-05-31 21:17 | yao [未注册用户]
GC(垃圾回收,Gabage Collection)控制
--
garbage

  回复  引用    
#38楼 2007-06-19 12:50 | bangbang [未注册用户]
对于没有重载过的==应该是自动调用Equals方法的。
  回复  引用    
#39楼 2007-06-26 11:13 | 林.NET [未注册用户]
有个问题想不通,本文说在不同的地方要选不同的类型,以改进程序的效率,但C#里面好像最小的单位是类,什么东西都在类里面,就算我们创建一个整型变量它也是类里面的,一样放在托管堆里,那我们还有必要区分值类型和引用类型吗,本刚学C#很多问题都不懂,希望大指点。
  回复  引用    
#40楼 2007-06-27 15:05 | linqingfeng [未注册用户]
"值类型变量不可为null值,值类型都会自行初始化为0值..."

有个问题请教楼主大大,如果把值类型定义为可空类型呢?
例如:int? a = null
  回复  引用  查看    
#41楼 [楼主]2007-07-15 22:25 | Anytao      
@Stanley.Luo
:-)
  回复  引用    
#42楼 2007-07-24 22:19 | txd_lf [未注册用户]
所以我觉得string表现出来的值类型特性,不是刻意而为之,而是设计完string类型后自然而然的一个结果
  回复  引用  查看    
#43楼 [楼主]2007-08-15 10:09 | Anytao      
@txd_lf
的确如此。
  回复  引用  查看    
#44楼 [楼主]2007-09-26 11:18 | Anytao      
@chinaifne
:-)
  回复  引用  查看    
#45楼 2008-07-10 15:23 | 非空      

--------------------------------------------------------
--引用--------------------------------------------------
Anytao: 常读常新。
--------------------------------------------------------
  回复  引用  查看    
#46楼 [楼主]2008-07-10 22:40 | Anytao      
@非空
呵呵,我又是也回头看
  回复  引用  查看    
#47楼 2008-08-23 12:19 | zsk      
string str="ss";

Test(ref str);

void Test(ref String s)//传递参数的引用的引用,s最终引用对象"ss"

{ //此时s变量值应该是str对象的地址吧

s="aa";//改变s为啥不能直接指向'aa'而是通过改变str的指向?(难道意思是改变对象的对象?)

}

s中不是存放着str变量的地址么?s="aa"; 不就是直接把str的地址换成'aa'的地址了么?

被指针弄得有点晕了,还请楼主明示,感谢
  回复  引用  查看    
#48楼 [楼主]2008-08-25 17:22 | Anytao      
@zsk
针对你的示例,我们逐行分析:

string str="ss";
//str表示指向"ss"的引用

Test(ref str);
//执行按引用传递

void Test(ref String s)
//传递参数的引用的引用,s指向str的引用,因此改变s就等于改变str,这正是“引用的引用”的体现,str表示到"ss"的引用,而s表示到"ss"的引用的引用

{

s="aa";
//改变s就等于改变str,所以str的引用发生改变,将执行"aa",而不是"ss"

}

根据新的注释,对照你原来的注释,希望能够有所帮助:-)


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-07-17 11:55 编辑过


相关链接: