不能使用clr编译c文件 怎么强制用clr_关于CLR的个人笔记2

1369733dae302dc8e751daf49cb40c78.png

上一篇主要说了下CLR的一些基础,这一篇继续说下CLR的设计类型

1.所有类型都是从System.Object派生,object是一切类的基类,所有类型都显式或者隐式的继承了Object,如class A{}他就等于class A:System.Object{}

2.is和as操作符

is和as操作符都是进行类型转换的,但是他俩有着一些差别;

is不会抛出异常,如 object A=new object();bool Equal=(A is object)//返回True,bool Equal=(A is 任意具体类型,如B(他是个类型))//返回false,为null时返回fasle;如果在if中使用,如if(A is object)时,如果A为空,则一直为false,在判断时,CLR会进行两次类型检测,首先看A是否兼容这个类型,如果是,则在核实是否引用,所以在性能上,is会有一定的损耗:

as在转型时也不会抛出异常,如果不能转,那他就是null,如果用as执行同样的操作,如

object e=o as object;if(o!=null)这样CLR指挥进行一次类型校验,if只判断是否为null,速度会快于is。

3.基元类型,引用类型,值类型

基元类型,如int float等编译器直接支持的数据类型被叫做基元类型;

引用类型,引用类型是指在托管堆上分配的成员,他有四个特性

fbdc3289f5631da396ffdba7bc228984.png

值类型,所有值类型都被称为结构或者枚举,例如System.Int32结构,,所有的结构都是从System.ValueType派生,它又派生自System.Object;

值类型的装箱和拆箱:

值类型存储在栈中,没有指针,也没有垃圾回收,反之引用类型存储在堆中,通过指针进行引用,有垃圾回收,当把一个值类型转换为引用类型时,被称为“装箱”,反之为“拆箱”;

如 int a; object b=a;//装箱 int c=(int)b;//拆箱

装箱和拆箱过多造成性能的极大消耗,比如说ArrayList的本质就是把所有对象都转换为object来进行存储的,如果存储的对象是值类型,则发生装箱操作,在遍历时(比如Add,赋值等)则会则会产生大量的拆装操作,还有内存分配,复制等,极大影响效率;

dynamic基元类型,它的本质是个object类型,编译器总会在编译时把他当作一个object,因为他的类型是不确定的,编译器无法知道它到底是不是个object,dynamic可用于局部变量,字段,参数。

4.类型和成员

类型的成员包括,常量,字段,实例构造器,类型构造器,方法操作符重载,转换操作符,属性,事件,类型;

静态类特性:

643a42c994e85f63d404d8c543b79002.png

CLR如何调用虚方法,属性,和事件:

调用虚方法:call 和callvirt

call可调用静态方法,实例方法和虚方法,但是call指令是以非虚方式调用虚方法;

callvirt可以调用实例方法和虚方法,callvirt则是以多态的方式调用虚方法;

常量:即指值不发生变化,如 const int a=5;,一旦确定,无法更改,且用const时必须赋值;readonly则可以不赋值,他的赋值可以延迟到构造函数里;

字段,即包含了一个值类型的的实例或者引用类型的引用, 如 public readonly int a=4;

实例构造器,即构造方法,1.当初始化一个类(引用类型)时,默认会调用一个无参的构造方法(隐式)或者自己写有参的等等,当用new关键字时,不但意味着内存的分配等,还意味着你调用了这个类的构造函数;2.当初始化一个值类型时,CLR不会主动调用构造函数,值类型默认会初始化为0,或者null,但是CLR允许为值类型定义构造函数,但必须显式定义,且用new初始化,否则不会执行(还会初始化为0或null),如

public struct Point

{ public int a;

public point(int a)

{ this.a=a;

}

}

sealed class exmple

{

public Point P=new Point(1);//会调用显式的构造函数,所以P.a=1;

public Point P2;//不会调用,直接初始化为0,所以,p2.a=0;

}

//接着昨天的写

扩展方法,当遇到有些没有提供且你想补充的时候,就可以用这个,比如你想用A类,但又不想改源代码,就可以自己写个静态类,类里面提供对这个类的扩展方法,如下:

public static class Ex

{

//this后面写的就是你要扩展的类,我要扩展A类,所以就是A

public static int ExFunc(this A,string Value)

{

//干一些你想要的

return 1;

}

}

public class example

{

void start

{

int A=Ex.ExFunc(A a , "")

Debug.log(A);//这里的A就是在那个扩展方法里面返回的1

}

}

需要注意的是扩展方法必须在静态类中声明,且不支持扩展属性,扩展事件,扩展操作符,且该类必须是顶级静态类。

分布方法略;

5.参数

传值方式和传引用方式

传值就是单纯的对于实参的复制,在方法内部对参数进行更改不会影响到外部的值如:

public int value=3;

Debug.log(value);//value=3

example(value);

Debug.log(value);//value=3

public void example(int a)

{

a=10;

}

引用方式ref和out,这个传递的则是参数的指针,会直接指向实参地址,在方法内更改参数,会影响到外部的值如:

public int value=3;//ref方式如不赋值会报错,out则无所谓

Debug.log(value);//value=3

example( ref value);

Debug.log(value);//value=10

public void example( ref int a)

{

a=10;//out在方法离开前必须赋值,否则报错

}

out和ref的区别就是,ref参数需要在用时赋值,out必须在方法离开前赋值;但实际CLR并不关心你是用的ref还是out,他俩生成的IL代码是一致的;

可变参数params;它允许你在参数前定义可变的参数如

public void Add(params int[] values)

{

}

6.属性

属性分为无参属性和有参属性(索引器);

属性的意义在于很好的封装数据,如

public int age;

这样写我在另一个类中调用时,它可以被任意赋值,比如200,这显示是不对的;CLR提供了get,set方式来获取一个字段的值,可以避免这个问题如:

public int age

{

get{return age;}

set{age=vaule;}//vaule总是代表新值,如需约束,直接if判断后在赋值即可,这样就避免了age=200或者负数的问题;

}

而且C#还提供了自动属性的语法糖,即 public int {get;set;}//无参属性

但要注意,无论是自动属性,还是显式实现属性的本质都是方法。

有参属性,即索引器如:

public class example

{

public int[]values=new int[20];

public int this[int index]

{

get{....retun 1;}

set{....return 2;}

}

}

public class Ex

{

int a=example[2];//get方法

example[2]=25;//set方法

}

索引器可以重载;

7.事件

事件基于委托,可以说事件是对委托的封装,事件的核心是回调,比如Task创建了一个事件,B和C都关注着A这个事(订阅),当A的事件发生时,将会通知B和C(回调);

如下图:

9257efd1b125a6e299967cf463045b2a.png

fb754668a4666a31d0cf64239c7ffee9.png
在类中定义回调方法

98a3b8e66acf7d18ed33d043e9803eae.png
+=即添加事件,-=移除事件

68d14727b0b04ef822692a7d3b0c3e07.png
执行结果

至于基于.net的标准事件写法,我不过多赘述,就是下面这种

public delegate void Dothings(object o,EventArgs e);

这种写法第一个参数object就是事件订阅者自身,EventArgs,就是自身的信息,可以自己写个类继承EventArgs类,然后传递一些有用的信息巴拉巴拉。。。

ClR在编译器内部实现了Add和Remove的方式,来实现上层的+=和-=

baa17948a221950233c3f614e6ac7910.png

8.泛型<T>

泛型具有清晰,类型安全,更好性能等优势,如当你使用ArrayList时,会发生装箱拆箱,List则不会,List就是个泛型,至于什么是泛型我不会赘述,我直接说一些特性

逆变与协变

fd5a3b4402bf90c262b1d79eb438fdec.png

泛型约束where

可以对泛型进行约束 如

public class Example<T> where T:class//表示是泛型类,泛型约束为引用类型

{

}

实际上泛型的小点很多,都说的话未必能说清楚,所以其他略。。

9.接口

C#不允许多重继承,但是接口间接的实现了这个功能,可以说是另类的多重继承;

接口用Interface为关键字,一般自己定义要用大写“I”作为开头,表明这是一个接口,就像泛型用“T”一样;继承接口就必须要实现接口方法,否则接口便没有了意义。如

public Interface Imfuncation

{

void func();//定义了一个无参无返回值的方法,接口方法不允许显式标记访问修饰符

}

public class example,Imfuncation//继承接口

{

public void func()//实现接口方法,且必须为public

{

}

}

接口继承还是类继承?即看你的需求是can-do“他能做”,还是is-a“属于”

接口的小点很多我也没有细说,也是因为边幅太长(懒),所以略

下章将讨论CLR的基本类型,还是一样,文章仅是个人粗略见解,仅供参考,如有错误请指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值