C#数据类型

4933701-80ee19a1d77ffb87.png
变量数据类型

C# 的预定义类型(predefined type)或基元类型(primitive type)被视为其他所有类型的集合。

C#基本数值类型包括

  • 整数类型 8种


    4933701-a60aa9bcce8921aa.png
    整数类型
  • 科学计算的二进制浮点类型 2种


    4933701-8939b0b53d18fc08.png
    浮点类型
  • 金融计算十进制浮点类型 1种


    4933701-7f751ea0d7366d89.png
    浮点类型
  • 字符类型 1种

变量

  • 变量内置类型:intdoublestringcharbooldecimal
  • 变量使用规则:先声明,再赋值,最后使用。
  • 变量命名方式:Camel、Pascal

常量

  • 声明常量
const 变量类型 变量名=值
  • 常量不能被重新赋值

复杂数据类型

  • 类(classes)
  • 结构体(structures)
  • 枚举(Enumerations)
  • 接口(interfaces)
  • 委托(delegates)
4933701-b8fdc0123bd978ae.png
类型派生谱系

枚举

枚举并非C#特有,与C语言中的枚举一样,C#中的枚举包含枚举值及与值关联的数字。所谓枚举,就是把这种类型数据可取的类一一列举出来。枚举类型是一种特殊的值类型,是一组已命名的数值常量,常用于声明一组命名的常数。枚举的类型必须是8种整形类型之一。

枚举是可由开发者声明的值类型,枚举的关键特征是在编译时声明了一组可通过名称来引用的常量值,使得代码更易懂。

声明枚举语法

[public] enum 枚举名
{
  值1,
  值2,
  值3...
}

- public 访问修饰符,默认为public公开的,表示无论什么位置都可以访问到。
- enum 关键字,声明枚举的关键字。
- 枚举名,需要符合Pascal命名规范。

枚举常声明在命名空间的下面类的外面,表示在这个命名空间下,所有的类都可以访问与使用它。

public enum UserState
{
    Online,
    Offline,
    Leave,
    Busy,
    CallMe
}

使用枚举替代布尔能改善代码的可读性,枚举类型默认可通过int类型相互转换,枚举类型跟int类型是兼容的。

enum ConnectionState
{
    Disconnected,
    Connecting,
    Connected,
    Disconnecting
}

使用枚举值需要为其附加枚举名称前缀

ConnectionState.Connected

根据约定,除了位标志之外的枚举名应该使用单数形式。

ConnectionState

枚举值实际是作为整数常量实现的,默认第一个枚举值为0,后续每一项都递增1,然而,可以显示地为枚举赋值。

enum ConnectionState:short
{
    Disconnected,
    Connecting = 10,
    Connected,
    Joined = Connected, 
    Disconnecting
}

枚举总是具有一个基础类型,可以是除了char之外的任意整型。事实上,枚举类型的性能完全取决于基础类型的性能。默认基础类型为int,但可以使用继承语法指定其他类型 。

枚举使用规范

  • 考虑在现有枚举中添加新成员,但要注意兼容性风险。
  • 避免创建代表不完整值几何的枚举
  • 避免在枚举中创建“保留给将来使用”的值
  • 避免包含单个值得枚举

枚举和其他类型稍有不同,因为枚举的继承链是从System.ValueTypeSystem.Enum,再到枚举。

枚举之间的类型兼容性

C#不支持不同枚举数组之间的直接转型,但CLR允许,前提是两个枚举具有相同的基础类型。为了避开C#的限制,一个可用的技巧是先转型为System.Array

可见枚举类型的变量跟intstring类型相互转换,枚举类型默认和int类型相互兼容,可通过强制类型转换的语法相互转换。但转换一个枚举中没有值的时候不会抛出异常,而是直接将数字显示。枚举和string类型那个相互转换时,若将枚举转换成string类型,则直接调用ToString(),若要将字符串转换为枚举,可采用:UserState state = (UserState)Enum.Parse(typeof(UserState), input)

enum UserState
{
    Online,
    Offline,
    Leave,
    Busy,
    CallMe
}

Console.Write("Input State Number: ");
string input = Console.ReadLine();
//将字符串转换成枚举类型,调用Parse()方法的目的是为了让其帮助我们将一个字符串转换成对应的枚举类型。
UserState state = (UserState)Enum.Parse(typeof(UserState), input);
Console.WriteLine(state); //0 Online

案例:输入选项输出选项值

enum Color
{
    Red,
    Green,
    Blue
}
while (true) { 
    Console.WriteLine("Input Option: [0]Red [1]Green [2]Blue");
    Console.Write("Option: ");
    string input = Console.ReadLine();
    switch (input)
    {
        case "0": 
        case "1":
        case "2":
            Color state = (Color)Enum.Parse(typeof(Color), input);
            Console.WriteLine($"State: {state}");
            break;
        default:
            Process.GetCurrentProcess().Kill();
            break;
    }
}

结构

  • 结构类型是一种值类型,是一种堆栈(stack)分配的复杂类型。
  • 结构类型不支持继承
  • 结构类型主要用于创建小型的对象,可节省内存。

结构可包含构造器、常量、字段、方法、属性、索引器、运算符、事件、嵌套类型等。若同时需要上述多个成员,则应该使用类。与类不同的是,结构是值类型,无需在堆(heap)中分配空间。而且结构不是引用类型,所以也不支持继承。

  • 类是引用类型,结构是值类型。
  • 结构是隐式密闭的,意味着结构不能被派生。

结构可帮助一次性声明多个不同类型的变量

[public] struct 结构名
{
  成员;
}

尽管可利用结构来简化类的声明和实现步骤,但并不是所有情况下都比类优越。作为值类型的结构在传递数据时,实际上是在传递数据本身的副本,而应用类型的类则传递的是引用地址。所以,但传递结构时会比传递类要慢,数据量越大差距就越明显。

public class Dimensions
{
  public double Length{get;set;|
  public double Width{get;set;}
}
public struct Dimensions
{
  public double Length{get;set;}
  public double Width{get;set;}
}

创建自定义值类型使用的是和定义类和接口相似的语法,区别在于值类型使用关键字struct。虽然语言本身未作要求,但对于使用值类型的的一种良好的规范是确保值类型是不可变的。换言之,一旦实例化值类型,实例就不能修改。若要需改则应创建新实例。这是因为:首先,值类型表示的是值。其次,因为值类型是按值复制的,而非按引用复制,所以很容易地迷惑或错误地认为,一个值类型变量的变化也会被观察到引起了另一个变量的变化。

struct Point
{
  public int x;
  public int y;
}

结构是值类型

  • 结构类型的变量不能为null
  • 两个结构变量不能引用同一个对象

虽然结构是值类型,但在语法上常常可将其视为类来处理。

var point = new Dimensions();
point.Length = 3;
point.Width =  4;

因为结构是值类型,所以new运算符和类和其他引用的工作方式不同。new运算符并不分配堆中的内存,只是调用相应的构造函数,根据传递给它的参数,初始化所有的字段。

Dimensions point;
point.Length = 3;
point.Width = 4;

结构的初始化

public struct Dimensions
{
  public double Length{get;set;}
  public double Width{get;set;}
  public Dimensions(double length, double width)
  {
    Length = length; 
    Width = width;
  }
  public double Diagonal => Math.Sqrt(Length * Length + Width * Width);
}

除了属性和字段,结构可包含方法和构造器,但不可以包含用户定义的默认无参构造器。每个结构自动获得一个无参构造器,将所有字段初始化为各自的默认值。被自动初始化了的结构变量会自动初始化变量。

由于编译器会将声明时的实例字段赋值放到类型的默认构造器中,所以C#禁止在声明时对结构的字段进行赋值。但这并不是表示无需对字段进行初始化。除非所有字段都初始化,否则结构类型的局部变量无法使用。

幸好C#支持有参构造器,而且它们提出了一个有趣的初始化要求。为了确保值类型的局部变量被完全初始化,每个构造器都必须初始化结构中的所有字段。

结构可以有实例构造函数和静态构造函数,但不允许有析构函数。

规范:要确保结构的默认值的有效,总是可以获得结果默认的全零的值。

对结构赋值

将一个结构赋值给另一个结构,也就是将一个结构的值赋值给另一个结构,这和复制类变量不同,复制类变量时只复制引用。

和类不同的是,结构不支持终结器。结构以值的形式复制,不像引用类型那样具有“引用同一性”,所以难以知道在什么时候能安全执行终结器并释放结构占用的非托管资源。垃圾回收器知道在什么时候不再有对一个引用类型实例的“活动”引用,可在此之后的任何时间为这个引用类型运行终结器。但是,“运行时”没有任何机制能够跟踪值类型在特定时刻有多少个副本。

获取类型默认值default

所有值类型都有自动定义的无参构造器将值类型的实例初始化成默认状态。所以,任何值类型都可合法地使用new S()的语法。除此之外,可使用default操作符生成结构的默认值。

值类型的继承和接口

所有的值类型都隐式封闭,除此之外,除了枚举之外的所有值类型都派生自System.ValueType,这意味着结构的继承链总是从objectSystem.ValueType到结构。

值类型也能实现接口,.NET Framework内建的许多值类型都实现了IComparableIFormattable等接口。

System.ValueType规定了值类型的行为,但没有包含任何附加成员。System.ValueType重写了object的所有虚成员。在结构中重写基类方法的规则与类基本一样。区别在于,对于值类型GetHashCode()的默认实现是将调用转发给结构中的第一个非空字段。此外,Equals()大量利用了反射。所以,加入一个值类型在集合中频繁使用,尤其是使用了散列码的字典类型的集合,那么值类型应该同时包含对Equals()GetHashCode()的重写,以确保获得较好的性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值