Free Hit Counters
http://www.dellasdeals.com

海天小阁

一个生于70年代的IT人,高不成低不就,与君共勉

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  65 随笔 :: 0 文章 :: 793 评论 :: 18 引用

引子:

TomiWong 园友发了一个帖子 有多少人能分得清值类型和引用类型?

问题:“class、object、string、int、bool、byte、interface、delegate、struct、enum,以上这些哪些是值类型,哪些是引用类型?”

回帖无数,但是我发现多数还是不明真相的群众。少数几个其实已经感觉到了真相,但是没有表达清楚。因此特此撰文,以正视听。

 

貌似正确的答案

如果要快速给一个一般性而言正确的答案,那么应该如下:

值类型: int, bool, byte, struct, enum

引用类型: class, object, string, interface, delegate

可是问题真那么简单么?难道很多回帖的兄弟都是胡闹?

核心问题

对问题里面的英文理解有歧义

题目应该试图问的,是些关键字定义并声明出来的变量分别是值类型还是引用类型

看看几个有代表性的意见:

============================

#4楼2010-12-03 17:14 | xiaotie     
interface 和其它的不一样吧

============================

#14楼2010-12-03 17:26 | Ivony...     
在这些东西里面:
object、string、int、bool、byte
是类型

class、interface、delegate、struct、enum
不是类型

只能说使用这些关键字定义的类型是引用类型还是值类型

============================

#18楼2010-12-03 17:30 | LeonSun     
我就想问一个问题,class是什么类型?object是什么类型?

本来这个题对名词的定义就是模糊的,恐怕出这个题的人概念也是模糊的

别来误导群众了,真的~~~

============================

好了,意见还是不少的。让我们从头开始梳理问题出在哪里?

1、区分int类型和int类型变量

2、定义TomiWong问题中提到的int是指int型变量还是指int关键字?

3、然后我们才可以开始回答问题

我们平时说惯了,说int是值类型。教科书式的回答应该类似是:

int类型的变量是值类型变量

由此,我们推导出一个简化的说法

int类型是值类型

不少同学在interface、class、object几个关键字上一下子转不过弯来。的确,这几个东西平时不考虑的。我们只说对象类型的变量是引用类型变量,因此看到class的时候第一反应就是他不能算是对象,类不是对象这个是天条。呵呵

这里就出现一个双重标准的问题:

把问题中的所有东西看作他对应的变量,还是就是看做这个关键字,或者看做其代表的类型

class的中文翻译是类,请问类是什么类型?应该什么都不是

如果都看作是关键字,则这些东西也都什么类型都不是

只有当我们统一把他们都看作是变量,是用这些关键字定义并声明出来的变量的时候,才能心平气和地去回答问题

标准答案

C# 类型体系包含下列几种类别:

值类型、引用类型、指针类型

值类型的变量存储数据,而引用类型的变量存储对实际数据的引用。引用类型也称为对象。指针类型仅可用于 unsafe 模式(往极端上说,指针也是值类型)。

通过装箱和取消装箱,可以将值类型转换为引用类型,然后再转换回值类型。除了装箱值类型外,无法将引用类型转换为值类型。

值类型也可以为空(可空类型如:int?),这意味着它们能存储法非值状态。

值类型主要由两类组成:

结构、枚举

结构分为以下几类:

         Numeric(数值)类型

               整型、浮点型、decimal

               整型包括:sbyte, byte, char, short, ushort, int, unit, long, ulong

               浮点型包括:float, double

         bool

         用户定义的结构。

引用类型的变量又称为对象,可存储对实际数据的引用。

class、interface、delegate

内置引用类型:object、string

其实这答案都不是我写的

参考文献

类型(C#)     值类型(C#)     引用类型(C#)

我们都需要抽空看看  C#编程指南

posted on 2010-12-04 21:50 徐少侠 阅读(2544) 评论(38) 编辑 收藏

评论

#1楼 2010-12-04 22:02 Alexis      
本来有些模糊的概念,读了本文又清晰起来了
 回复 引用 查看   

#2楼 2010-12-04 22:04 xiaotie      
我的观点在那篇文章的后面回复已经纠正了,以前不知道值类型转换为接口的装箱问题。
 回复 引用 查看   

#3楼 2010-12-04 22:09 Alexis      
@xiaotie
我以前没怎么想过底层中堆啊、栈的问题~~~
 回复 引用 查看   

#4楼 2010-12-04 22:16 xiaotie      
引用Alexis:
@xiaotie
我以前没怎么想过底层中堆啊、栈的问题~~~

一般也用不上。为了压榨性能,才会大量的用 stackalloc 和 AllocHGlobal。
 回复 引用 查看   

#5楼 2010-12-04 22:21 Alexis      
@xiaotie
这些东西C#里面也会用到么,怎么感觉像C、C++里面的东西
 回复 引用 查看   

#6楼 2010-12-04 22:31 xiaotie      
@Alexis
既然提供了,需要时就用吧。用了就和C差不多了。但比C好用太多太多了。不过上面那两种,都无法用在class上,这是最郁闷的。
 回复 引用 查看   

#7楼 2010-12-04 22:53 LeonSun      
说的还是有点儿偏颇吧,个人观点如下

1..net体系中貌似没有指针类型,即使有指针类型,也应该与值类型和引用类型不在同一层次上
2.可空值类型是引用类型
3.我之前说的此题概念不清,不清之处在于如使用interface关键字,定义出的东西(不好意思,没想到用什么更确切的词儿形容),他是引用类型么?值类型么?或者是,实现interface定义出的东西的类型是值类型还是引用类型?再或者是,使用实现interface定义出的东西的类型的变量时引用类型还是值类型?越说越像绕口令了是不是?

总的来讲,我还是之前的观点,这个题出的本身就是有问题,并且也看不出这个题到底是在考什么知识点,倒不如考个堆啊栈啊来的实在。
 回复 引用 查看   

#8楼 2010-12-04 22:57 LeonSun      
3、然后我们才可以开始回答问题

我们平时说惯了,说int是引用类型。教科书式的回答应该类似是:


加粗部分是不是应该改成“说int是值类型”
 回复 引用 查看   

#9楼 2010-12-04 23:03 Tony Zhou      
书上都有,不知道的看下书,15分钟就行了
 回复 引用 查看   

#10楼 2010-12-04 23:08 xiaotie      
说的还是有点儿偏颇吧,个人观点如下
1..net体系中貌似没有指针类型,即使有指针类型,也应该与值类型和引用类型不在同一层次上
>> 有
2.可空值类型是引用类型
>> Nullable<(Of <(T>)>) 是值类型
3.我之前说的此题概念不清,不清之处在于如使用interface关键字,定义出的东西(不好意思,没想到用什么更确切的词儿形容),他是引用类型么?值类型么?或者是,实现interface定义出的东西的类型是值类型还是引用类型?再或者是,使用实现interface定义出的东西的类型的变量时引用类型还是值类型?越说越像绕口令了是不是?
>> interface 是引用类型,测试代码:

public interface IPerson
{
int id { get; set; }
}

public struct Person : IPerson
{
public int id { get; set; }
}

class Program
{
static void Main(string[] args)
{
IPerson p = new Person();
p.id = 3;
ChangePersonId(p);
Console.WriteLine(((Person)p).id);
Console.WriteLine(p.id);
Console.ReadLine();
}

private static void ChangePersonId(IPerson p)
{
p.id = 5;
}
}

总的来讲,我还是之前的观点,这个题出的本身就是有问题,并且也看不出这个题到底是在考什么知识点,倒不如考个堆啊栈啊来的实在。
>> 那个题出的应该没啥问题。越是基本的问题越难答。就像,什么是“空气”,人们花了很多年才知道。什么是“企业”?到现在还没有一个标准的答案。
 回复 引用 查看   

#11楼 2010-12-04 23:37 Jeff Wong      
@xiaotie
没错,我也认为不必拘泥于名词解释,知道各个类型是如何进行内存分配的更能看出一个人的认知水平。反正我不认为他的原题出得有多高明。
 回复 引用 查看   

#12楼 2010-12-04 23:42 xiaotie      
@Jeff Wong
如果考内存分配,考不啥结果的。.Net内存分配水也比较深。
 回复 引用 查看   

#13楼 2010-12-04 23:45 菜田小鸟      
好像有懂得什么 但晕得更多
 回复 引用 查看   

#14楼 2010-12-05 00:00 hoodlum1980      
我认为 interface 不应该放在这个问题里面。interface 是什么类型,这个问题感觉很不严谨。
 回复 引用 查看   

#15楼 2010-12-05 00:05 hoodlum1980      
interface本身不是一个具体存在,所以我觉得不能这样问。你用interface声明一个对象,但是在语义上你不能说这个对象是该interface,而是实现了该interface(定义的时候继承)的某个类的实例。这和其他声明不一样,你用一个class声明一个对象,最终你都可以在语义上说,这个对象是该class。
 回复 引用 查看   

#16楼 2010-12-05 00:08 hoodlum1980      
纠缠什么关键字完全没有意义,因为每个人都知道你想问的是什么。class是引用类型,struct是值类型,string是引用类型,但是带有某些值类型的操作特征。这些基本都是MSDN上明确找得到的描述。
 回复 引用 查看   

#17楼 2010-12-05 00:09 yxjyxj      
既然说到了值类型和引用类型,那我想问一下,它们之间的转换是否是可逆的?
 回复 引用 查看   

#18楼 2010-12-05 00:42 xiaotie      
@hoodlum1980
从语义角度上来说interface和引用类型完全是两码事,但是微软在实现上脑残的把interface当作引用类型来实现,这个搞清楚还是很有必要的。比如说,我经常用一个没有成员的struct来实现一个interface,然后new一个struct,然后当接口来用,主要用于泛型编程中。以前一直以为整个这个struct会是在栈上,不会去骚扰托管堆,现在看来错了,微软会脑残的把这个struct装箱,然后再去调用。还就在这几天,我正准备让我的图像库中的那些struct来实现接口,然后通过接口调用,实现更高的抽象。幸亏没有这样干。不然的话,随便一个稍微大点的图像操作,就需要百万、千万次的装箱拆箱。
 回复 引用 查看   

#19楼 2010-12-05 01:18 sheng.chao      
引用xiaotie:
@hoodlum1980
从语义角度上来说interface和引用类型完全是两码事,但是微软在实现上脑残的把interface当作引用类型来实现,这个搞清楚还是很有必要的。比如说,我经常用一个没有成员的struct来实现一个interface,然后new一个struct,然后当接口来用,主要用于泛型编程中。以前一直以为整个这个struct会是在栈上,不会去骚扰托管堆,现在看来错了,微软会脑残的把这个struct装箱,然后再去调用。还就在这几天,我正准备让我的图像库中的那些struct来实现接口,然后通过接口调用,实现更高的抽象。幸亏没有这样干。不然的话,随便一个稍微大点的图像操作,就需要百万、千万次的装箱拆箱。


你确信脑残的是微软而不是....
 回复 引用 查看   

#20楼 2010-12-05 08:28 卡通一下      
引用xiaotie:
@hoodlum1980
从语义角度上来说interface和引用类型完全是两码事,但是微软在实现上脑残的把interface当作引用类型来实现,...

虽然我不大懂,但您的这个说法是否也有些武断?呵呵!
 回复 引用 查看   

#21楼 2010-12-05 08:56 gbb21      
引用xiaotie:
@hoodlum1980
从语义角度上来说interface和引用类型完全是两码事,但是微软在实现上脑残的把interface当作引用类型来实现,这个搞清楚还是很有必要的。比如说,我经常用一个没有成员的struct来实现一个interface,然后new一个struct,然后当接口来用,主要用于泛型编程中。以前一直以为整个这个struct会是在栈上,不会去骚扰托管堆,现在看来错了,微软会脑残的把这个struct装箱,然后再去调用。还就在这几天,我正准备让我的图像库中的那些struct来实现接口,然后通过接口调用,实现更高的抽象。幸亏没有这样干。不然的话,随便一个稍微大点的图像操作,就需要百万、千万次的装箱拆箱。


You know what: all functions in "interface" are virtual function... However, struct / value type do NOT allow these kind of virtual function....

If you want to do so to achieve high level abstraction, please use generic type instead of using interface.....


 回复 引用 查看   

#22楼 2010-12-05 09:31 xiaotie      
@gbb21
但是 struct 是不会被继承的,无妨碍 interface 的语义。也就是说,.Net 完全可以被实现得 struct 可以不被装箱就可转换为 interface。没这样做的原因猜测是.Net没有在 struct 上花心血。struct 至少可以做以下改进:
(1)interface 对 struct 做特殊处理;
(2)泛型支持指针;
(3)这个是最脑残的,也就是 [StructLayout(LayoutKind.Explicit)] 的问题,比如:
[StructLayout(LayoutKind.Explicit)]
public struct Rgb24
{
[FieldOffset(0)]
public Byte Blue;
[FieldOffset(1)]
public Byte Green;
[FieldOffset(2)]
public Byte Red;
[FieldOffset(3)]
public Byte Pad;
[FieldOffset(0)]
public Int32 Value;

public Rgb24(byte red, byte green, byte blue, Int32 pad)
{
Red = red;
Green = green;
Blue = blue;
Pad = pad;
}
}
这个编译器会报错的。必须把构造函数写成这样才行:
public Rgb24(byte red, byte green, byte blue, byte pad, Int32 value)
{
Red = red;
Green = green;
Blue = blue;
Pad = pad;
Value = value;
}

 回复 引用 查看   

#23楼 2010-12-05 09:52 cath      
文字游戏真的没意义
 回复 引用 查看   

#24楼 2010-12-05 10:21 xiaotie      
类型是一个抽象概念。类是OO上的概念,类型是编程语言的概念。讲类,讲OO的书,扫一眼就能明白,那些讲类型的书,第一章之后,俺就从没看明白过。这是在网上搜到的一段话:“类型(type)以及类型系统的起源以及研究与发展是独立于OOP的。早在五十年代的FORTRAN语言编译器实现中,就已经采用类型系统作为类型检查的一种手段。广义的类型一般被定义为一种约束,也就是一种逻辑公式。” struct 是类型,class 是类型,interface 是类型这种说法应该是没问题的。.net 为它们添加了不同的约束。然后就是值类型和引用类型的区分了,.net 对值类型和引用类型进行了不同的约束,比如说引用类型就没法用指针指,而值类型就可以,比如:
public enum m
{
a,
}
...
unsafe
{
m m = m.a;
m* mm = &m;
}
这样是没问题的。普通struct也可以这样用。但是class和interface就不能这样用。还有struct转换为interface会进行装箱,装箱执行的正是从值类型->引用类型的转换操作。至于interface为什么会是引用类型,这完完全全是实现层面的权衡问题,就比如说实现List容器,你可以实现成先分配s大小的空间,不够用了再分配2倍大的空间,把原来的复制过去,也可以是先分配一个s大小的空间,不够了再挂一个2倍大的空间。既然微软这样实现interface,也只能将它当引用类型用了。
 回复 引用 查看   

#25楼 2010-12-05 11:42 Alan@Net      
文字游戏。。。
 回复 引用 查看   

#26楼[楼主] 2010-12-05 17:11 徐少侠      
引用LeonSun:
3、然后我们才可以开始回答问题

我们平时说惯了,说int是引用类型。教科书式的回答应该类似是:


加粗部分是不是应该改成“说int是值类型”

呵呵,糗大了
立刻去改
 回复 引用 查看   

#27楼[楼主] 2010-12-05 17:14 徐少侠      
引用yxjyxj:既然说到了值类型和引用类型,那我想问一下,它们之间的转换是否是可逆的?

参考MSDN,我文章最后的那个 C#类型 链接
通过装箱和取消装箱,可以将值类型转换为引用类型,然后再转换回值类型。除了装箱值类型外,无法将引用类型转换为值类型。
 回复 引用 查看   

#28楼[楼主] 2010-12-05 17:27 徐少侠      
引用hoodlum1980:interface本身不是一个具体存在,所以我觉得不能这样问。你用interface声明一个对象,但是在语义上你不能说这个对象是该interface,而是实现了该interface(定义的时候继承)的某个类的实例。这和其他声明不一样,你用一个class声明一个对象,最终你都可以在语义上说,这个对象是该class。

我觉得我想表达的意思是说属于接口类型的变量

定义接口
Interface IExample
{
void Method();
}

实现接口
Class Class1: IExample
{
public void Method()
{
Console.WriteLine("Hello World");
}
}

声明接口变量
statuc void Main()
{
IExample IClass;

IClass=new Class1;
}

在这里 IClass是一个接口类型的变量,这个变量是引用类型变量。
更具体的可以参考前面 XiaoTie的代码。
一个引用类型 的接口类型变量引用了一个值类型结构型实例
通过函数形参传递后,实际的结构体只有一份

 回复 引用 查看   

#29楼 2010-12-05 18:37 花祭果凛      
确实很有帮助
 回复 引用 查看   

#30楼 2010-12-05 20:40 LeonSun      
@xiaotie
说的还是有点儿偏颇吧,个人观点如下
1..net体系中貌似没有指针类型,即使有指针类型,也应该与值类型和引用类型不在同一层次上
>> 有
>>> 其实我表达的是有问题,我知道有指针类型和安全指针类型,但是重点在于,所谓的指针类型是值类型,所以我说与值类型和引用类型不在同一层次上,属于包含和包含于的关系

2.可空值类型是引用类型
>> Nullable<(Of <(T>)>) 是值类型
>>> 这个确实是我错了

3. 我之前说的此题概念不清,不清之处在于如使用interface关键字,定义出的东西(不好意思,没想到用什么更确切的词儿形容),他是引用类型么?值类型么?或者是,实现interface定义出的东西的类型是值类型还是引用类型?再或者是,使用实现interface定义出的东西的类型的变量时引用类型还是值类型?越说越像绕口令了是不是?
>> interface 是引用类型,测试代码:

public interface IPerson
{
int id { get; set; }
}

public struct Person : IPerson
{
public int id { get; set; }
}

class Program
{
static void Main(string[] args)
{
IPerson p = new Person();
p.id = 3;
ChangePersonId(p);
Console.WriteLine(((Person)p).id);
Console.WriteLine(p.id);
Console.ReadLine();
}

private static void ChangePersonId(IPerson p)
{
p.id = 5;
}
}

总的来讲,我还是之前的观点,这个题出的本身就是有问题,并且也看不出这个题到底是在考什么知识点,倒不如考个堆啊栈啊来的实在。
>> 那个题出的应该没啥问题。越是基本的问题越难答。就像,什么是“空气”,人们花了很多年才知道。什么是“企业”?到现在还没有一个标准的答案。
>>> 那个啥,你还是重读一遍我那个绕口令吧,你这段代码只是其中一句的情况
 回复 引用 查看   

#31楼 2010-12-05 20:50 xiaotie      
@LeonSun
你如果这样说的话,那么几乎每句话都是有疑义的了。而在日常生活中,我们只需要结合背景推测说话者的原意即可。不然,交流就只剩下语言游戏了。
 回复 引用 查看   

#32楼 2010-12-05 23:28 Ivony...      
这里面的概念的确是很含糊不清的,标准答案实在是言过其实了。

其实把指针扯出来问题就已经不好解答了,指针到底算值类型还是引用类型,判定依据是什么?行为?

当然,还有绝大多人都难以解答的ValueType到底是不是值类型的问题。

事实上,.NET Framework里面的类型还远不止这些。

单单是可以用Type实例表示的类型就有很多种,如指针类型、按引用传递参数类型、数组类型、泛型类型参数、泛型类型参数约束。。。。这还不包括Type实例都不能表示,但也在上下文可以被理解为类型的东西,例如dynamic。
 回复 引用 查看   

#33楼 2010-12-06 10:10 bengxia      
这是玩脑筋急转弯么?
 回复 引用 查看   

#34楼[楼主] 2010-12-06 12:31 徐少侠      
参与了、围观了
技术问题搞明白了
剩下的大家就权当娱乐吧
 回复 引用 查看   

#35楼 2010-12-06 13:47 undefined      
居然有人会问接口是什么类型的。。。
太好笑了。

接口如果可以new, 那么我们可以讨论讨论他的类型。

问题是他能new吗?
 回复 引用 查看   

#36楼 2010-12-06 22:50 xiaotie      
@undefined
类型的概念比类要早几十年啊
 回复 引用 查看   

#37楼[楼主] 2010-12-07 09:57 徐少侠      
引用undefined:
居然有人会问接口是什么类型的。。。
太好笑了。

接口如果可以new, 那么我们可以讨论讨论他的类型。

问题是他能new吗?

其实问的是接口类型的变量
通过大家的努力,已经确认接口类型的变量是引用类型

具体可以自己运行代码以及好好体会了。

 回复 引用 查看   

#38楼 2010-12-10 10:41 十二月的雪      
知道了..感谢.
 回复 引用 查看