c#1所搭建的核心基础之类型系统的特征

时间:2021-09-20 00:14:42

类型系统的特征简介

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

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. re.S

    在Python的正则表达式中,有一个参数为re.S.它表示多行匹配

  2. python_way ,day26 django_admin 自定义

    1.想在admin中增加新的字段如图: 默认django只显示 def __str__(self)里面的return的值 from django.contrib import admin # Regi ...

  3. 有时候为了方便sql语句的对比和查询,我们可以使用declare来定义变量 上下篇的问题

    --定义一些参数 declare @nodeId int,@ekid int ,@ecid int,@eid int,@pid int --列表 SELECT * FROM ( SELECT *,RO ...

  4. Attention and Augmented Recurrent Neural Networks

    Attention and Augmented Recurrent Neural Networks CHRIS OLAHGoogle Brain SHAN CARTERGoogle Brain Sep ...

  5. 【Hadoop学习】Super用户以其他用户的名义执行操作

    Hadoop版本:2.6.0 本文系从官方文档翻译而来,转载请尊重译者的工作,注明以下链接: http://www.cnblogs.com/zhangningbo/p/4146410.html 简介 ...

  6. Qt 经典出错信息之”Basic XLib functionality test failed!”(Z..z..)

    此完整出错信息是在./configure阶段 Basic XLib functionality test failed! You might need to modify the include an ...

  7. 【android】android中activity的生命周期

    activity生命周期: 实例代码: public class DemoActivity extends Activity { //1.activity第一次被创建的时候,执行 @Override ...

  8. 如何处理JS与smarty标签的冲突

    smarty的默认标记符是大括号:{}, 假如我们页面上有JS且定义了函数或者对象,或者有CSS定义了样式,就会出现大括号, smary引擎就会把这些大括号当作smarty语法来解释, 很明显,这些C ...

  9. Shell基础命令(一)

    Shell 教程 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了一个 ...

  10. 20155328 《网络对抗》 实验九 Web安全

    20155328 <网络对抗> 实验九 Web安全 基础 实验过程记录 在实验开始之前先把webgoat的jar包放到home目录下.打开终端,用命令java -jar webgoat-c ...