类型系统的特征简介

几乎每种编程语言都有某种形式的一个类型系统。类型系统大致被分为:强/弱,安全/不安全,静态/动态,显式/隐式等类型。

c#在类型系统世界中的位置

c#1的类型系统是静态的、显式的和安全的

静态类型和动态类型

c#是静态类型的:每个变量都有一个特定的类型,而且该类型在编译时是已知的。只有该类型已知的的操作才被允许。

例子:object o="hello";Console.WriteLine(o.Length);//报错

object o="hello";Console.WriteLine(o.ToString().Length);//5

与静态类型对应的是动态类型,动态类型的的实质是变量中含有值,但那些值并不限定于特定的类型,所以编译器不能执行相同形式的检查。相反,执行环境试图采取一种合适的方式理解引用值的给定表达式。动态类型需要在执行代码时来动态检查类型。

例子(项目中例子部分代码):dynamic d = JObject.Parse(actionModel.jsonEntity);

Term term=new Term();

term.BeginTime = d.BeginTime;
            term.Enabled = d.Enabled;
            term.EndTime = d.EndTime;
            term.TermName = d.TermName;
            term.TermType = d.TermType;

显式类型和隐式类型

显式类型必须在声明中显式的声明。隐式类型则允许编译器根据变量的用途来推断变量的类型。

类型安全和类型不安全

描述类型安全系统的最简单方法就是描述它的对立面。有的语言允许做一些非常不正当的操作。在恰当的时候功能很强大,但是如果使用不当就会搬起石头砸自己的脚。滥用类型系统就属于这种情况。

c#是安全类型系统。在编译时,如果编译器发现这种转换实际是不可行的,就会触发一个编译时错误。另外如果理论上允许,但在执行时发现不正确,CLR也会抛出一个异常。

c#1的类型系统在什么时候不够用

集合强和弱

.net1.1内建了以下三种类型:

  • 数组 强类型 内建到语言和运行时中
  • System.Collections 命名空间中的弱类型集合
  • System.Collections.Specialized命名空间中的强类型集合

数组是强类型的。所以在编译时,不可能将string[]的一个元素设置成一个FileStream。然而,引用类型的数组也支持协变,只要元素的类型之间允许这样的转换,就能隐式将一种数组类型转换成另一种类型。执行时会进行检查,以确保类型有误的引用不会被存储下来。

例子:string[] strings=new string[5];

object[] objects=strings;

objects[0]=new button();

运行代码,会抛出ArrayTypeMismatchException 异常。这是由于从string[]转换成object[]会返回原始引用-无论strings或者objects都引用同一个数组。数组本身知道他是一个字符串数组,所以会拒绝存储非字符串的引用。数组协变有时候会派上用场,但代价是一些类型安全性在执行时才能实现,而不能在编译时实现。

让我们把它和弱类型的集合如ArrayList和HashTable的情况对比。这些集合的API定义键和值的类型都是object.例如,写一个ArrayList方法时,没有办法保证在编译时,调用者会传入一个字符串列表。可以把这个要求写入文档。只要将列表中的每个元素都强制转换为string,运行时类型安全性就会帮你强制使这个限制生效。但是,这样不会获得编译时的类型安全性。同样,如果返回一个ArrayList,可以再文档中指出他只包含字符串。但是调用者必须相信你说的是实话,而且在访问列表元素时必须插入强制类型转换。

最后看看强类型集合,如StringCollection.这些集合提供了一个强类型的API。所以,如果接受一个StringCollection作为参数或者返回值,可以肯定它只包含string.另外在取回集合元素时,不需要进行强制类型转换。这听起来似乎很理想,但有两个问题。首先,它实现了IList,所以仍然可以为它添加非字符串的对象,虽然运行时会失败。其次,它只能处理字符串。还有一些专门集合,但它们包括的范围不是很大。例如CollectionBase类型,可以用它创建你自己的强类型集合,但是那意味着要为每种元素类型都创建一个新集合,所以同样不理想。

缺乏协变返回类型

ICloneable 是框架中最简单的接口之一。它只有一个Clone方法,该方法返回调用方法的那个对象的一个副本。暂时不讨论这应该是一个深复制还是一个浅复制,先看看Clone方法的签名:object clone()

这是一个非常简单的签名,就像刚才所说,改方法应返回调用方法的那个对象的一个副本。这意味着他需要返回同类型的一个对象或至少兼容类型的一个对象。用一个覆盖方法的签名更准确地描述该方法实际返回的东西,应该是讲得通的。例如,在Person类中,像下面这样实现ICloneable接口是不错的选择:

public Person Clone();

这应该破坏不了任何东西,代码期待的旧的对象仍然能够正常工作。这个特性称为返回类型的协变。但遗憾的是,接口实现和方法覆盖不支持这一特性。对于接口来说,正常的解决方法是使用显式接口实现来获得预期结果。

Public Person Clone()

{

[Implemention goes here]

}

object ICloeeable.Clone()

{

return Clone();

}

这样以来,任何代码为一个表达式调用Clone时,如果编译器知道这个表达式的类型是Person ,就会调用顶部方法;如果表达式只是ICloneable,就会调用底部方法。这样虽然可行,但是太别扭了。参数也存在类似问题。假定一个接口方法或者一个虚拟方法,其签名是void process(string x),那么在实现或者覆盖这个方法时,使用一个放宽类限制的签名应该是合乎逻辑的,例如void process(object x).这称为参数类型的逆变性。但是和返回类型协变性一样,参数类型的逆变性也是不支持的。那么如何解决?对于接口,解决方案是一样的,同样是进行显示接口的实现。对于虚方法,解决方法则是进行普通的方法重载。虽然不是大问题,但着实令人烦恼。

拓展:深复制和浅复制

浅复制

可以使用受保护的方法System.Object.MemberwiseClone()进行浅复制。

public class Cloner{

pubilc int var;

public Cloner(int newVal)

{

var =newVal;

}

public object GetCopy()

{

return MemberwiseClone();

}

}

使用浅复制的问题

假定有引用类型的字段,而不是值类型的字段:

public class Content{

pubic int val;

}

public  class Cloner{

public Content MyContent=new Content();

public Cloner(int newVal)

{

MyContent.val=newVal;

}

public object GetCopy()
{

return MemberwiseClone();

}

}

此时通过GetCopy()方法得到的浅复制包括一个字段,它引用的对象与源对象相同。下面的代码使用这个Cloner类来说明浅复制引用类型的结构。

Cloner MySource=new Cloner(5);

Cloner MyTarget=(Cloner)MySource.GetCopy();

Console.WriteLine("MyTarget.MyContent.val={0}",MyTarget.MyContent.Val);

MySource.MyContent.val=2;

Console.WriteLine("MyTarget.MyContent.val={0}",MyTarget.MyContent.Val);

上述结果如下:

MyTarget.MyContent.val=5;

MyTarget.MyContent.val=2;

为了解决这个问题需要执行深复制

修改上面的类

public class Content{

pubic int val;

}

public class Cloner:ICloneable{

public Content MyContent=new Content();

public Cloner(int newVal)

{

MyContent.val=newVal;

}

public object clone()
{

Cloneer clonedCloner=new Cloner(MyContent.Val);

return clonedCloner;

}

}

上述结果如下:

MyTarget.MyContent.val=5;

MyTarget.MyContent.val=5;

注:整理自《深入理解c#》,《c#入门经典》

c#1所搭建的核心基础之类型系统的特征的更多相关文章

  1. C#复习笔记(2)--C#1所搭建的核心基础

    通过对C#1所搭建的核心基础的深入了解,可以知道之后的C#版本在C#1的基础上做了很多扩展,而这些扩展都是基于C#搭建的核心基础而来的. 委托 一.编写委托的过程 委托经常和C语言的“函数指针”挂钩. ...

  2. c#1所搭建的核心基础之值类型和引用类型

    这个主题很重要,在.NET中做的一切其实都是在和一个值类型或者引用类型打交道. 现实世界中的值和引用 假定你在读一份非常棒的东西,希望一个朋友也去读他.于是你到复印室里复印了一份.这个时候他获得了属于 ...

  3. c#1所搭建的核心基础之委托

    本文将对c#1的委托进行详细探索 委托(delegate)   注 delegate:vt.委派代表; 授权给; [法律]债务转移;  委托作用:在恰当的时间执行一系列操作 1.简单委托的构成 声明委 ...

  4. C#1所搭建的核心基础

    一,委托 委托封装了包含特殊返回类型和一组参数的行为,类似包含单一方法接口. 委托类型声明中所描述的类型签名决定了哪个方法可以用于创建委托实例,同时决定了调用的签名:委托类型实际上只是参数类型的一个列 ...

  5. Androd核心基础01

    Androd核心基础01包含的主要内容如下 Android版本简介 Android体系结构 JVM和DVM的区别 常见adb命令操作 Android工程目录结构 点击事件的四种形式 电话拨号器Demo ...

  6. css核心基础总结篇

    今日这篇是整合前面的css补充知识的. 我觉得前面的关于css的知识补充进去有点乱,今日整理整理一下. 层叠样式表 层叠是什么意思?为什么这个词如此重要,以至于要出现在它的名称里. 层叠可以简单地理解 ...

  7. Android应用的核心基础

    Android4开发入门经典 之 第二部分:Android应用的核心基础 Android应用中的组件 Application Components Android应用中最主要的组件是: 1:Activ ...

  8. C#核心基础--类(2)

    C#核心基础--类的声明 类是使用关键字 class 声明的,如下面的示例所示: 访问修饰符 class 类名 { //类成员: // Methods, properties, fields, eve ...

  9. (1) css的核心基础

     css的核心基础 1.css的基本语法在具体使用css之前,请各位兄弟姐妹先思考一个生活中的问题,一般情况下我们是如何描述一个人的呢? 小明{ 民族:汉族: 性格:温柔: 性别:男: 体重:68kg ...

随机推荐

  1. AFNetworking 保存Cookie Session 和 Webview 共享Cookie

    1, 请求并携带SessionId, 代码如下: //当请求成共后调用如下代码, 保存Cookie NSArray *cookies = [[NSHTTPCookieStorage sharedHTT ...

  2. CodeForces 294B Shaass and Bookshelf 【规律 & 模拟】或【Dp】

    这道题目的意思就是排两排书,下面这排只能竖着放,上面这排可以平着放,使得宽度最小 根据题意可以得出一个结论,放上这排书的Width 肯定会遵照从小到大的顺序放上去的 Because the total ...

  3. 平衡二叉树算法实现 c语言版 插入 删除

    #include <stdio.h>#include <malloc.h>#include<stdlib.h> #define EQ(a,b) ((a)==(b)) ...

  4. python 拼写检查代码(怎样写一个拼写检查器)

    原文:http://norvig.com/spell-correct.html 翻译:http://blog.youxu.info/spell-correct.html 怎样写一个拼写检查器 Pete ...

  5. NYOJ-开灯问题

    开灯问题 时间限制:3000 ms  |  内存限制:65535 KB 难度: 描写叙述 有n盏灯,编号为1~n.第1个人把全部灯打开,第2个人按下全部编号为2 的倍数的开关(这些灯将被关掉),第3  ...

  6. tweenanim动画

    1.视图 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:too ...

  7. C++多态原理

    C++的多态性是通过动态绑定实现的 非虚函数是在编译时绑定的; 通过对象进行的函数(虚函数,非虚函数)也是编译时绑定的; C++编译器在编译的时候,要确定每个对象调用的函数(要求此函数是非虚函数)的地 ...

  8. C#中ref参数及out参数对比

    ref 关键字和out关键字均会导致参数通过引用来传递(相同点1).这是两者的共同点. 通过引用传递参数,会使方法中对参数所做的任何修改都将反映在该变量中. 两者还有一个共同点,那就是:若要使用 re ...

  9. Swift - .plist文件数据的读取和存储

    每次在Xcode中新建一个iOS项目后,都会自己产生一个.plist文件,里面记录项目的一些配置信息.我们也可以自己创建.plist文件来进行数据的存储和读取. .plist文件其实就是一个XML格式 ...

  10. net core 中间件详解及项目实战

    net core 中间件详解及项目实战 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的H ...