C# 基础

  • C#由微软开发,并由ECMA和ISO核准认可。
  • C#由Anders Hejlsberg和他的团队在.NET框架期间开发的
  • C#专为公共语言基础结构(CLI)设计的,CLI由可执行代码和运行时环境组成,允许在不同计算机平台和体系结构上使用各种高级语言。
  • 集成开发环境IDE:编辑=>编译=>调试=>发布
  • 学习路径:语言=>类库=>框架

功能

  • C#的构想接近于C和C++,是一门面向对象的编程语言,并与Java非常相似。
  • .NET 指 .Net Framework框架 是一种平台、技术
  • C# 可以开发基于.Net平台的应用

.Net开发领域

  • 桌面应用程序 WinForm
  • Internet应用程序 WebService/ASP.NET
  • 手机开发 WP7

网络架构模式

  • B/S
    浏览器(Browser)/服务器(Server)模式
  • C/S
    客户端(Client)/服务端(Server)模式
    桌面开发(WinForm)的网络应用程序,如飞秋、QQ等

环境

C#是.NET框架的一部分,且用于编写.NET应用程序。

.NET框架是一个创新的平台,能编写Windows应用程序、Web应用程序、Web服务。.NET由一个巨大的代码库组成,用于C#等客户端语言。

路线

.NET学习路线

入门

# 编写源代码
$ vim app.cs

// using关键字用于在程序中包含System命名空间
using System;
// 命名空间声明
namespace Application
{
    // 类声明:程序的最小单元
    class Klass
    {
        // 入口点方法:主函数
        static void Main(string[] args)
        {
            // 在屏幕上显示消息,WriteLine是定义在System命名空间下Consolle类的方法
            Console.WriteLine("Hello World");//命令:以分号结束
            // 针对VS.NET,程序会等待一个按键动作,防止程序从VS.NET启动时屏幕会快速运行并关闭。
            Console.ReadKey();
//F5调试运行 Ctrl+F5不调试直接运行 
        }
    }
}

# 编译源代码
$ csc app.cs
Microsoft (R) Visual C# Compiler version 4.7.3056.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

# 生成可执行文件
$ ls
app.cs app.exe

# 执行程序
$ app.exe
Hello World
将.NET添加入系统环境变量

代码剖析

  • 类class是构成程序的主体
  • 命名空间namespace以树型结构组织类
  • C#是大小写敏感的
  • 语句和表达式以分号结尾
  • 程序的执行从Main()方法开始
  • 与Java不同的是,文件名可以不同于类名。

类库的引用

  • DLL引用 黑盒引用
  • 项目引用 白盒引用

语法

$ vim rect.cs

using System;
namespace RectangleApp
{
    class Rectangle
    {
        double length;
        double width;
        
        public void AcceptDetails()
        {
            length = 1.5;
            width = 3;
        }
        
        public double GetArea()
        {
            return length * width;
        }
        
        public void Display()
        {
            Console.WriteLine("Length:{0}", length);
            Console.WriteLine("Width:{0}", width);
            Console.WriteLine("Area:{0}", GetArea());
        }
    }
    
    class ExecuteRectangle
    {
        static void Main(string[] args)
        {
            Rectangle rect = new Rectangle();
            rect.AcceptDetails();
            rect.Display();
            Console.ReadLine();
        }
    }
}

$ csc rect.cs
$ rect.exe
Length:1.5
Width:3
Area:4.5

从程序中输出文本

Console.WriteLine(格式字符串(含替代标记), 替换值0, 替换值1...)

System命名空间中的Console类提供ReadLine()函数,用于接收来自用户的输入,并将其存储到一个变量中。

int num;
num = Convert.ToInt32(Console.ReadLine());

标识符

用于识别类、变量、函数或任何其它用户定义的项目。

标识符
  • 标识符必须以字母、下划线、@开头,后跟字母、数字、下划线、@。
  • 标识符第一个字符不能是数字
  • 标识符必须不包含任何嵌入的空格或符号
  • 标识符不能是C#关键字,除非有@前缀。
  • 标识符必须区分大小写
  • 不能与C#类库名称相同
  • @字符只能放在标识符首位,不推荐将@作为常用字符。

关键字

关键字是C#编译器预定义的保留字,不能作为标识符。关键字分为保留关键字(Reserved Keywords) 和上下文关键字(Contextual Keywords)。

  • 上下文关键字是仅在特定的语言结构中充当关键字的标识符
上下文关键字

关键字约定

  • 关键字全部由小写字母组成
  • 关键字不能用作变量名或任何其他形式的标识符,除非以@字符开始。

堆栈

程序运行时,数据必须存储在内存中。一个数据项需要多大内存、存储在什么地方、如何存储都依赖于该数据项的类型。运行中的程序使用两个内存区域来存储数据:栈和堆。

栈是一个内存数组,是一个LIFO(Last-In First-Out,后进先出)的数据结构。

栈存储几种类型的数据

  • 某些类型变量的值
  • 程序当前的执行环境
  • 传递给方法的参数

栈的特征

  • 数据只能从栈的顶端插入和删除
  • 把数据放到栈顶成为入栈(push)
  • 从栈顶删除数据成为出栈(pop)
入栈出栈

堆是一块内存区域,在堆里可以分配大块的内存用于存储某类型的数据对象。与栈不同,堆里的内存能够以任意顺序存入和移除。

内存堆

程序可在堆中存储数据,但并不能显式地删除。CLR的自动GC(Garbage Collector,垃圾收集器)在判断出程序的代码将不会再访问某数据项时,自动清除无主的堆对象。

堆中的自动垃圾收集

类型

  • C程序是一组函数和数据类型
  • C++程序是一组函数和类
  • C#程序是一组类型声明

C# 程序是一组类型声明

  • C#程序或DLL的源代码是一组一种或多种类型声明
  • 对于可执行程序,类型声明中必须有一个包含Main方法的类。
  • 命名空间是一种把相关的类型声明分组并命名的方法

C# 程序是一组类型声明,学习C#就是学习如何创建和使用类型。什么是类型呢,可将它想象成一个用来创建数据结构的模板。模板本身并不是数据结构,但它详细说明了由该模板构造的对象的特性。

类型的组成元素

  • 名称
  • 用来保存数据成员的数据结构
  • 行为和约束条件
类型是一种模板

实例化类型

从某个类型模板创建实际的对象,称为实例化该类型。

  • 通过实例化类型而创建的对象被称为类型的对象或实例。
  • C#程序中,每个数据项都是某种类型的实例。
通过实例化类型创建实例

预定义类型

预定义类型

数据项的类型定义了存储数据需要的内存大小与组成该类型的数据成员。类型还决定了对象在内存中的存储位置 - 堆或栈。

类型被分为两种:值类型和引用类型,两种类型的对象在内存中的存储方式不同。

数据类型
  • 值类型 Value types
    仅需一段单独的内存,用于存储实际的数据。
值类型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 命名空间
namespace ConsoleApp
{
    //枚举类型需放在类class之外,才能被其它类调用
    //枚举类型:限定变量的可能值,其对应的是整型数值,默认从0开始。
    enum Weekday { Monday, Tuesday, Wenesday }
    //枚举类型:可定义带数字的枚举类型
    enum Color {Red = 1, Blue = 2, Green = 3};


    //类:程序的最小单元
    class Program
    {
        //struct和enum均为值类型,存储在内存的栈上。
        //结构体
        struct Person
        {
            public int age;
            public string name;
            public string firstName;
            public string lastName;
        }


        //程序入口:主函数
        static void Main(string[] args)
        {
            //所有类型均继承自 System.Object
            object obj = null;

            //值类型 继承自 System.ValueType
            int i1 = 0;   
            System.Int32 i2 = 10;

            //变量小写,必须预先声明类型
            bool flag = true;
            float f = 1.23f;

            //变量转换
            string str = "hello";
            //short s = (short)str;
            //string str1 = Convert.ToString(f);

            //调用结构
            Person person = new Person();
            person.age = 20;
            person.name = "junchow";

            //使用枚举
            var day = Weekday.Tuesday;
            Console.WriteLine(Weekday.Monday);
            Console.ReadLine();

        }
    }
}
  • 引用类型 Reference types
    需要两段内存,第一段存储实际的数据,位于堆中。第二段是一个引用,指向数据在堆中的存放位置。
引用类型
  • 指针类型 Pointer types
    指针类型存储另一种类型的内存地址,C#的指针与C或C++中的指针有相同的功能。
// 声明指针类型
type* identifier;
// 例如
char* cptr;
int * iptr;

C#中没有全局变量的概念,所有的变量必须由该类的实例进行操作,这样做提升了安全性,但某些情况下却显得力不从心。因此,在保存类的公共信息时,会使用静态变量。

# 在变量前添加static关键字即可声明为静态变量
static <data_type> <variable_name> = value;

值类型

值类型变量可直接分配给一个值,从System.ValueType中派生,最终继承自System.Object,值类型直接包含数据。

预定义类型
$ vim size.cs
using System;
namespace DatatypeApp
{
    class App
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Size of bool:{0}", sizeof(bool));
            Console.WriteLine("Size of byte:{0}", sizeof(byte));
            Console.WriteLine("Size of char:{0}", sizeof(char));
            Console.WriteLine("Size of decimal:{0}", sizeof(decimal));
            Console.WriteLine("Size of double:{0}", sizeof(double));
            Console.WriteLine("Size of float:{0}", sizeof(float));
            Console.WriteLine("Size of int:{0}", sizeof(int));
            Console.WriteLine("Size of long:{0}", sizeof(long));
            Console.WriteLine("Size of sbyte:{0}", sizeof(sbyte));
            Console.WriteLine("Size of short:{0}", sizeof(short));
            Console.WriteLine("Size of uint:{0}", sizeof(uint));
            Console.WriteLine("Size of ulong:{0}", sizeof(ulong));
            Console.WriteLine("Size of ushort:{0}", sizeof(ushort));
            Console.ReadLine();
        }
    }
}
$ csc size.cs
$ size.exe
Size of bool:1
Size of byte:1
Size of char:2
Size of decimal:16
Size of double:8
Size of float:4
Size of int:4
Size of long:8
Size of sbyte:1
Size of short:2
Size of uint:4
Size of ulong:8
Size of ushort:2

如需得到一个类型或一个变量在特定平台上的准确尺寸,可使用sizeof()。表达式sizeof(type)产生以字节为单位存储对象或类型的存储尺寸。

引用类型

引用类型不包含存储在变量中的实际数据,包含的是对变量的引用。它们指向的是一个内存位置。使用多个变量时,引用类型可指向一个内存位置。若内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的引用类型有object、dynamic、string。

引用类型

C#内置引用类型分为objectstringdynamic

对象类型

对象类型是C#通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object是System.Object类的别名。所以对象类型可以被分配任何其他类型的值。在分配之前,需先进行类型转换。

Object终极基类,是CLR运行时定义的最基础的类型,CLR运行时要求所有类型,不管是系统定义的类型,还是自定义的类型,都必须从Object类派生。

注意:一个值类型转换为对象类型时被称为装箱,但一个对象类型被转换为值类型时被称为拆箱。

由于所有类型都从System.Object类型派生而来,所以每个类型的每个对象实例均有一组最基本的方法。

  • Equals 虚方法,若两个对象具有相同的值,则返回 true。
[__DynamicallyInvokable]
public virtual bool Equals(object obj)
{
  return RuntimeHelpers.Equals(this, obj);
}
  • ToString 虚方法,默认返回类型的完整名称
this.GetType().FullName
  • GetHashCode 虚方法,返回对象值得哈希值
  • GetType
    返回从Type派生的一个类型的实例,指出调用GetType的对象是什么类型,返回的Type对象和反射类配合,可获取对象类型相关元数据信息。
    GetType并非虚方法,目的是防止类重写该方法,隐瞒其类型以破坏类型的安全性。
object obj1 = new object();//小写objecct是System.Object类的别名
object obj2 = new Object();
Console.WriteLine(obj1.GetType());//System.Object
Console.WriteLine(obj1.ToString());//System.Object
Console.ReadLine();

字符串类型

字符串类型运行为变量分配任何字符串值,字符串类型stringSystem.String类的别名,它是从对象类型object派生的。字符串类型的值可通过两种形式分配:引号和@引号。

String str = "hi"

@"hi"

C#的字符串类型前可添加@(称作“逐字字符串”)将转义字符\当作普通字符对待。

string str = @"C:\Windows";

string str = "C:\\Windows";

@字符串中可任意换行,换行符、缩进空格都计算在字符串长度之内。

C#中字符串一旦创建就不可以更改长度大小,每次使用System.String类中方法时,都要在内存中创建新的字符串对象,也就是说每次都要为新对象分配空间。在需要对字符串执行重复修改的情况下,与创建新的String对象相关的系统开销可能会非常昂贵。

//分配固定内存大小
string controllername = "User";
//创建新的内存分配给controllername,代价昂贵。
controllername += "Controller";

如果要修改字符串而非创建新的对象则可使用System.Text.StringBuilder类,例如:当在一个循环中将许多字符串连接起来时,此时可提升性能。

//指定分配大小,当指定分配大小后性能会提升。
StringBuilder sb = new StringBuilder(20);
//分配到堆区
sb.Append("User");
//不会被销毁直接追加
sb.Append("Controller");

String声明后在内存中大小是不可以修改的,使用StringBuilder则可自由扩展大小,原因String分配在栈区,StringBuilder则分配在堆区。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp
{                    
    class Program
    {                   
        static void Main(string[] args)
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();

            //string str = string.Empty;
            //for(int i=0; i<100000; i++)
            //{
            //    str += i.ToString(); //elapsed milliseconds : 16563
            //}

            StringBuilder sb = new StringBuilder();
            for(int i=0; i<100000; i++)
            {
                sb.Append(i.ToString());//elapsed milliseconds : 14
            }

            timer.Stop();
            Console.WriteLine("elapsed milliseconds : {0}", timer.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}

用户定义类型

  • 类类型 class
  • 结构类型 struct
  • 数组类型 array
  • 枚举类型 enum
  • 委托类型 delegate
  • 接口类型 interface

类型通过类型声明创建

  • 要创建的类型的种类
  • 新类型的名称
  • 对类型中每个成员的声明(名称和规格)

动态类型

动态类型可存储任何类型的值在动态数据类型变量中,这些变量的类型检查时在运行时发生的。

# 声明动态类型
dynamic <variable_name> = value;
# 例如
dynamic variable = 20;

动态类型与对象类型相似,对象类型变量的类型检查时在编译时发生的,动态类型变量的类型检查时在运行时发生的。

静态类型

每个变量都包括变量类型,编译器可以确定运行时需要的内存容量以及那些部分应该存放在栈上,哪些部分应该存放在堆上。变量的类型在编译时确定,并且不能再运行时修改,这叫静态类型。

可空类型

某些情况下,特别是使用数据库时,希望表示变量目前未保存有效的值。对于引用类型很简单,可见变量设置为null。但定义值类型的变量时,不管它的内容是否有效,内存都会进行分配。

对于这种情况,可能会使用一个布尔指示器来和变量关联,如果值有效则设置为true否则设置为false

可空类型运行创建可以标记为有效或无效的值类型,在使用之前确定值得有效性。普通的值类型成为非可空类型。

using System;
namespace NullApp
{
    class Nullable
    {
        static void Main(string[] args)
        {
            // ?单问号
            // 用于对int、double、bool等无法直接赋值为null的数据进行null的赋值。
            int? n = 1;
            // 意思是此数据是Nullable可空类型的
            Nullable<int> m = new Nullable<int>(1);
            Console.WriteLine("n = {0}, m = {1}", n, m);
            
            // 可空类型
            // C#提供了一个特殊的数据类型nullable可空类型
            // 可空类型可以表示其基础值类型正常范围内的值,再加上一个null值。
            // 在处理数据库和其他包含可能未赋值的元素的数据类型时
            // 将null复制给数值类型或布尔类型的功能特别有用
            int? i = null;
            int? j = 1;
            double? x = new double?();
            double? y = 3.14;
            // 数据库中的布尔型字段可存储值true或false或者该字段也可以未定义
            bool? flag = new bool?();
            Console.WriteLine("i={0}, j={1}, x={2}, y={3}, flag={4}", i, j, x, y, flag);
            
            // Null合并运算符??
            // Null合并运算符用于定义可空类型和引用类型的默认值
            double? a = null;
            Console.WriteLine("a = {0}", a ?? 1.1);
            // Null合并运算符为类型转换定义了一个预设值,以防止可能类型的值Null。
            // Null合并运算符把操作数类型隐式转换为另一个可空或非空的值类型的操作数的类型
            // 如果第一个操作数的值为空,则运算符返回第二个操作数的值,否则返回第一个操作数的值。
            double? b = 1.23;
            Console.WriteLine("b = {0}", b ?? 3.21);
            
            Console.ReadLine();
        }
    }
}

类型转换

类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。

C#中,类型铸造有两种形式

  • 隐式类型转换

隐式类型转换是C#默认的以安全方式进行的转换,不会导致数据丢失。例如,从小范围的整形转换为大范围的整形,从派生类转换为基类。

int i = 1;
double d = 1.1;

int n = i + d;//错误:无法将类型double隐式地转换为int

当高精度数据类型与低精度数据类型进行运算时,定义运算结果的变量类型必须与高精度的变量类型保持一致,以防止在运算过程中造成数据丢失。

  • 显式类型转换

显示类型转换即强制类型转换,显示转换需要强制转换运算符,且强制转换会造成数据丢失。

$ vim conv.cs
using System;
namespace TypeConversionApp
{
    class Explicit
    {
        static void Main(string[] args)
        {
            double d= 123.456;
            int i;
            
            i = (int)d;
            Console.WriteLine("d={0}, i={1} ", d, i);
            Console.ReadKey();
        }
    }
}
$ cs conv.cs
$ conv.exe
d=123.456, i=123

变量

  • 变量是一个名称,表示程序执行时存储在内存中的数据。

变量是一个供程序操作的存储区的名字。C#中每个变量都有一个特定的类型,类型决定了变量的内存大小和布局。范围内的值可存储在内存中,可对变量进行一系列操作。

class Test
{
    static void Main()
    {
        string username;
        System.Console.Write("username: ");
        username = System.Console.ReadLine();
        System.Console.WriteLine("your username is {0}", username);
    }
}

变量习惯采用驼峰法Camel命名,即大小写形式。

C#提供4种变量

  • 本地变量
    在方法的作用域内保存的临时变量,不是类型的成员。
  • 字段
    保存和类型或类型实例相关的数据,是类型的成员。
  • 参数
    用于从一个方法到另一个方法传递数据的临时变量,不是类型的成员。
  • 数组元素
    同类数据项构成的有序集合的一个成员,可以是本地变量,也可以是类型的成员。
# 变量定义
<data_type> <variable_list>;

# 示例
int i,j,k;
char c,ch;
float f;
double d;

# 变量定义时初始化
int n = 1;

变量声明

变量使用前必须声明,变量声明定义了变量,并完成两件事情。

  • 给变量命名,并为它关联一种类型。
  • 让编译器为它分配一块内存
变量声明
变量声明

变量初始化(variable initializer)

除声明变量的名称和类型外,声明还能将它的内存初始化为一个明确的值。

# 变量通过在等号后跟一个常量表达式进行初始化(赋值)
variable_name = value;

# 变量可在声明时被初始化,即指定一个初始值。
<data_type> <variable_name> = value;

正确地初始化变量是一个良好的编程习惯,否则有时程序会产生意想不到的结果。

$ vim def.cs
using System;

namespace VariableDefinition
{
    class Program{
        static void Main(string[] args)
        {
            short s;
            int i;
            double d;
            
            s = 1;
            i = 10;
            d = s + i;
            Console.WriteLine("s = {0}, i = {1}, d = {2}", s, i, d);
            Console.ReadLine();
        }
    }
}
$ csc def.cs
$ def.exe
s = 1, i = 10, d = 11

左值与右值

C#中的两种表达式

  1. lvalue 左值表达式可出现在赋值语句的左边或右边
    变量是左值的,可出现在赋值语句的右边。
int i = 10;
  1. rvalue 右值表达式可出现在赋值语句的右边,不能出现在赋值语句的左边。
    数值是右值的,因此不能被赋值,不能出现在赋值语句的左边。
10 = 100;// 无效语句,编译时错误。

常量

  • 类是一种活动的数据结构
    面向对象分析和设计产生之前,程序员把程序当作指令的序列。焦点主要放在指令的组合和优化上。随着面向对象的出现,焦点从优化指令转移到组织程序的数据和功能上。程序的数据和功能被组织为逻辑上相关的数据项和函数的封装集合,并称为类。
    类是一个能存储数据并执行代码的数据结构,包含数据成员和函数成员。
扑克牌程序

方法

var关键字并不是特定类型变量的符号,只是句法上的速记,表示任何可以从初始化语句的右边推断出的类型。C#中的var不像JS的那样可以引用不同类型,C#的var是从等号右边推断出的实际类型的速记,它并不改变C#的强类型性质。

  • var只能用于本地变量,不能用于字段。
  • var只能在变量声明中包含初始化时使用
  • 一旦编译器推断出变量的类型,它就是固定且不能更改的。
using System;
namespace CalculorApp
{
    class NumberManipulator
    {
        // 方法调用
        public int Max(int i, int j)
        {
            return i>j ? i : j;
        }
        // 递归方法调用
        // 一个方法可以自我调用,就是所谓的递归。
        public int Factorial(int i)
        {
            if(i==1){
                return 1;
            }else{
                return Factorial(i-1)*i;
            }
        }
        // 按值传递参数
        // 参数传递的默认形式,但调用方法时会为每个值创建一个新的存储位置。
        // 实际参数的值会复制给形参,实参和形参使用的是两个不同内存的值。
        // 但形参的值发生改变时,不会影响实参的值,从而保证实参数据的安全。
        public void Swap(int i, int j)
        {
            int tmp;
            tmp = i;
            i = j;
            j = tmp;
        }
        // 按引用传递参数
        // 引用参数是一个对变量的内存位置的引用。
        // 按引用传递参数时,与值参数不同的时,不会为参数创建新的存储位置。
        // 引用参数表示与提供方法的实际参数具有相同的内存位置。
        // C#中使用ref关键字声明引用参数
        public void RefSwap(ref int i, ref int j)
        {
            int tmp;
            tmp = i;
            i = j;
            j = tmp;
        }
        // 按输出传递参数
        // return 语句可用于只从函数中返回一个值,使用输出参数可从函数中返回多个值。
        // 输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
        public void Output(out int i, out int j)
        {
            // 提供给输出参数的变量不需要赋值
            // 当需要从一个参数没有指定初始化的方法中返回值时,输出参数特别有用。
            i = Convert.ToInt32(Console.ReadLine());
            j = Convert.ToInt32(Console.ReadLine());
        }
        // 入口点
        static void Main(string[] args)
        {
            NumberManipulator nm = new NumberManipulator();
            /*
            int i = Convert.ToInt32(Console.ReadLine());
            int j = Convert.ToInt32(Console.ReadLine());
            int max = nm.Max(i, j);
            Console.WriteLine("i = {0}, j = {1}, max = {2}", i, j, max);
            
            int i = Convert.ToInt32(Console.ReadLine());
            int result = nm.Factorial(i);
            Console.WriteLine("i = {0}, result = {1}", i, result);
            
            int i = Convert.ToInt32(Console.ReadLine());
            int j = Convert.ToInt32(Console.ReadLine());
            nm.Swap(i,j);
            Console.WriteLine("i = {0}, j = {1}", i, j);
            
            int i = Convert.ToInt32(Console.ReadLine());
            int j = Convert.ToInt32(Console.ReadLine());
            nm.RefSwap(ref i, ref j);
            Console.WriteLine("i = {0}, j = {1}", i, j);
            
            int i,j;
            nm.Output(out i, out j);
            Console.WriteLine("i = {0}, j={1}", i, j);
            */
        }
    }
}

数组

using System;
namespace ArrayApp
{
    class Ary
    {
        static void Main(string[] args)
        {
            // 数组
            // 数组是一个存储相同类型元素的固定大小的顺序集合
            // 数组是用来存储数据的集合,数组是相同类型变量的集合。
            // 数组中指定元素是通过索引来访问的,数组是由连续的内存位置组成的。
            // 最低的位置对应第一个元素,最高的地址对应最后一个元素。
            
            // 声明数组
            // 声明数组不会在内存中初始化数组
            int[] arr;
            
            // 初始化数组
            // 当初始化数组变量时,可赋值给数组。
            // 数组是引用类型,需使用new来创建数组的实例。
            arr = new int[10];
            
            // 赋值给数组
            // 通过使用索引赋值给一个单独的数组元素
            arr[0] = 1;
            // 可以在声明数组的同时给数组赋值
            double[] darr = {1.1, 2.2, 3.3};
            // 可以创建并初始化一个数组
            int[] iarr = new int[3]{1,2,3};
            // 创建并初始化数组时可以省略数组的大小
            int[] iary = new int[]{3,2,1};
            // 可赋值数组变量到另一个目标数组变量中
            // 此时目标和源会指向相同的内存位置
            int[] src = new int[]{10,20,30};
            int[] dst = src;
            // 创建数组时,C#编译器会根据数组类型,隐式初始化每个数组元素为一个默认值。
            // int数组的所有元素都会被初始化为0
            
            // 访问数组元素
            // 元素通过带索引的数组名称来访问
            int score = dst[1];
            
            // 遍历数组
            foreach(int i in dst)
            {
                Console.WriteLine("Element = {0}", i);
            }
            
            for(int j = 0; j < src.Length; j++)
            {
                Console.WriteLine("Item[{0}] = {1}", j, dst[j]);
            }
            
            Console.ReadLine();
            
        }
    }
}

字符串

using System;
namespace StringApp
{
    class Str
    {
        static void Main(string[] args)
        {
            // 字符串
            // C#中可使用字符数组表示数组,可使用string关键字声明字符串变量。
            // string关键字是System.String类的别名
            
            // 通过给string变量指定一个字符串
            string firstname, lastname;
            firstname = "Bruce";
            lastname = "Lee";
            // 字符串连接
            string fullname = firstname + lastname;
            Console.WriteLine(fullname);//BruceLee
            
            //通过使用String类构造函数
            char[] letters = {'a', 'n', 't', 'm', 'a', 'n'};
            string nickname = new string(letters);
            Console.WriteLine(nickname);//antman
            
            //通过方法返回字符串
            string[] msgs = {"GNU", "is","Not","UNIX"};
            string message = String.Join(" ", msgs);
            Console.WriteLine(message);//GNU is Not UNIX
            
            //用于转化值得格式化方法
            DateTime dt = new DateTime(2018, 7, 26, 12, 30, 00);
            string datetime = String.Format("{0:d} {0:t}",dt);
            Console.WriteLine(datetime);//2018/7/26 12:30
            
            Console.ReadLine();
        }
    }
}

结构体

using System;

namespace StructApp{
    // 结构体
    // 结构体是值类型的数据结构
    // 结构体使得单一变量可存储各种数据类型的数据。
    // 结构体使用struct关键字创建
    // 结构体是用来代表一个记录
    public struct Kv
    {
        //结构体声明字段无法赋初值,类可以。
        private string key;
        private string val;
        // 结构体的构造函数中,必须为结构体所有字段赋初值,类的构造函数无此限制。
        public Configs(string _key, string _val)
        {
            this.key = _key;
            this.val = _val;
        }
        public string getKey(){
            return key;
        }
        public string getVal(){
            return val;
        }
    }
    // 结构体是值类型,类是引用类型。
    // 结构体不支持继承
    // 结构体不能声明默认的构造函数
    public class StructCls
    {
        public static void Main(string[] args)
        {
            Kv app = new Kv("appid","va2lq2en1");
            Kv web = new Kv("website","http://yourname.com");
            
            Console.WriteLine("{0}:{1}", app.getKey(), app.getVal());
            Console.WriteLine("{0}:{1}", web.getKey(), web.getVal());
            
            Console.ReadLine();
            
        }
    }   
}

枚举

using System;
namespace EnumApp
{
    class EnumCls
    {
        // 枚举
        // 枚举是一组命名整型常量,枚举是值类型,也就是说,枚举包含自己的值,不能继承或传递继承。
        // 枚举列表中每个符号代表一个整数值,一个比它前面的符号大的整数值。
        // 默认情况下,第一个枚举符号的值为0。
        public enum Week {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
        
        public static void Main(string[] args)
        {
            int sun = (int)Week.Sunday;
            int mon = (int)Week.Monday;
            int tues = (int)Week.Tuesday;
            //Sunday:0, Monday:1, Tuesday:2     
            Console.WriteLine("Sunday:{0}, Monday:{1}, Tuesday:{2}", sun, mon, tues);
            Console.ReadLine();
        }
    }
}

using System;

namespace ClsApp
{
    // 类的定义
    // 类的定义以关键字class开始,后跟类名。
    // 类的默认访问标识符是internal
    // 类的陈云默认访问标识符为private
    internal class Cls{
        // 成员变量
        // 成员变量是对象的属性,且保持私有来实现封装,只能使用公共成员函数来访问。
        private double length;
        private double width;
        private double height;
        // 静态变量
        // 静态变量用于定义常量,其值直接调用类而无需创建类的实例来获取。
        // 静态变量在成员函数或类的定义外部进行初始化,也可在类的定义内部初始化静态变量。
        public static double PI;
        //public static double PI = 3.14;//错误:类、结构或接口成员声明中的标记“=”无效
        //public static count;//类、结构或接口成员声明中的标记“;”无效
        public static int count;//类、结构或接口成员声明中的标记“;”无效
        
        // 构造函数
        // 类的构造函数是类的特殊成员函数
        // 当创建类的新对象时自动执行
        // 构造函数的名称与类名完全相同,且无返回值。
        // 默认构造函数无参数,有参数的构造函数称为参数化构造函数。
        // 参数化构造函数在创建对象时给对象赋初始值
        // public void Cls(){} //错误成员名称不能与它们的封闭类型相同
        Cls(double _length = 0.0, double _width = 0.0, double _height = 0.0)
        {
            Console.WriteLine("对象已创建");
            length = _length;
            width = _width;
            height = _height;
        }
        // 析构函数
        // 析构函数是类的特殊成员函数
        // 当类的对象超出范围时执行
        // 析构函数名称是在类名前加上波浪号~作为前缀
        // 析构函数无返回值且无参数
        // 析构函数用于在结束程序之前释放资源
        // 析构函数不能继承或重载
        ~Cls()
        {
            Console.WriteLine("对象已销毁");
        }
        // 成员函数
        // 类的成员函数是在类定义中有它的定义或原型的函数
        // 作为类的成员,能在类的任何对象上操作,且能访问该对象的类的所有成员。
        public void SetLength(double _length = 0.0)
        {
            length = _length;
        }
        public double GetLength(){
            return length;
        }
        
        public double GetVolume()
        {
            return length * width * height;
        }
        // 静态变量可在成员或类的外部进行初始化,也可在类的定义内部初始化。
        public void SetCount()
        {
            count++;
        }
        public int GetCount()
        {
            return count;
        }
        // 静态函数
        // 静态函数只能访问静态变量
        // 静态函数在对象被创建之前就已经存在
        // 静态函数无需实例化即可调用
        public static double GetArea(double radius = 0.0)
        {
            return PI * radius;
        }
        // 入口点
        private static void Main(string[] args)
        {
            Cls cls = new Cls(1.0, 2.0, 3.0);
            double result = cls.GetVolume();
            Console.WriteLine("volumn = {0}", result);
            
            /*
            double input = Convert.ToDouble(Console.ReadLine());
            
            Cls.PI = 3.14;
            double area = Cls.GetArea(input);
            Console.WriteLine("area = {0}", area);
            Console.ReadLine();
            */
        }
    }
}

继承

using System;

namespace InheritanceApp
{
    // 基类
    // 一个类可派生自多个类或接口,意味着类可从多个基类或接口继承数据和函数。
    class Shape
    {
        protected double width;
        protected double height;
        public Shape(double _width, double _height)
        {
            width = _width;
            height = _height;
        }
        public void SetWidth(double _width)
        {
            width = _width;
        }
        public void SetHeight(double _height)
        {
            height = _height;
        }
    }
    // 派生类
    class Rectangle:Shape
    {
        private double area;
        // 基类初始化
        // 派生类继承了基类的成员变量和函数,父类对象应在子类对象创建前被创建,可在成员初始化列表中进行父类的初始化。
        public Rectangle(double _width, double _height):base(_width, _height)
        {
            area = width * height;
        }
        public double GetArea()
        {
            return area;
        }
        public double GetCost(double _price){
            return area * _price;
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Rectangle rect = new Rectangle(10.0, 20.0);
            Console.WriteLine("area = {0}, cost = {1}", rect.GetArea(), rect.GetCost(5.5));
            Console.ReadLine();
        }
    }
}

多态


using System;

namespace App
{
    class Print
    {
        // 函数重载
        static void Out(int i)
        {
            Console.WriteLine("{0}",Convert.ToInt32(i));
            Console.ReadLine();
        }
        static void Out(double d)
        {
            Console.WriteLine("{0}",Convert.ToDouble(d));
            Console.ReadLine();
        }
        static void Out(string s)
        {
            Console.WriteLine("{0}",Convert.ToString(s));
            Console.ReadLine();
        }
        static void Main(string[] args)
        {
            Print.Out(1);
            Print.Out(1.1);
            Print.Out("hello");
        }
    }
}

获取系统相关信息

using System;
using System.Collections.Generic;
namespace App
{
    public class OS
    {       
        public static Dictionary<string, string> GetOS()
        {
            
            int major = System.Environment.OSVersion.Version.Major;
            int minor = System.Environment.OSVersion.Version.Minor;
            string[] arr = {Convert.ToString(major), Convert.ToString(minor)};
            
            string osVersion = string.Join(".", arr);
            string osName = "";
            switch(osVersion){
                case "5.0":
                    osName = "Windows2000"; 
                    break;
                case "5.1":
                    osName = "WindowsXP"; 
                    break;
                case "5.2": 
                    osName = "Windows2003";
                    break;
                case "6.0":
                    osName = "Windows2008";
                    break;
                case "6.1": 
                    osName = "Windows7"; 
                    break;
                case "6.2": 
                    osName = "Windows8"; 
                    break;
                case "10.0": 
                    osName = "Windows10"; 
                    break;
                default:
                    osName = "Unknown";
                    break;
            }
            
            Dictionary<string, string> dict = new Dictionary<string, string>();
            dict.Add("OSVersion", osVersion);
            dict.Add("OSName", osName);
            return dict;
        }
        public static Dictionary<string, string> GetIp()
        {
            string hostname = System.Net.Dns.GetHostName();
            
            System.Net.IPHostEntry ipEntry = System.Net.Dns.GetHostEntry(hostname);
            string ip = ipEntry.AddressList[1].ToString();
            
            Dictionary<string,string> dict = new Dictionary<string, string>();
            dict.Add("HostName", hostname);
            dict.Add("IP", ip);
            
            return dict;
        }
        static void Main(string[] args)
        {
            foreach(KeyValuePair<string, string> kvp in OS.GetIp())
            {
                Console.WriteLine("{0}:{1}",kvp.Key, kvp.Value);
            }
            
            //操作系统信息
            OperatingSystem os = System.Environment.OSVersion;
            Console.WriteLine("Platform:{0}, ServicePack:{1}, Version:{2}, VersionString:{3}", os.Platform, os.ServicePack, os.Version, os.VersionString);
            
            Console.WriteLine("CLR Version:{0}", System.Environment.Version);
            
            //检查程序运行时间
            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            timer.Start();
            
            decimal total = 0;
            for(int i=0; i<100000; ++i){
                    total = total + (decimal)Math.Sqrt(i);
            }
            
            timer.Stop();
            Console.WriteLine("ElapsedMilliseconds:{0}, ElapsedTime:{1}", timer.ElapsedMilliseconds, timer.Elapsed);
        }
    }
}

MVC开发所使用的C#语言特性

使用自动实现的属性

常规的C#属性可以暴露类的数据片段,这种数据片段与设置和接收数据采取了一种松耦合的方式。

// 定义属性
namespace App.Models{
  public class Product{
    //字段 name 
    private string name;
    //在类中定义属性 Name,属性是带有getter、setter块的成员
    public string Name{
      get {return name;}//读取块
      set{name = value;}//设置块
    }
  }
}

// 在控制器中使用属性
namespace App.Controllers{
  public class HomeController : Controller {
    public ViewResult Index(){
      // 创建新的Product对象
      Product product = new Product();
      // 设置属性值
      product.Name = "alice";
      // 读取属性
      string productName = product.Name;
      // 生成视图
      return View("Result", (object)String.Format("product name is {0}", productName));
    }
  }
}

属性要具有灵活性,没有重复的gettersetter,解决办法是使用自动实现的属性,也称为“Automatic Property”自动属性。利用自动属性,可采用字段支持式属性模式,而无需定义该字段或在gettersetter中指定代码。

//自动实现的属性
namespace App.Models{
  public class Product{
    // 并未定义getter和setter的体,也未定义该属性返回的字段,两者由C#编译器在类被编译时自动完成。
    public int ProductID {get; set;}
    // 使用自动属性与使用规则属性没什么不同
    public string Name{get; set;}
    // 使用自动属性可减少一些输入,形成更易于阅读的代码,但仍能保持属性的灵活性。
    // 若需改变一个属性的实现方式,还可返回到规则属性的格式。
    public decimal Price{get; set;}
  }
}

将自动属性切换成规则属性

namespace App.Models{
  public class Product{
    //字段
    private string name;
    // 自动属性
    public int ProductID {get; set;}
    // 规则属性:必须同时实现getter和setter,以回到规则属性的定义方式。
    public string Name {
      // C#在一个属性中不支持混用自动和规则风格的getter和setter
      get{ return ProductID + name; }
      set{ name = value; }
    }
  }
}

使用对象与集合的初始化器

namespace App.Controllers {
  public class HomeController:Controller{
    public ViewResult CreateProduct(){
      // 创建新的Product对象
      Product product = new Product();
      // 设置属性值
      product.ProductID = 100;
      product.Name = "alice";

      return View("Result", (object)String.Format("product name is {0}", product.Name));
    }
  }
}

使用对象初始化器(Object Initializer)能够在一个步骤中创建并填写实例

// 创建并填充对象
Product product = new Product(){
  ProductID = 100,
  Name = "alice"
}

类名后调用的花括号形成了初始化器,目的是为参数提供值,以此作为该对象的构造过程。已同样的特性作为构造过程,也能对集合和数组的内容进行初始化。

// 构建并初始化一个数组
string[] strArr = {"Windows", "Linux", "UNIX"}

//构建并初始化发反省集合库的类
List<init> intList = new List<int> {10, 20, 30};

Dictionary<string, int> dic = new Dictionanry<string, int>{
  {"windows", 10},
  {"Linux", 20},
  {"UNIX", 30}
};

使用扩展方法

扩展方法(Extension Method)指那些不是你拥有的、不能直接修改的类添加方法的一种方便的方式。

namespace App.Models{
  public class ShoppingCart {
    // 类中封装了一个Product对象的列表
    public List<Product> Products {get; set;}
  }
}

定义扩展方法

namespace App.Models{
  public static class ExtensionMethods{
    // this关键字把TotalPrices标记为一个扩展方法
    public static decimal TotalPrices(this ShoppingCart cart){
      decimal total = 0;
      foreach(Product product  in cart.Products){
        total += product.Price;
      }
      return total;
    }
  }
}

使用扩展方法

namespace App.Controllers{
  public class HomeController{
    public ViewResult UseExtension(){
      // 创建并填充
      ShoppingCart cart = new ShoppingCart{
        Products = new List<Product>{
          new Product {Name = "kayak", Price = 10},
          new Product{Name = "ball", Price = 20}
        }
      };
      // 计算购物车总价
      decimal total = cart.TotalPrices();

      return View("Result", (object)String.Format("total is {0:c}", total));
    }
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,102评论 0 13
  • 版权声明:本文为 gfson 原创文章,转载请注明出处。注:作者水平有限,文中如有不恰当之处,请予以指正,万分感谢...
    gfson阅读 2,888评论 0 6
  • 亲爱的读者们: 欢迎大家继续阅读并支持我的作品,近日将继续更新。最后祝大家暑假快乐! 在梦想之间 5/31 日 中国
    在梦想之间阅读 105评论 0 0
  • 英国某小镇。 有一个青年人,整日以沿街为小镇的人说唱为生;有一个华人妇女,远离家人,在这儿打工。他们总是在同一个小...
    窗爸爸家居阅读 105评论 0 0
  • 我想写一首诗, 但是, 我不曾见过大海, 也不曾去过草原, 现在没有太阳暖暖地照着, 也没有月光透过枝丫斜斜洒下,...
    非久阅读 273评论 0 1