C#编程思想及C#类型/运算符和强制类型转换

1.C#编程思想的几大方面

1)baseType过程式的编程缺陷:C#要注意内存数据值类型/引用类型,类型转换错误。
2)CRL OOP面向对象泛型接口:泛型设计和接口设计特性理解CRL补充的代码自己要添加的代码,IL中间语言分析原理。
3)AOP面向切面的编程需求特例,元编程,函数编程,数据编程:元编程和函数编程的风格,正则表达式,lambda表达式,LinQ编程。

4).Net Framework事件框架异步编程机制, 组件插件化微内核编程架构::委托的事件编程, 线程调度,异步编程。
5)语言优缺点,底层交互和优化性能:对象申请释放,内存管理特性,和C/C++语言的交互操作。

这里讲类型和类型转换。

2.引用类型和值类型,及一些内存操作

引用类型是类的实例(字符串/数组/所有的类对象),值类型是基本数据类型/结构体/或枚举的实例( 值类型初始化时候就会产生一个对应的堆实例类型但是不使用,装箱时候才填充使用)。
装箱将值类型转换为引用类型,例如转换为string ,转换为Object。
拆箱只能拆之前装箱过的,拆箱时候需要用()强制类型转换,转换后一定要确保拆后的栈类型大小能够容纳拆出来的类型,否则会报错。
装箱和拆箱时候内容都是拷贝过去的,所以装箱后改变了类型的值就不会影响原来值

对复杂类型用sizeof需要指定在不安全代码中,*,->指针运算也要放置到不安全代码区域中。
typeof返回特定类型的对象,在反射技术中很有用。

3.checked写在转换的表达式处转换溢出抛出异常

checked写在代码块中用{}括起来的将做溢出检查,默认是unchecked的。
编译选项设置为/checked就可以检查程序中所有未标记代码的溢出。
checked溢出检查不会对嵌套的代码起作用,所以需要在真正表达式转换的地方进行限定


4.可空类型?/??符号使用

可空类型,可以用Nullable声明,也可以用?声明,可空类型可以在于db/xml交互时候免去装箱和拆箱提高性能:
?声明为可空类型,可空类型参与的数学运算都是null,逻辑运算为false。
??第一个非空取第一个,否则取第二个。
Nullable x1;  
int ? x2;  
对可以声明可空类型,操作时候:  
x1 = 10;  
x2 = x1;  
if(!x1.HasValue)  
{  
 x1 = null;  
}  
 
数据交互时候:  
非可空类型可以直接转换为可空类型:  
int y1 = 4;  
int ? x1 = y1;  
 
可空类型转换为不可空类型,可能会失败,需要进行强制转换:  
int ? x1 = GetNullableType();  
int y1 = (int)x1;  
或??声明为合并符号,定义了默认值如果x1空那么y1 = 0:  
int y1 = x1 ?? 0;

5.隐式和显式类型转换

隐式类型转换直接转,基础类型提升,子类引用转换为父类都是直接进行的,其中uint转换为float等也算是隐式转换安全的。

显式类型转换用在强制类型转换中,用C风格的,不用C++风格的,类型提升会隐式进行转换,类型下降需要强制了类型转换,强制类型转换最好用checked符号限定避免出现异常。
基本类型转换为字符串用ToString函数,字符串类型转换为其它基本类型用Parse函数。

6.对象的相等判断函数

Object类的四个比较函数:
EqualReference静态引用比较不能重写,指向相同的引用才会相等。

虚拟的Equals方法,也是比较引用类型,如果要按值类比较那么需要重写,数据结构的字典类型中作为键值的类型一定要重写,否则会导致问题GetHashCode会异常,其它.net基类也可能出现异常
结构体中只是包含值类型.net做了处理,如果结构体中包含引用那么也要重写虚拟的Equals方法,否则只是比较引用的地址。

静态的Equals方法,不能重写,可以比较空值类型,两个空则true,一个空则fasle,都是引用类型会调用虚拟的Equals方法。
==运算符,默认是比较引用,但是提供了介于值类型和引用类型之间的比较,而不是改变Equals引用比较或值比较,.net string类型就重载了该运算符,用于比较字符串内容。结构体==运算符比较会出错,除非重载了该运算符。

7.运算符重载

运算符重载都是要public static的,表示属于类的特性,不是实例的特性。
 public static Vector operator +(Vector lhs, Vector rhs)
        {
            Vector result = new Vector(lhs);
            result.x += rhs.x;
            result.y += rhs.y;
            result.z += rhs.z;

            return result;
        }
+= -= *= /=等运算符C# IL 会展开所以重载 + - * /运算符即可。
重载了==运算符,那么也要重载!=运算符,也要重载Object的Equals和GetHashCode函数,原因是==运算符和Object的虚函数Equals需要实现相同的逻辑,要分别写因为==运算符先比较空值(IL 中会先传入null值比较),调用null.Equals(b)就会导致错误。

8.自定义类型间转换

1)自定义类型和基础类型间的转换:

转换函数也是和操作符重载一样public static的。

// 可以隐式转换
 public static implicit operator float(Currency value)
            {
                return value.Dollars + (value.Cents / 100.0f);
            }
// 需要显式用()符号写明
            public static explicit operator Currency(float value)
            {
                uint dollars = (uint)value;
                ushort cents = (ushort)((value - dollars) * 100);
                return new Currency(dollars, cents);
            }
为了确保精度在实现四舍五入,而不是截断的类型转换,用System.Convert类型进行转换,例如:Convert.ToUInt16();

2)类结构层次间的转换,子类对象转换为父类是合法的, 父类对象转换为子类需要在子类提供一个传入基类对象的构造函数
如果是兄弟节点之间的转换也是用一个子类中父类对象作为传入参数的构造函数即可。
is判断 用于检查类型是否是特定类型或者是特定类型的子类。
as转换 是将引用类型进行显式类型转换,如果是指定类型或者是指定类型的子类那么转换成功会返回被转换的类型,否则返回null。如果要

将子类当做父类用 用childObj as Father即可。

3)自定义类型转换总结:
不同数据类型尽量用相同的数据类型,结果格式化即可,不同的多种在转换显式的编写转换代码,避免默认复杂的转换路径


你可能感兴趣的:(C#.NET)