C#实例化对象对象数据类型如何转化为对象类型?

数据类型转换 -- JavaScript 标准参考教程(alpha)
数据类型转换
来自,by 阮一峰
JavaScript是一种动态类型语言,变量是没有类型的,可以随时赋予任意值。但是,数据本身和各种运算是有类型的,因此运算时变量需要转换类型。大多数情况下,这种数据类型转换是自动的,但是有时也需要手动强制转换。
强制转换主要指使用Number、String和Boolean三个构造函数,手动将各种类型的值,转换成数字、字符串或者布尔值。
Number函数:强制转换成数值
使用Number函数,可以将任意类型的值转化成数字。
(1)原始类型值的转换规则
数值:转换后还是原来的值。
字符串:如果可以被解析为数值,则转换为相应的数值,否则得到NaN。空字符串转为0。
布尔值:true转成1,false转成0。
undefined:转成NaN。
null:转成0。
Number(&324&) // 324
Number(&324abc&) // NaN
Number(&&) // 0
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0
Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
parseInt('011') // 9
parseInt('42 cats') // 42
parseInt('0xcafebabe') //
Number('011') // 11
Number('42 cats') // NaN
Number('0xcafebabe') //
上面代码比较了Number函数和parseInt函数,区别主要在于parseInt逐个解析字符,而Number函数整体转换字符串的类型。另外,Number会忽略八进制的前导0,而parseInt不会。
Number函数会自动过滤一个字符串前导和后缀的空格。
Number('\t\v\r12.34\n ')
(2)对象的转换规则
对象的转换规则比较复杂。
先调用对象自身的valueOf方法,如果该方法返回原始类型的值(数值、字符串和布尔值),则直接对该值使用Number方法,不再进行后续步骤。
如果valueOf方法返回复合类型的值,再调用对象自身的toString方法,如果toString方法返回原始类型的值,则对该值使用Number方法,不再进行后续步骤。
如果toString方法返回的是复合类型的值,则报错。
Number({a:1})
上面代码等同于
if (typeof {a:1}.valueOf() === 'object'){
Number({a:1}.toString());
Number({a:1}.valueOf());
上面代码的valueOf方法返回对象本身({a:1}),所以对toString方法的返回值“[object Object]”使用Number方法,得到NaN。
如果toString方法返回的不是原始类型的值,结果就会报错。
var obj = {
valueOf: function () {
console.log(&valueOf&);
return {};
toString: function () {
console.log(&toString&);
return {};
Number(obj)
// TypeError: Cannot convert object to primitive value
上面代码的valueOf和toString方法,返回的都是对象,所以转成数值时会报错。
从上面的例子可以看出,valueOf和toString方法,都是可以自定义的。
Number({valueOf:function (){return 2;}})
Number({toString:function(){return 3;}})
Number({valueOf:function (){return 2;},toString:function(){return 3;}})
上面代码对三个对象使用Number方法。第一个对象返回valueOf方法的值,第二个对象返回toString方法的值,第三个对象表示valueOf方法先于toString方法执行。
String函数:强制转换成字符串
使用String函数,可以将任意类型的值转化成字符串。规则如下:
(1)原始类型值的转换规则
数值:转为相应的字符串。
字符串:转换后还是原来的值。
布尔值:true转为“true”,false转为“false”。
undefined:转为“undefined”。
null:转为“null”。
String(123) // &123&
String(&abc&) // &abc&
String(true) // &true&
String(undefined) // &undefined&
String(null) // &null&
(2)对象的转换规则
如果要将对象转为字符串,则是采用以下步骤。
先调用toString方法,如果toString方法返回的是原始类型的值,则对该值使用String方法,不再进行以下步骤。
如果toString方法返回的是复合类型的值,再调用valueOf方法,如果valueOf方法返回的是原始类型的值,则对该值使用String方法,不再进行以下步骤。
如果valueOf方法返回的是复合类型的值,则报错。
String方法的这种过程正好与Number方法相反。
String({a:1})
// &[object Object]&
上面代码相当于下面这样。
String({a:1}.toString())
// &[object Object]&
如果toString方法和valueOf方法,返回的都不是原始类型的值,则String方法报错。
var obj = {
valueOf: function () {
console.log(&valueOf&);
return {};
toString: function () {
console.log(&toString&);
return {};
String(obj)
// TypeError: Cannot convert object to primitive value
下面是一个自定义toString方法的例子。
String({toString:function(){return 3;}})
String({valueOf:function (){return 2;}})
// &[object Object]&
String({valueOf:function (){return 2;},toString:function(){return 3;}})
上面代码对三个对象使用String方法。第一个对象返回toString方法的值(数值3),然后对其使用String方法,得到字符串“3”;第二个对象返回的还是toString方法的值(&[object Object]&),这次直接就是字符串;第三个对象表示toString方法先于valueOf方法执行。
Boolean函数:强制转换成布尔值
使用Boolean函数,可以将任意类型的变量转为布尔值。
(1)原始类型值的转换方法
以下六个值的转化结果为false,其他的值全部为true。
''(空字符串)
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
(2)对象的转换规则
所有对象的布尔值都是true,甚至连false对应的布尔对象也是true。
Boolean(new Boolean(false))
请注意,空对象{}和空数组[]也会被转成true。
Boolean([]) // true
Boolean({}) // true
当遇到以下几种情况,JavaScript会自动转换数据类型:
不同类型的数据进行互相运算;
对非布尔值类型的数据求布尔值;
对非数值类型的数据使用一元运算符(即“+”和“-”)。
自动转换为布尔值
当JavaScript遇到预期为布尔值的地方(比如if语句的条件部分),就会将非布尔值的参数自动转换为布尔值。它的转换规则与上面的“强制转换成布尔值”的规则相同,也就是说,在预期为布尔值的地方,系统内部会自动调用Boolean方法。
因此除了以下六个值,其他都是自动转为true:
''(空字符串)
if (!undefined && !null && !0 && !NaN && !''){
console.log('true');
自动转换为字符串
当JavaScript遇到预期为字符串的地方,就会将非字符串的数据自动转为字符串,转换规则与“强制转换为字符串”相同。
字符串的自动转换,主要发生在加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。
'5' + 1 // '51'
'5' + true // &5true&
'5' + false // &5false&
'5' + {} // &5[object Object]&
'5' + [] // &5&
'5' + function (){} // &5function (){}&
'5' + undefined // &5undefined&
'5' + null // &5null&
自动转换为数值
当JavaScript遇到预期为数值的地方,就会将参数值自动转换为数值,转换规则与“强制转换为数值”相同。
除了加法运算符有可能把运算子转为字符串,其他运算符都会把两侧的运算子自动转成数值。
'5' - '2' // 3
'5' * '2' // 10
false - 1 // -1
'1' - 1
'5'*[]
false/'5' // 0
'abc'-1
上面都是二元算术运算符的例子,JavaScript的两个一元算术运算符——正号和负号——也会把运算子自动转为数值。
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0
由于自动转换有很大的不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用Boolean、Number和String方法进行显式转换。
加法运算符的类型转化
加法运算符(+)需要特别讨论,因为它可以完成两种运算(加法和字符连接),所以不仅涉及到数据类型的转换,还涉及到确定运算类型。
加法运算符的类型转换,可以分成三种情况讨论。
(1)运算子之中存在字符串
两个运算子之中,只要有一个是字符串,则另一个不管是什么类型,都会被自动转为字符串,然后执行字符串连接运算。前面的《自动转换为字符串》一节,已经举了很多例子。
(2)两个运算子都为数值或布尔值
这种情况下,执行加法运算,布尔值转为数值(true为1,false为0)。
true + 5 // 6
true + true // 2
(3)运算子之中存在对象
运算子之中存在对象(或者准确地说,存在非原始类型的值),则先调用该对象的valueOf方法。如果返回结果为原始类型的值,则运用上面两条规则;否则继续调用该对象的toString方法,对其返回值运用上面两条规则。
上面代码的运行顺序是,先调用[1,2].valueOf(),结果还是数组[1,2]本身,则继续调用[1,2].toString(),结果字符串“1,2”,所以最终结果为字符串“11,2”。
// &1[object Object]&
对象{a:1}的valueOf方法,返回的就是这个对象的本身,因此接着对它调用toString方法。({a:1}).toString()默认返回字符串&[object Object]&,所以最终结果就是字符串“1[object Object]”
有趣的是,如果更换上面代码的运算次序,就会得到不同的值。
原来此时,JavaScript引擎不将{a:1}视为对象,而是视为一个代码块,这个代码块没有返回值,所以被忽略。因此上面的代码,实际上等同于 {a:1};+1 ,所以最终结果就是1。为了避免这种情况,需要对{a:1}加上括号。
&[object Object]1&
将{a:1}放置在括号之中,由于JavaScript引擎预期括号之中是一个值,所以不把它当作代码块处理,而是当作对象处理,所以最终结果为“[object Object]1”。
1 + {valueOf:function(){return 2;}}
上面代码的valueOf方法返回数值2,所以最终结果为3。
1 + {valueOf:function(){return {};}}
// &1[object Object]&
上面代码的valueOf方法返回一个空对象,则继续调用toString方法,所以最终结果是“1[object Object]”。
1 + {valueOf:function(){return {};}, toString:function(){return 2;}}
上面代码的toString方法返回数值2(不是字符串),则最终结果就是数值3。
1 + {valueOf:function(){return {};}, toString:function(){return {};}}
// TypeError: Cannot convert object to primitive value
上面代码的toString方法返回一个空对象,JavaScript就会报错,表示无法获得原始类型的值。
四个特殊表达式
有了上面这些例子,我们再进一步来看四个特殊表达式。
(1)空数组 + 空数组
首先,对空数组调用valueOf方法,返回的是数组本身;因此再对空数组调用toString方法,生成空字符串;所以,最终结果就是空字符串。
(2)空数组 + 空对象
// &[object Object]&
这等同于空字符串与字符串“[object Object]”相加。因此,结果就是“[object Object]”。
(3)空对象 + 空数组
JavaScript引擎将空对象视为一个空的代码块,加以忽略。因此,整个表达式就变成“+ []”,等于对空数组求正值,因此结果就是0。转化过程如下:
// Number([])
// Number([].toString())
// Number(&&)
如果JavaScript不把前面的空对象视为代码块,则结果为字符串“[object Object]”。
// &[object Object]&
(4)空对象 + 空对象
JavaScript同样将第一个空对象视为一个空代码块,整个表达式就变成“+ {}”。这时,后一个空对象的ValueOf方法得到本身,再调用toSting方法,得到字符串“[object Object]”,然后再将这个字符串转成数值,得到NaN。所以,最后的结果就是NaN。转化过程如下:
// Number({})
// Number({}.toString())
// Number(&[object Object]&)
如果,第一个空对象不被JavaScript视为空代码块,就会得到“[object Object][object Object]”的结果。
// &[object Object][object Object]&
// &[object Object][object Object]&
console.log({} + {})
// &[object Object][object Object]&
var a = {} + {};
// &[object Object][object Object]&
需要指出的是,对于第三和第四种情况,Node.js的运行结果不同于浏览器环境。
// &[object Object][object Object]&
// &[object Object]&
可以看到,Node.js没有把第一个空对象视为代码块。原因是Node.js的命令行环境,内部执行机制大概是下面的样子:
eval.call(this,&(function(){return {} + {}}).call(this)&)
Node.js把命令行输入都放在eval中执行,所以不会把起首的大括号理解为空代码块加以忽略。
Axel Rauschmayer,
Axel Rauschmayer,
Benjie Gillam,
Please enable JavaScript to view the
| last modified on当前位置&&&&&&&&&关于C#数据类型之间转换的问题!
更多文章查询:&
关于C#数据类型之间转换的问题!
&&&&&&&日09:11&&来源:&&
【文章摘要】关于C#数据类型之间转换的问题!
在装箱,拆箱过程中,首先涉及到的是数据的转换。发下午几个小时,研究了C#中数据类型之间的转换涉及的一些问题,希望大家给予意见1、数据类型的类名这里讲的数据的类名指的是: Sytem.data.DbType对应的类型,我是这样理解的。类名
System中相对应的类型bool
System.Boolean (布尔型,其值为 true 或者 false)char
System.Char (字符型,占有两个字节,表示 1 个 Unicode 字符)byte
System.Byte (字节型,占 1 字节,表示 8 位正整数,范围 0 ~ 255)sbyte
System.SByte (带符号字节型,占 1 字节,表示 8 位整数,范围 -128 ~ 127)ushort
System.UInt16 (无符号短整型,占 2 字节,表示 16 位正整数,范围 0 ~ 65,535)uint
System.UInt32 (无符号整型,占 4 字节,表示 32 位正整数,范围 0 ~ 4,294,967,295)ulong
System.UInt64 (无符号长整型,占 8 字节,表示 64 位正整数,范围 0 ~ 大约 10 的 20 次方)short
System.Int16 (短整型,占 2 字节,表示 16 位整数,范围 -32,768 ~ 32,767)int
System.Int32 (整型,占 4 字节,表示 32 位整数,范围 -2,147,483,648 到 2,147,483,647)long
System.Int64 (长整型,占 8 字节,表示 64 位整数,范围大约 -(10 的 19) 次方到 10 的 19 次方)float
System.Single (单精度浮点型,占 4 个字节)double
System.Double (双精度浮点型,占 8 个字节)代码如下:using Susing System.Collections.GponentMusing System.Dusing System.Dusing System.Tusing System.Windows.Fnamespace TypeTest{
public partial class Form1 : Form
public Form1()
InitializeComponent();
private void Form1_Load(object sender, EventArgs e)
byte b = 9;
char c = 'a';
sbyte sb = 8;
short s = 8;
uint u = 6;
long l = 5;
this.textBox1.Text = "typetest";
this.textBox1.AppendText("bool -> " + bo.GetType().FullName + ""n");
this.textBox1.AppendText("byte -> " + b.GetType().FullName + ""n");
this.textBox1.AppendText("char -> " + c.GetType().FullName + ""n");
this.textBox1.AppendText("sbyte -> " + sb.GetType().FullName + ""n");
this.textBox1.AppendText("short -> " +s.GetType().FullName + ""n");
this.textBox1.AppendText("int -> " + i.GetType().FullName + ""n");
this.textBox1.AppendText("uint -> " +u.GetType().FullName + ""n");
this.textBox1.AppendText("long -> " +l.GetType().FullName + ""n");
//其实类型就不写上,自己可以真接加上去
}}结果可看到:typetestbool -> System.Booleanbyte -> System.Bytechar -> System.Charsbyte -> System.SByteshort -> System.Int16int -> System.Int32uint -> System.UInt32long -> System.Int64//说明,以后所编的代码都是写在private void Form1_Load(object sender, EventArgs e)中的2、Value Type间的转换。bool bo =
byte b = 9;
char c = 'a';
sbyte sb = 8;
short s = 8;
int i = 7;
uint u = 6;
long l = 5;
this.textBox1.Text = "datatype";
this.textBox1.AppendText("bool bo=" + bo.ToString() + ""n");
this.textBox1.AppendText("byte b= " + b.ToString() + ""n");
this.textBox1.AppendText("char c= " + c.ToString() + ""n");
this.textBox1.AppendText("sbyte sb= " + sb.ToString() + ""n");
this.textBox1.AppendText("short s= " + s.ToString() + ""n");
this.textBox1.AppendText("int i= " + i.ToString() + ""n");
this.textBox1.AppendText("uint u=" + u.ToString() + ""n");
this.textBox1.AppendText("long l= " + l.ToString() + ""n");此段代码并没有转换数据类型,只说明它们的类型公别还是System.bool型…System.long型。追加一行:int g = 1;short h =this.textBox1.AppendText("h = " + h.ToString() + ""n");结果编译报错:  G:"Projects"Visual C#"Convert"Form1.cs(118): 无法将类型“int”隐式转换为“short”数据要进行强制转换。如上例修改如下:short g = 1;byte h = (byte) // 将 short 型的 g 的值强制转换成byte型后再赋给变量 hthis.textBox1.AppendText("h = " + h.ToString() + ""n");就可以了Short->byte
short g = 265; //265 = 255 + 10byte h = (byte)this.textBox1.AppendText("h = " + h.ToString() + ""n");注意:溢出问题3、ASCIIUnicodechar ch = 'a';
short ii = 65;
this.textBox1.Text = "";
this.textBox1.AppendText("The ASCII code of "'" + ch + ""' is: " + (short)ch + ""n");
this.textBox1.AppendText("ASCII is " + ii.ToString() + ", the char is: " + (char)ii + ""n");
char name1 = '屈';
char name2 = '志';
short name3 = 21195;
this.textBox1.AppendText("The Unicode of "'" + name1 + ""' is: " + (short)name1 + ""n");
this.textBox1.AppendText("The Unicode of "'" + name2 + ""' is: " + (short)name2+ ""n");
this.textBox1.AppendText("Unicode is " + name3.ToString() + ", the name3 is: " + (char)name3 + ""n");  它的运行结果是The ASCII code of 'a' is: 97ASCII is 65, the char is: AThe Unicode of '屈' is: 23624The Unicode of '志' is: 24535Unicode is 21195, the name3 is: 勋4、intstringfloat f = 12.3f;
string str = "258";
this.textBox1.Text = "";
this.textBox1.AppendText("f = " + f.ToString() + ""n");//float->string
if (int.Parse(str) == 258) //string->int
this.textBox1.AppendText("str convert to int successfully.");
this.textBox1.AppendText("str convert to int failed.");5、Stringchar[]string str = "quzhixun";
char[] chars = str.ToCharArray();//string->char[]
this.textBox1.Text = "";
this.textBox1.AppendText("Length of ""quzhixun"" is " + str.Length + ""n");
this.textBox1.AppendText("Length of char array is " + chars.Length + ""n");
this.textBox1.AppendText("char[2] = " + chars[2] + ""n");char[] name = { 'q', 'u', 'z', 'h', 'i', 'x', 'u','n' };
string sname = new String(name);//char[]->string
this.textBox1.AppendText("sname = """ + sname + """"n");6、tringbyte[]string s = "hi,屈志勋";
byte[] b1 = System.Text.Encoding.Default.GetBytes(s);//sting->byte[],半个英文1个字节,汉字2 个字节。
byte[] b2 = System.Text.Encoding.Unicode.GetBytes(s); //sting->byte[],都是两个字节。
string t1 = "", t2 = "";
foreach (byte b in b1)
t1 += b.ToString("") + " ";
foreach (byte b in b2)
t2 += b.ToString("") + " ";
this.textBox1.Text = "";
this.textBox1.AppendText("b1.Length = " + b1.Length + ""n");
this.textBox1.AppendText(t1 + ""n");
this.textBox1.AppendText("b2.Length = " + b2.Length + ""n");
this.textBox1.AppendText(t2 + ""n");//byte[] b = { 65, 66, 67 };
string s = System.Text.Encoding.ASCII.GetString(b);//byte[]->string
this.textBox1.AppendText("The string is: " + s + ""n");//7、转换十六进制int a = 159357;
this.textBox1.Text = "";
this.textBox1.AppendText("a(10) = " + a.ToString() + ""n");
this.textBox1.AppendText("a(16) = " + a.ToString("x6") + ""n");
this.textBox1.AppendText("a(16) = " + a.ToString("X6") + ""n");8、DateTimelongdouble doubleDate = DateTime.Now.ToOADate();//按原来的double值输出,DateTime->long
DateTime theDate = DateTime.FromOADate(doubleDate);//从原来的的double值获得System.DateTime对象,long->DateTime
this.textBox1.Text = "";
this.textBox1.AppendText("Double value of now: " + doubleDate.ToString() + ""n");
this.textBox1.AppendText("DateTime from double value: " + theDate.ToString() + ""n");//9、form DateTimeDateTime now = DateTime.N
this.textBox1.Text = "";
format = """year"":yyyy,""month"":MM,""day"":dd HH:mm:ss";
this.textBox1.AppendText(format + ": " + now.ToString(format) + ""n");
format = "yy年M日d日";
this.textBox1.AppendText(format + ": " + now.ToString(format)
责任编辑:彭凡
&关于&编程 开发&相关报道
&硅谷动力最新热点
loading...
频道热文排行
经典软件下载
往期精彩文章
网站合作、内容监督、商务咨询、投诉建议:010-
Copyright © 2000--
硅谷动力公司版权所有 京ICP证000088号C# 语言的类型划分为两大类:值类型 (Value type) 和引用类型 (reference type)。值类型和引用类型都可以为泛型类型 (generic type),泛型类型采用一个或多个类型参数。类型参数可以指定值类型和引用类型。
value-type
reference-type
type-parameter
第三种类型是指针,只能用在不安全代码中。第 18.2 节对此做了进一步的探讨。
值类型与引用类型的不同之处在于:值类型的变量直接包含其数据,而引用类型的变量存储对其数据的引用 (reference),后者称为对象 (object)。对于引用类型,两个变量可能引用同一个对象,因此对一个变量的操作可能影响另一个变量所引用的对象。对于值类型,每个变量都有自己的数据副本,对一个变量的操作不可能影响另一个变量。
C# 的类型系统是统一的,因此任何类型的值都可以按对象处理。C# 中的每个类型直接或间接地从 object 类类型派生,而 object 是所有类型的最终基类。引用类型的值都被视为 object 类型,被简单地当作对象来处理。值类型的值则通过对其执行装箱和拆箱操作(第
4.3 节)按对象处理。
一个值类型或是结构类型,或是枚举类型。C# 提供称为简单类型 (simple type) 的预定义结构类型集。简单类型通过保留字标识。
value-type:
struct-type
struct-type:
simple-type
nullable-type
simple-type:
numeric-type
numeric-type:
integral-type
floating-point-type
integral-type:
floating-point-type:
nullable-type:
non-nullable-value-type&& ?
non-nullable-value-type:
enum-type:
与引用类型的变量不同的是,仅当该值类型是可以为 null 的类型时,值类型的变量才可包含 null 值。 对于每个不可以为 null 的值类型,都存在一个对应的可以为 null 的值类型,该类型表示相同的值集加上 null 值。
对值类型变量赋值时,会创建所赋的值的一个副本。这不同于引用类型的变量赋值,引用类型的变量赋值复制的是引用而不是由引用标识的对象。
所有值类型从类 System.ValueType 隐式继承,后者又从类 object 继承。任何类型都不可能从值类型派生,因此,所有值类型都是隐式密封的(第
10.1.1.2 节)。
注意,System.ValueType 本身不是 value-type, 而是 class-type,所有 value-type 都从它自动派生。
所有值类型都隐式声明一个称为默认构造函数 (default constructor) 的公共无参数实例构造函数。默认构造函数返回一个零初始化实例,它就是该值类型的默认值 (default value):
对于所有 simple-types,默认值是由所有位都置零的位模式产生的值:
对于 sbyte、byte、byte、ushort、int、uint、long 和 ulong,默认值为 0。
对于 char,默认值为 '\x0000'。
对于 float,默认值为 0.0f。
对于 double,默认值为 0.0d。
对于 decimal,默认值为 0.0m。
对于 bool,默认值为 false。
对于 enum-type E,默认值为 0,该值被转换为类型 E。
对于 struct-type,默认值是通过将所有值类型字段设置为它们的默认值并将所有引用类型字段设置为 null 而产生的值。
对于 nullable-type,默认值是一个其 HasValue 属性为 false 且 Value 属性未定义的实例。默认值也称为可以为 null
的类型的 null 值 (null value)。
与任何其他实例构造函数一样,值类型的默认构造函数也是用 new 运算符调用的。出于效率原因,实际上,不必故意调用它的构造函数。在下面的示例中,变量 i 和 j 都被初始化为零。
void F() {
&&&& int i = 0;
&&&& int j = new int();
由于每个值类型都隐式地具有一个公共无形参实例构造函数,因此,一个结构类型中不可能包含一个关于无形参构造函数的显式声明。但允许结构类型声明参数化实例构造函数(第
11.3.8 节)。
结构类型是一种值类型,它可以声明常量、字段、方法、属性、索引器、运算符、实例构造函数、静态构造函数和嵌套类型。结构类型的声明在第 11.1 节中说明。
C# 提供称为简单类型 (simple type) 的预定义结构类型集。简单类型通过保留字标识,而这些保留字只是 System 命名空间中预定义结构类型的别名,详见下表。
化名的类型
System.SByte
System.Byte
System.Int16
System.UInt16
System.Int32
System.UInt32
System.Int64
System.UInt64
System.Char
System.Single
System.Double
System.Boolean
System.Decimal
由于简单类型是结构类型的别名,所以每个简单类型都具有成员。例如,int 具有在 System.Int32 中声明的成员以及从
System.Object 继承的成员,允许使用下面的语句:
int i = int.MaxV&&&&&&& // System.Int32.MaxValue constant
string s = i.ToString();&&&& // System.Int32.ToString()
instance method
string t = 123.ToString();&&&&&& // System.Int32.ToString()
instance method
简单类型与其他结构类型的不同之处在于,简单类型允许某些附加的操作:
大多数简单类型允许通过编写 literals(第 2.4.4 节)来创建值。例如,123 是类型 int 的文本,'a' 是类型 char 的文本。C# 没有普遍地为结构类型设置类似的以文本创建值的规则,所以其他结构类型的非默认值最终总是通过这些结构类型的实例构造函数来创建的。
当表达式的操作数都是简单类型常量时,编译器可以在编译时计算表达式。这样的表达式称为 constant-expression(第
节)。涉及其他结构类型所定义的运算符的表达式不被视为常量表达式。
通过 const 声明可以声明简单类型(第 10.4 节)的常量。常量不可能属于其他结构类型,但 static readonly 字段提供了类似的效果。
涉及简单类型的转换可以参与由其他结构类型定义的转换运算符的计算,但用户定义的转换运算符永远不能参与其他用户定义运算符的计算(第 6.4.3 节)。
C# 支持 9 种整型:sbyte、byte、short、ushort、int、uint、long、ulong 和 char。整型具有以下所列的大小和取值范围:
sbyte 类型表示有符号 8 位整数,其值介于 -128 和 127 之间。
byte 类型表示无符号 8 位整数,其值介于 0 和 255 之间。
short 类型表示有符号 16 位整数,其值介于 -32768 和 32767 之间。
ushort 类型表示无符号 16 位整数,其值介于 0 和 65535 之间。
int 类型表示有符号 32 位整数,其值介于 - 和
uint 类型表示无符号 32 位整数,其值介于 0 和
long 类型表示有符号 64 位整数,其值介于 -4775808 和 4775807 之间。
ulong 类型表示无符号 64 位整数,其值介于 0 和
char 类型表示无符号 16 位整数,其值介于 0 和 65535 之间。char 类型的可能值集与 Unicode 字符集相对应。虽然 char 的表示形式与 ushort 相同,但是可以对一种类型进行的所有计算并非都可以对另一种类型执行。
整型一元运算符和二元运算符总是对有符号 32 位精度、无符号的 32 位精度、有符号 64 位精度或无符号 64 位精度进行计算:
对于一元运算符 + 和 ~,操作数转换为 T 类型,其中 T 是 int、uint、long 和 ulong 中第一个可以完全表示操作数的所有可能值的类型。然后用 T 类型的精度执行运算,结果的类型是 T 类型。
对于一元运算符 &,操作数转换为类型 T,其中 T 是 int 和 long 中第一个可以完全表示操作数的所有可能值的类型。然后用 T 类型的精度执行运算,结果的类型是 T 类型。一元运算符 & 不能应用于类型 ulong 的操作数。
对于 +、&、*、/、%、&、^、|、==、!=、&、&、&= 和 &= 二元运算符,操作数转换为类型 T,其中 T 是 int、uint、long 和 ulong 中第一个可以完全表示两个操作数的所有可能值的类型。然后用 T 类型的精度执行运算,运算的结果的类型也属于 T(对于关系运算符为 bool)。对于二元运算符,不允许一个操作数为 long 类型而另一个操作数为 ulong 类型。
对于二元运算符 && 和 &&,左操作数转换为 T 类型,其中 T 是 int、uint、long 和 ulong 中第一个可以完全表示操作数的所有可能值的类型。然后用 T 类型的精度执行运算,结果的类型是 T 类型。
char 类型归类为整型类型,但它在以下两个方面不同于其他整型:
不存在从其他类型到 char 类型的隐式转换。具体而言,即使 sbyte、byte 和 ushort 类型具有完全可以用 char 类型来表示的值范围,也不存在从 sbyte、byte 或 ushort 到 char 的隐式转换。
char 类型的常量必须写成 character-literal 或带有强制转换为类型 char 的 integer-literal。例如,(char)10 与 '\x000A' 是相同的。
checked 和 unchecked 运算符和语句用于控制整型算术运算和转换(第 7.6.12 节)的溢出检查。在 checked 上下文中,溢出产生编译时错误或导致引发 System.OverflowException。在 unchecked 上下文中将忽略溢出,任何与目标类型不匹配的高序位都被放弃。
C# 支持两种浮点型:float 和 double。float 和 double 类型用 32 位单精度和 64 位双精度 IEEE 754
格式来表示,这些格式提供以下几组值:
正零和负零。大多数情况下,正零和负零的行为与简单的值零相同,但某些运算会区别对待此两种零(第 7.8.2 节)。
正无穷大和负无穷大。无穷大是由非零数字被零除这样的运算产生的。例如,1.0 / 0.0 产生正无穷大,而 &1.0 / 0.0 产生负无穷大。
非数字 (Not-a-Number) 值,常缩写为 NaN。NaN 是由无效的浮点运算(如零被零除)产生的。
以 s & m & 2e
形式表示的非零值的有限集,其中 s 为 1 或 &1,m 和 e 由特殊的浮点类型确定:对于 float,为 0 & m & 224 并且 &149 & e & 104;对于 double,为 0 & m & 253 并且 &1075 & e & 970。非标准化的浮点数被视为有效非零值。
float 类型可表示精度为 7 位、在大约 1.5 & 10&45 到 3.4 & 1038 的范围内的值。
double 类型可表示精度为 15 位或 16 位、在大约 5.0 & 10&324 到 1.7 & 10308 的范围内的值。
如果二元运算符的一个操作数为浮点型,则另一个操作数必须为整型或浮点型,并且运算按下面这样计算:
如果一个操作数为整型,则该操作数转换为与另一个操作数的类型相同的浮点型。
然后,如果任一操作数的类型为 double,则另一个操作数转换为 double。至少用 double 范围和精度执行运算,结果的类型为 double(对于关系运算符则为 bool)。
否则,至少用 float 范围和精度执行运算,结果的类型为 float(对于关系运算符则为 bool)。
浮点运算符(包括赋值运算符)从来不产生异常。相反,在异常情况下,浮点运算产生零、无穷大或 NaN,如下所述:
如果浮点运算的结果对于目标格式太小,则运算结果变成正零或负零。
如果浮点运算的结果对于目标格式太大,则运算结果变成正无穷大或负无穷大。
如果浮点运算无效,则运算的结果变成 NaN。
如果浮点运算的一个或两个操作数为 NaN,则运算的结果变成 NaN。
可以用比运算的结果类型更高的精度来执行浮点运算。例如,某些硬件结构支持比 double 类型具有更大的范围和精度的&extended&或&long double&浮点型,并隐式地使用这种更高精度类型执行所有浮点运算。只有性能开销过大,才能使这样的硬件结构用&较低&的精度执行浮点运算。C# 采取的是允许将更高的精度类型用于所有浮点运算,而不是强求执行规定的精度,造成同时损失性能和精度。除了传递更精确的结果外,这样做很少会产生任何可察觉的效果。但是,在 x * y / z 形式的表达式中,如果其中的乘法会产生超出 double 范围的结果,而后面的除法使临时结果返回到 double 范围内,则以更大范围的格式去计算该表达式,可能会产生有限值的结果(本来应是无穷大)。
decimal 类型是 128 位的数据类型,适合用于财务计算和货币计算。decimal 类型可以表示具有 28 或 29 个有效数字、从 1.0 & 10&28 到大约 7.9 & 1028 范围内的值。
decimal 类型的有限值集的形式为 (&1)s & c & 10-e,其中符号 s 是 0 或 1,系数 c 由 0 & c & 296 给定,小数位数 e 满足 0 & e &
28。decimal 类型不支持有符号的零、无穷大或 NaN。decimal 可用一个以 10 的幂表示的 96 位整数来表示。对于绝对值小于 1.0m 的 decimal,它的值最多精确到第 28 位小数。对于绝对值大于或等于 1.0m 的 decimal,它的值精确到小数点后第 28 或 29 位。与 float 和 double 数据类型相反,十进制小数数字(如 0.1)可以精确地用 decimal 表示形式来表示。在
float 和 double 表示形式中,这类数字通常变成无限小数,使这些表示形式更容易发生舍入错误。
如果二元运算符的一个操作数为
decimal 类型,则另一个操作数必须为整型或 decimal 类型。如果存在一个整型操作数,它将在执行运算前转换为 decimal。
decimal 类型值的运算结果是这样得出的:先计算一个精确结果(按每个运算符的定义保留小数位数),然后舍入以适合表示形式。结果舍入到最接近的可表示值,当结果同样地接近于两个可表示值时,舍入到最小有效位数位置中为偶数的值(这称为&银行家舍入法&)。零结果总是包含符号 0 和小数位数 0。
如果十进制算术运算产生一个绝对值小于或等于 5 & 10-29 的值,则运算结果变为零。如果 decimal 算术运算产生的值对于
decimal 格式太大,则将引发 System.OverflowException。
与浮点型相比,decimal 类型具有较高的精度,但取值范围较小。因此,从浮点型到 decimal 的转换可能会产生溢出异常,而从 decimal 到浮点型的转换则可能导致精度损失。由于这些原因,在浮点型和 decimal 之间不存在隐式转换,如果没有显式地标出强制转换,就不可能在同一表达式中同时使用浮点操作数和 decimal 操作数。
bool 类型表示布尔逻辑量。bool 类型的可能值为
true 和 false。
在 bool 和其他类型之间不存在标准转换。具体而言,bool 类型与整型截然不同,不能用 bool
值代替整数值,反之亦然。
在 C 和 C++
语言中,零整数或浮点值或 null 指针可以转换为布尔值 false,非零整数或浮点值或非 null 指针可以转换为布尔值 true。在 C# 中,这种转换是通过显式地将整数或浮点值与零进行比较,或者显式地将对象引用与 null 进行比较来完成的。
枚举类型是具有命名常量的独特的类型。每个枚举类型都有一个基础类型,该基础类型必须为 byte、sbyte、short、ushort、int、uint、long 或 ulong。枚举类型的值集和它的基础类型的值集相同。枚举类型的值并不只限于那些命名常量的值。枚举类型是通过枚举声明(第
14.1 节)定义的。
可以为 null 的类型可以表示其基础类型 (underlying type) 的所有值和一个额外的 null
值。可以为
null 的类型写作 T?,其中 T 是基础类型。此语法是
System.Nullable&T& 的简写形式,这两种形式可以互换使用。
相反,不可以为 null 的值类型 (non-nullable value type) 可以是除 System.Nullable&T&
及其简写形式T?(对于任何类型的 T)之外的任何值类型,加上约束为不可以为 null 的值类型的任何类型参数(即具有 struct
约束的任何类型参数)。System.Nullable&T& 类型指定
的值类型约束(第 10.1.5 节),这意味着可以为 null 的类型的基础类型可以是任何不可以为 null 的值类型。可以为 null 的类型的基础类型不能是可以为 null 的类型或引用类型。例如,int?? 和 string? 是无效类型。
可以为 null 的类型 T? 的实例有两个公共只读属性:
类型为 bool 的 HasValue 属性
类型为 T 的 Value 属性
HasValue 为 true 的实例称为非 null。非 null 实例包含一个已知值,可通过 Value 返回该值。
HasValue 为 false 的实例称为 null。null 实例有一个不确定的值。尝试读取 null 实例的 Value 将导致引发 System.InvalidOperationException。访问可以为 null 的实例的 Value 属性的过程称作解包 (unwrapping)。
除了默认构造函数之外,每个可以为 null 的类型 T?
都有一个具有类型为 T 的单个实参的公共构造函数。例如,给定一个类型为 T 的值 x,调用形如
的构造函数将创建
null 实例,其 Value 属性为 x。为一个给定值创建可以为 null
的类型的非 null 实例的过程称作包装 (wrapping)。
从 null 文本转换为 T?(第 6.1.5 节)以及从 T 转换为 T?(第 6.1.4 节)可使用隐式转换。
引用类型是类类型、接口类型、数组类型或委托类型。
reference-type:
class-type
interface-type
array-type
delegate-type
class-type:
interface-type:
array-type:
non-array-type&& rank-specifiers
non-array-type:
rank-specifiers:
rank-specifier
rank-specifiers&& rank-specifier
rank-specifier:
[&& dim-separatorsopt&&
&dim-separators:
dim-separators&& ,
delegate-type:
引用类型值是对该类型的某个实例 (instance) 的一个引用,后者称为对象 (object)。null 值比较特别,它兼容于所有引用类型,用来表示&没有被引用的实例&。
类类型定义包含数据成员、函数成员和嵌套类型的数据结构,其中数据成员包括常量和字段,函数成员包括方法、属性、事件、索引器、运算符、实例构造函数、析构函数和静态构造函数。类类型支持继承,继承是派生类可用来扩展和专门化基类的一种机制。类类型的实例是用 object-creation-expressions(第 7.6.10.1 节)创建的。
有关类类型的介绍详见第 10 章。
某些预定义类类型在 C# 语言中有特殊含义,如下表所示。
System.Object
所有其他类型的最终基类。请参见第 4.2.2 节。
System.String
C# 语言的字符串类型。请参见第 4.2.4 节。
System.ValueType
所有值类型的基类。请参见第 4.1.1 节。
System.Enum
所有枚举类型的基类。请参见第 14 章。
System.Array
所有数组类型的基类。请参见第 12 章。
System.Delegate
所有委托类型的基类。请参见第 15 章。
System.Exception
所有异常类型的基类。请参见第 16 章。
object 类类型是所有其他类型的最终基类。C# 中的每种类型都是直接或间接从 object 类类型派生的。
关键字 object 只是预定义类 System.Object 的别名。
dynamic 类型与 object 一样,可以引用任何对象。在将运算符应用于 dynamic 类型的表达式时,其解析会推迟到程序运行时进行。因此,如果运算符不能合法地应用于引用的对象,在编译过程中不会报告任何错误。而是在运行时解析运算符失败时,会引发异常。
在第 4.7 节中进一步介绍了动态类型,在第 7.2.2 节中进一步介绍了动态绑定。
string 类型是直接从 object 继承的密封类类型。string 类的实例表示 Unicode 字符串。
string 类型的值可以写为字符串(第
2.4.4.5 节)。
关键字 string 只是预定义类 System.String 的别名。
一个接口定义一个协定。实现某接口的类或结构必须遵守该接口定义的协定。一个接口可以从多个基接口继承,而一个类或结构可以实现多个接口。
有关接口类型的介绍详见第 13 章。
数组是一种数据结构,它包含可通过计算索引访问的零个或更多个变量。数组中包含的变量(又称数组的元素)具有相同的类型,该类型称为数组的元素类型。
有关数组类型的介绍详见第 12 章。
委托是引用一个或多个方法的数据结构。对于实例方法,委托还可引用实例方法对应的对象实例。
在 C 或 C++
中与委托最接近的是函数指针,但函数指针只能引用静态函数,而委托则既可以引用静态方法,也可以引用实例方法。在后一种情况中,委托不仅存储了一个对该方法入口点的引用,还存储了一个对相应的对象实例的引用,该方法就是通过此对象实例被调用的。
有关委托类型的介绍详见第 15 章。
装箱和拆箱的概念是 C# 的类型系统的核心。它在 value-types 和 reference-types 之间架起了一座桥梁,使得任何 value-type 的值都可以转换为 object 类型的值,反过来转换也可以。装箱和拆箱使我们能够统一地来考察类型系统,其中任何类型的值最终都可以按对象处理。
装箱转换允许将 value-type 隐式转换为 reference-type。存在下列装箱转换:
从任何 value-type 到 object 类型。
从任何 value-type 到 System.ValueType 类型。
从任何 non-nullable-value-type 到 value-type
实现的任何 interface-type。
从任何 nullable-type
nullable-type 的基础类型实现的任何 interface-type。
从任何 enum-type 到 System.Enum 类型。
从任何具有基础 enum-type 的 nullable-type 到 System.Enum 类型。
请注意,对类型形参进行隐式转换将以装箱转换的形式执行(如果在运行时它最后从值类型转换到引用类型(第 6.1.10 节))。
将 non-nullable-value-type 的一个值装箱包括以下操作:分配一个对象实例,然后将 non-nullable-value-type 的值复制到该实例中。
对 nullable-type 的值装箱时,如果该值为 null 值(HasValue 为 false),将产生一个 null 引用;否则将产生对基础值解包和装箱的结果。
最能说明 non-nullable-value-type 的值的实际装箱过程的办法是,设想有一个泛型装箱类 (boxing
class),其行为与下面声明的类相似:
sealed class Box&T&: System.ValueType
public Box(T
&&&& value =
T 类型值 v 的装箱过程现在包括执行表达式 new Box&T&(v) 和将结果实例作为 object 类型的值返回。因此,下面的语句
int i = 123;
object box =
在概念上相当于
int i = 123;
object box = new Box&int&(i);
实际上,像上面这样的 Box&T&装箱类并不存在,并且装箱值的动态类型也不会真的属于一个类类型。相反,T 类型的装箱值属于动态类型 T,若用 is 运算符来检查动态类型,也仅能引用类型 T。例如,
int i = 123;
object box =
if (box is int) {
Console.Write("Box contains an int");
将在控制台上输出字符串&Box contains an int&。
装箱转换隐含着复制一份 待装箱的值。这不同于从 reference-type 到 object 类型的转换,在后一种转换中,转换后的值继续引用同一实例,只是将它当作派生程度较小的 object 类型而已。例如,给定下面的声明
struct Point
public int x,
public Point(int x, int y) {
&&&& this.x =
&&&& this.y =
则下面的语句
Point p = new Point(10, 10);
object box =
Console.Write(((Point)box).x);
将在控制台上输出值 10,因为将 p 赋值给 box 是一个隐式装箱操作,它将复制 p 的值。如果将 Point 声明为 class,由于 p 和 box 将引用同一个实例,因此输出值为 20。
取消装箱转换允许将 reference-type 显式转换为 value-type。存在以下拆箱转换:
从 object 类型到任何 value-type。
从 System.ValueType 类型到任何 value-type。
从任何 interface-type 到实现了该 interface-type 的任何 non-nullable-value-type。
从任何 interface-type 到其基础类型实现了该 interface-type 的任何 nullable-type。
从 System.Enum 类型到任何 enum-type。
从 System.Enum 类型到任何具有基础 enum-type 的 nullable-type。
请注意,到类型形参的显式转换将以取消装箱转换的形式执行(如果在运行时它结束从引用类型到值类型(第 6.2.6 节)的转换)。
对 non-nullable-value-type 取消装箱的操作包括下列步骤:首先检查对象实例是否是给定 non-nullable-value-type 的装箱值,然后将该值从实例中复制出来。
对 nullable-type 取消装箱在源操作数为 null
时会产生 nullable-type 的 null 值;否则将产生从对象实例到 nullable-type 的基础类型的取消装箱的包装结果。
参照前一节中关于假想的装箱类的描述,从对象 box 到 value-type T 的取消装箱转换包括执行表达式 ((Box&T&)box).value。因此,下面的语句
object box =
int i = (int)
在概念上相当于
object box =
new Box&int&(123);
int i = ((Box&int&)box).
为使针对给定 non-nullable-value-type 的取消装箱转换在运行时取得成功,源操作数的值必须是对该 non-nullable-value-type 的装箱值的引用。如果源操作数为 null,则将引发 System.NullReferenceException。如果源操作数是对不兼容对象的引用,则将引发 System.InvalidCastException。
为使针对给定 nullable-type
的取消装箱转换在运行时取得成功,源操作数的值必须是
null 或是对该 nullable-type 的基础 non-nullable-value-type 的装箱值的引用。如果源操作数是对不兼容对象的引用,则将引发 System.InvalidCastException。
泛型类型声明本身表示未绑定的泛型类型 (unbound generic type),它通过应用类型实参 (type argument) 被用作构成许多不同类型的&蓝图&。类型实参编写在紧跟在泛型类型的名称后面的尖括号(& 和 &)中。至少包括一个类型实参的类型称为构造类型 (constructed type)。构造类型可以在语言中能够出现类型名的大多数地方使用。未绑定的泛型类型只能在 typeof-expression(第 7.6.11 节)中使用。
构造类型还可以在表达式中用作简单名称(第 7.6.2 节)或在访问成员时使用(第
7.6.4 节)。
在计算 namespace-or-type-name 时,仅考虑具有正确数目的类型形参的泛型类型。因此,可以使用同一个标识符标识不同的类型,前提是那些类型具有不同数目的类型形参。当在同一程序中混合使用泛型和非泛型类时,这是很有用的:
namespace Widgets
class Queue {...}
class Queue&TElement& {...}
namespace MyApplication
&&&& Queue q1;&&&&&&&& // Non-generic Widgets.Queue
&&&& Queue&int& q2;&&&&&& // Generic Widgets.Queue
即使未直接指定类型形参,type-name 也可以标识构造类型。当某个类型嵌套在泛型类声明中,并且包含该类型的声明的实例类型被隐式用于名称查找(第
10.3.8.6 节)时,就会出现这种情况:
class Outer&T&
public class Inner {...}
public Inner
i;&&&&&&&&&&&& // Type of i is Outer&T&.Inner
在不安全代码中,构造类型不能用作 unmanaged-type(第 18.2
类型实参列表中的每个实参都只是一个 type。
type-argument-list:
&&& type-arguments&& &
type-arguments:
type-argument
type-arguments&& ,&& type-argument
type-argument:
在不安全代码(第 18 章)中,type-argument 不可以是指针类型。每个类型实参都必须满足对应的类型形参上的所有约束(第
10.1.5 节)。
所有类型都可归类为开放类型 (open type) 或封闭类型 (closed type)。开放类型是包含类型形参的类型。更明确地说:
类型形参定义开放类型。
当且仅当数组元素类型是开放类型时,该数组类型才是开放类型。
当且仅当构造类型的一个或多个类型实参为开放类型时,该构造类型才是开放类型。当且仅当构造的嵌套类型的一个或多个类型实参或其包含类型的类型实参为开放类型时,该构造的嵌套类型才是开放类型。
封闭类型是不属于开放类型的类型。
在运行时,泛型类型声明中的所有代码都在一个封闭构造类型的上下文中执行,这个封闭构造类型是通过将类型实参应用该泛型声明来创建的。泛型类型中的每个类型形参都绑定到特定的运行时类型。所有语句和表达式的运行时处理都始终使用封闭类型,开放类型仅出现在编译时处理过程中。
每个封闭构造类型都有自己的静态变量集,任何其他封闭构造类型都不会共享这些变量。由于开放类型在运行时并不存在,因此不存在与开放类型关联的静态变量。如果两个封闭构造类型是从相同的未绑定泛型类型构造的,并且它们的对应类型实参属于相同类型,则这两个封闭构造类型是相同类型。
术语未绑定类型 (unbound type) 是指非泛型类型或未绑定的泛型类型。术语绑定类型 (bound type) 是指非泛型类型或构造类型。
未绑定类型是指类型声明所声明的实体。未绑定泛型类型本身不是一种类型,不能用作变量、参数或返回值的类型,也不能用作基类型。可以引用未绑定泛型类型的唯一构造是
表达式(第 7.6.11 节)。
每当引用构造类型或泛型方法时,都会根据泛型类型或方法(第 10.1.5 节)上声明的类型形参约束对所提供的类型实参进行检查。对于每个 where 子句,将根据每个约束检查与命名的类型形参相对应的类型实参 A,如下所示:
如果约束为类类型、接口类型或类型形参,则假设 C 表示该约束,并用所提供的类型实参替换出现在该约束中的任何类型形参。若要满足该约束,必须可通过下列方式之一将类型 A 转换为类型 C:
标识转换(第 6.1.1 节)
隐式引用转换(第 6.1.6 节)
装箱转换(第 6.1.7 节)& 前提是类型 A 为不可以为 null 的值类型。
从类型形参 A 到 C 的隐式引用、装箱或类型形参转换。
如果约束为引用类型约束 (class) 则类型 A 必须满足下列条件之一:
为接口类型、类类型、委托类型或数组类型。注意,System.ValueType 和 System.Enum 是满足此约束的引用类型。
A 是已知为引用类型的类型形参(第 10.1.5 节)。
如果约束为值类型约束 (struct) 则类型 A 必须满足下列条件之一:
A 为结构类型或枚举类型,但不是可以为 null 的类型。请注意,System.ValueType 和 System.Enum 是不满足此约束的引用类型。
A 为具有值类型约束的类型形参(第 10.1.5 节)。
如果约束为构造函数约束 new(),则类型 A 一定不能为 abstract,并且必须具有公共无形参构造函数。如果下列条件之一成立,则满足此条件:
A 为值类型,因为所有值类型都具有公共默认构造函数(第 4.1.2 节)。
A 为具有构造函数约束的类型形参(第 10.1.5 节)。
A 为具有值类型约束的类型形参(第 10.1.5 节)。
A 是不为 abstract 并且包含显式声明的无参数 public 构造函数的类。
A 不为 abstract,并且具有默认构造函数(第 10.11.4 节)。
如果给定的类型实参未满足一个或多个类型形参的约束,则会发生编译时错误。
由于类型形参未被继承,因此约束也从不被继承。在下面的示例中,T 需要指定其类型形参
T 上的约束,以便 T 满足基类 B&T& 所施加的约束。相反,类 E 不需要指定约束,因为对于任何 T,List&T& 都实现 IEnumerable。
class B&T&
where T: IEnumerable {...}
class D&T&: B&T& where T: IEnumerable
class E&T&: B&List&T&& {...}
类型形参是指定形参在运行时要绑定到的值类型或引用类型的标识符。
type-parameter:
identifier
由于类型形参可使用许多不同的实际类型实参进行实例化,因此类型形参具有与其他类型稍微不同的操作和限制。这包括:
不能直接使用类型形参声明基类(第 10.2.4 节)或接口(第 13.1.3 节)。
类型形参上的成员查找规则取决于应用到该类型形参的约束(如果有)。这将在第 7.4
节中详细描述。
类型形参的可用转换取决于应用到该类型形参的约束(如果有)。这将在第 6.1.10 和 6.2.6 节中详细描述。
如果事先不知道由类型形参给出的类型是引用类型(第 6.1.10 节),不能将标识 null 转换为该类型。不过,可以改为使用 default 表达式(第 7.6.13 节)。此外,具有由类型形参给出的类型的值可以 使用 == 和 != 与 null 进行比较(第 7.10.6 节),除非该类型形参具有值类型约束。
仅当类型形参受 constructor-constraint 或值类型约束(第 7.6.10.1 节)的约束时,才能将 new 表达式(第 10.1.5 节)与类型形参联合使用。
不能在特性中的任何位置上使用类型形参。
不能在成员访问(第 7.6.4 节)或类型名称(第 3.8 节)中使用类型形参标识静态成员或嵌套类型。
在不安全代码中,类型形参不能用作 unmanaged-type(第 18.2 节)。
作为类型,类型形参纯粹是一个编译时构造。在运行时,每个类型形参都绑定到一个运行时类型,运行时类型是通过向泛型类型声明提供类型实参来指定的。因此,使用类型形参声明的变量的类型在运行时将是封闭构造类型(第
4.4.2 节)。涉及类型形参的所有语句和表达式的运行时执行都使用作为该形参的类型实参提供的实际类型。
表达式树 (Expression tree) 允许匿名函数表示为数据结构而不是可执行代码。表达式树是 System.Linq.Expressions.Expression&D& 形式的表达式树类型 (expression tree
type) 的值,其中 D 是任何委托类型。对于本规范的其余部分,我们将使用简写形式 Expression&D& 引用这些类型。
如果存在从匿名函数到委托类型 D 的转换,则也存在到表达式树类型 Expression&D& 的转换。不过,匿名函数到委托类型的转换会生成一个引用该匿名函数的可执行代码的委托,而到表达式树类型的转换则会创建该匿名函数的表达式树表示形式。
表达式树是匿名函数有效的内存数据表示形式,它使匿名函数的结构变得透明和明晰。
与委托类型 D 一样,Expression&D& 具有与 D 相同的参数和返回类型。
下面的示例将匿名函数表示为可执行代码和表达式树。因为存在到 Func&int,int& 的转换,所以也存在到 Expression&Func&int,int&& 的转换:
Func&int,int&
del = x =& x + 1;&&&&&&&&&&&&&&&&&& // Code
Expression&Func&int,int&& exp = x =&
x + 1;&&&&&& // Data
进行上面的赋值之后,委托
引用返回 x
的方法,表达式目录树 exp
引用描述表达式 x =& x + 1 的数据结构。
泛型类型 Expression&D& 的准确定义以及当将匿名函数转换为表达式树类型时用于构造表达式树的确切规则不在本规范的范围之内,将另作说明。
有两个要点需要明确指出:
并非所有匿名函数都能表示为表达式树。例如,具有语句体的匿名函数和包含赋值表达式的匿名函数就不能表示为表达式树。在这些情况下,转换仍存在,但在编译时将失败。
Expression&D& 提供一个实例方法 Compile,该方法产生一个类型为 D 的委托:
Func&int,int& del2 = pile();
调用此委托将导致执行表达式树所表示的代码。因此,根据上面的定义,del 和 del2
等效,而且下面的两个语句也将等效:
int i1 = del(1);
int i2 = del2(1);
&&&&&&&&&&&&& 执行此代码后,i1 和 i2
的值都为 2。
dynamic 类型在 C# 中具有特殊含义。其用途在于允许进行动态绑定(在第 7.2.2 节中进行了详细介绍)。
dynamic 被视为与 object 相同,除了以下这些方面:
对 dynamic 类型的表达式进行的运算可以动态绑定(第 7.2.2 节)。
类型推断(第 7.5.2 节)在 dynamic 和 object 都是候选项时,会优先考虑前者。
由于此等效性,因此存在以下情况:
object 与 dynamic 之间,以及对于在将 dynamic 替换为 object 时相同的构造类型之间,存在隐式标识转换
与 object 之间的隐式和显式转换也适用于 dynamic。
在将 dynamic 替换为 object 时相同的方法签名视为是相同的签名
dynamic 类型在运行时与 object 没有区别。
dynamic 类型的表达式称为动态表达式 (dynamic expression)。
阅读(...) 评论() &}

我要回帖

更多关于 实例化对象 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信