C#学习

感觉还是得再学习下C#
C# .net源码
core源码
开始!

调试Debug

直接在VSCode里设置断点在Unity里不生效
找到一个帖子使用VSCode进行Unity开发也不行
似乎很麻烦,VSCode也不怎么支持了,先这样吧

构成C#语言的基本元素

  • 关键字 add as if 。。。。。。
  • 操作符 + = - 。。。。。。。
  • 标识符 起的名 规则如下

标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。
标识符中的第一个字符不能是数字。 标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } .
; : " ’ / \。
标识符不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if是关键字。
标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。
不能与C#的类库名称相同。

  • 标点符号 ; {} ()
  • 文本(字面值)等号右侧的东西
    整数 int long
    实数 float double
    字符 char
    字符串 string
    布尔 bool
    空 null
  • 注释与空白 // /**/ ///

运算符优先级

在这里插入图片描述

类型

懒得写了,直接看吧C#数据类型

敲重点:动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的

属性

声明快捷方式:prop
属性可以在访问时实时计算出属性值,而字段是需要主动去进行计算
根据使用场景来判断使用哪个
建议:永远使用属性(而不是字段)来暴露数据

// 声明类型为 int 的 Age 属性
private int age;
public int Age
{
   get
   {
      return age;
   }
   set
   {
      age = value;
   }
}

索引器

声明快捷方式:indexer

参数传递

  • 值参数
    就是正常的参数传递
  • 引用参数
public void swap(ref int x, ref int y){}

参数传递的是使用传进来变量的内存位置获取值,所以参数改变原变量也会变

  • 输出参数
    在引用参数的基础上,传入的参数可以不初始化赋值
public void getValues(out int x, out int y ){
	Console.WriteLine("请输入第一个值: ");
    x = Convert.ToInt32(Console.ReadLine());
    Console.WriteLine("请输入第二个值: ");
    y = Convert.ToInt32(Console.ReadLine());
}
static void Main(string[] args)
{
   NumberManipulator n = new NumberManipulator();
   /* 局部变量定义 */
   int a , b;
   
   /* 调用函数来获取值 */
   n.getValues(out a, out b);

   Console.WriteLine("在方法调用之后,a 的值: {0}", a);
   Console.WriteLine("在方法调用之后,b 的值: {0}", b);
   Console.ReadLine();
}

数组参数

不确定参数个数时可以这样用

public int AddElements(params int[] arr){}
// 调用
app.AddElements(512, 720, 250, 567, 889);
// 或者
app.AddElements(new int[]{512, 720, 250, 567, 889});

可空类型

  • float? num1; // 默认值为null 可以表示float范围内值再加一个null
  • num1 = num2 ?? 6.66; // num2 如果为空值则返回 6.66

数组

这块有点懵逼

二维数组

可以理解为一个矩阵表格,元素长度要一致

 int[,] a = new int[5, 2] {{0,0}, {1,2}, {2,4}, {3,6}, {4,8} };
 int i, j;
 /* 输出数组中每个元素的值 */
 for (i = 0; i < 5; i++)
 {
     for (j = 0; j < 2; j++)
     {
         Console.WriteLine("a[{0},{1}] = {2}", i, j, a[i,j]);
     }
 }

交错数组

数组的数组
每个元素长度可以不同
交错数组为一维数组

int[][] a = new int[][]{new int[]{0,0},new int[]{1,2},
            new int[]{2,4},new int[]{ 3, 6 }, new int[]{ 4, 8 , 6} };
int i, j;
/* 输出数组中每个元素的值 */
for (i = 0; i < 5; i++)
{
    for (j = 0; j < 2; j++)
    {
        Console.WriteLine("a[{0}][{1}] = {2}", i, j, a[i][j]);
    }
}

密封类

不能被继承的类

sealed class Person {}

抽象类

  • 抽象类不能被实例化
  • 不能用sealed修饰符修饰抽象类
  • 抽象类方法不能是静态的
  • 抽象方法不能标记为virtual
  • 抽象类可以有构造方法,但构造方法不能直接被调用而只能由派生类的构造方法调用
// 抽象方法
abstrace class Person {
	public abstract void Speak();
}
// 派生方法
class Student: Person {
	public override void Speak(){
		Console.WriteLine("我是学生");
	}
}

接口

  • 接口中只能声明方法、属性、索引器和事件
    返回值类型 方法名(参数);
  • 接口不能声明字段、构造方法、常量和委托
  • 一个类既可以派生自另一个类还可以同时实现某个接口
  • 一个类可以同时实现多个接口
    class PrintHP:Print, IPrint, IScan {}

接口作为方法的参数

// 前情提要:接口IPrint由两个类实现PrintHp和PrintIBM
IPrint iHP = new PrintHP();
IPrint iIBM = new PrintIBM();
PrintPhoto(iHP);

// 接口作为参数
public static void PrintPhoto(IPrint i) {
	// 可以根据传入的不同对象进行不同的打印方法,实现了多态
	i.Print();
}

接口的绑定

接口继承接口
可以把不同接口合在一起称为一个新的接口

interface ISumSport: ISport, ILalaDui {}

As和Is

当不确定一个对象是否为某一个类型时

  • is
    测试一个实例是不是另一个类型
  • as
    测试一个实例是不是另一个类型,并且把实例转换为另一种类型,如果转换不成功则返回null

抽象类和接口

接口抽象类
相同点
都不能被实例化
包含有未实现的方法
子类必须实现所有未实现的方法
不同点
interface关键字abstrace关键字
子类可以实现多个接口子类只能继承一个抽象类
直接实现方法使用override关键字实现

多态

虚方法

父类中需要子类去实现的方法

// 虚方法
public virtual int area()
{
   Console.WriteLine("父类的面积:");
   return 0;
}

// 子类中重写虚方法
public override int area ()
{
   Console.WriteLine("Rectangle 类的面积:");
   return (width * height);
}

虚方法和抽象方法

虚方法抽象方法
用virtual修饰用abstract修饰
要有方法体不允许有方法体
可以被子类override必须被子类override
除了密封类都可以写只能在抽象类中

委托

  • 委托是一个类
  • 主要作用是将方法作为方法的参数
    疑问?还是不太理解有什么作用,我直接调用AddCompute方法也可以
/// <summary>
/// 用委托实现一个计算器功能
/// </summary>
/// <returns></returns>
//定义委托
public delegate double Calculator(double num1, double num2);
class Program
{
    static void Main(string[] args)
    {
        //实例化委托
        Calculator cal = new Calculator(AddCompute);
        // 使用委托 
        double res = cal(2.4, 34.5); // 36.5
    }
    // 定义方法 (一般是先定义方法再定义委托 委托的参数和方法参数必须保持一致)
    public static double AddCompute(double num1, double num2)
    {
        return num1 + num2;
    }

}

匿名方法

  • 实例化委托时直接将方法体放在后面
  • 不需要指定匿名方法的返回值类型
    修改委托的例子
// 匿名方法(实例化委托+定义方法)
Calculator cal = delegate (double num1,double num2) {
    return num1 + num2;
};

事件

用户双击了鼠标,点击了关闭按钮,滚动滚轮。。。。。。都是事件
类似之前学习的订阅发布者模式,事件源不需要知道谁来响应自己发布任务就好,订阅者不需要知道什么时候触发事件知道怎么响应就好,实例中的主函数就是事件控制中心

  • 可以把事件看成委托的容器
  • 事件要素 (以学校类上课事件举例)
    – 事件源(老师)
    – 订阅者 (学生)
    – 订阅者处理事件 (学生回到座位安静坐好)
    – 要素间的关系 (老师说上课了,学生去上课)
/// <summary>
/// 上课事件
/// </summary>

//定义委托 (习惯在事件委托后加Handler)
public delegate void TeachHandler();

// 事件源
class Teacher
{
    // 定义事件
    // [访问修饰符] event 委托名 事件名;
    public event TeachHandler TeachEvent;
    // 定义方法
    public void BeginClass()
    {
        Console.WriteLine("老师:上课啦!!!!!!!");

        // 处理事件
        if (TeachEvent != null)
        {
            // 执行事件
            TeachEvent();
        }
    }

}

// 订阅者
class Student
{
    string name;
    public string Name { get; set; }
    public Student(string name)
    {
        this.name = name;
    }

    // 定义方法
    public void SleepListener()
    {
        Console.WriteLine("{0}在课堂上睡觉zzzzzz", name);
    }
    public void GoodListener()
    {
        Console.WriteLine("{0}在课堂上认真听课", name);
    }
    public void VeryGoodListener()
    {
        Console.WriteLine("{0}在课堂上一边认真听课一边记笔记", name);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Teacher t = new Teacher();
        Student s1 = new Student("张三");
        Student s2 = new Student("李四");
        Student s3 = new Student("王五");

        // 订阅事件
        // 事件名 += new 委托(事件处理方法名)
        t.TeachEvent += new TeachHandler(s1.SleepListener);
        t.TeachEvent += new TeachHandler(s2.VeryGoodListener);
        t.TeachEvent += new TeachHandler(s3.GoodListener);

        // 触发事件
        t.BeginClass();
        /* Console
            老师:上课啦!!!!!!!
            张三在课堂上睡觉zzzzzz
            李四在课堂上一边认真听课一边记笔记
            王五在课堂上认真听课
        */
    }
}

泛型集合

  • List<T> 对应 ArrayList
  • Dictionary<T> 对应 HashTable
  • 明确指定放入集合中对象的数据类型
  • 优点
    – 类型安全,编译期间泛型集合会检查放入的对象数据类型
    – 提高性能,存取数据时不会发生类型转换,特别是存取值类型时不会发生装箱和拆箱操作

List

class Study
{
    static void Main(string[] args)
    {
        // 泛型集合
        // List<数据类型> 实例名 = new List<数据类型>()
        List<Student> studentList = new List<Student>();
        Student s1 = new Student("张三", 1);
        Student s2 = new Student("李四", 2);
        Student s3 = new Student("王五", 3);
        Student s4 = new Student("赵六", 4);
        studentList.Add(s1);
        studentList.Add(s2);
        studentList.Add(s3);
        studentList.Add(s4);

        //展示所有学生
        studentList.ForEach(delegate (Student s)
        {
            Console.WriteLine("学号{0},姓名{1}", s.Name, s.Id);
        });
        // 获取所有学号大于2的学生
        List<Student> newStuList =  studentList.FindAll(delegate (Student s)
        {
            return s.Id > 2;
        });
    }
}

class Student
{
    public string Name { get; set; }
    public int Id { get; set; }

    public Student()
    {

    }
    public Student(string name, int id)
    {
        this.Name = name;
        this.Id = Id;
    }
}

Dictionary

// Dictionary<键数据类型,值数据类型> 实例名 = new Directionary<键数据类型,值数据类型>()
Dictionary<string, int> propValueDir = new Dictionary<string, int>
{
    { "生命值", 100 },
    { "法力值", 194},
    { "心情值", 10}
};
propValueDir.Add("防御值", 4000);
Console.WriteLine(propValueDir.Count); // 4

foreach (var propName in propValueDir.Keys)
{
    Console.WriteLine("{0}:{1}", propName, propValueDir[propName]);
}
/*
    生命值:100
    法力值:194
    心情值:10
    防御值:4000
*/

对象和集合的初始化器

  • 对象初始化器 不再需要构造函数去赋值
class Study
{
    static void Main(string[] args)
    {
        // 对象初始化器
        // 实例化学生对象
        Student s1 = new Student{ Id = 1, Name = "刘六"};
    }
}

class Student
{
    public string Name { get; set; }
    public int Id { get; set; }
}
  • 集合初始化器
    好巧,就是上节课代码提示给的
// 初始化器
Student s1 = new Student("张三", 1);
Student s2 = new Student("李四", 2);
Student s3 = new Student("王五", 3);
Student s4 = new Student("赵六", 4);
List<Student> studentList = new List<Student>{
    s1,s2,s3,s4
};

Dictionary<string, int> propValueDir = new Dictionary<string, int>
{
    { "生命值", 100 },
    { "法力值", 194},
    { "心情值", 10}
};

Lambda表达式

  • 是一个匿名函数
// 数数组里有多少奇数
int[] numbers = { 23, 525, 66, 32, 65, 88, 456, 83, 36 };
int count = numbers.Count((n) =>
{
    return n % 2 == 1;
});
// 或者 简写
count = numbers.Count(n => n % 2 == 1);
Console.WriteLine("数组中奇数个数为:{0}", count);

预处理器

以#开头的一行命令,在编译之前进行处理
似乎是用于调试或者版本控制之类的
和使用正常控制的区别是可以避免某些代码进行编译
菜鸟教程

异常

  • 请勿将try/catch处于控制流
  • 用户只能处理catch异常
  • 不得声明空catch块
  • 避免在catch块内嵌套try/catch
  • 只有使用finally块才能从try语句中释放资源

try catch finally

一般先catch特殊异常,最后放Exception

try {
	// 程序代码
} catch (异常类型 参数名) {
	// 异常处理
} catch (另一个异常类型 参数名) {
	// 异常处理
} finally {
	// 清楚内存等
}

throw

抛出异常 可以是系统异常也可以是自定义异常

class HelloWorld
{
    static void Main(string[] args)
    {
        try
        {
            int divisor = 0;
            if (divisor == 0)
            {
                throw new MyException("除数为0?");
            }
        }
        catch (MyException ex)
        {

            Console.WriteLine("出错啦!错误信息:" + ex.Message);
        }
    }
}

// 自定义异常
class MyException : ApplicationException
{
    // 异常信息继承于父类异常信息
    public MyException(string message) : base(message) { }
}

小知识

  • 前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,没有前缀则表示十进制。
  • int i; //默认值0
    int? ii; //默认值null
  • 类的默认修饰符号是internal,接口的默认修饰符号是public
  • var弱类型 类似object 性能比object高点
  • using System.Threading;
    Thread.Sleep(2000); // 进程暂停两秒
  • 按F12可以查看定义代码

递归

using System;

namespace CalculatorApplication
{
    class NumberManipulator
    {
        public int factorial(int num)
        {
            /* 局部变量定义 */
            int result;

            if (num == 1)
            {
                return 1;
            }
            else
            {
                result = factorial(num - 1) * num;
                return result;
            }
        }
   
        static void Main(string[] args)
        {
            NumberManipulator n = new NumberManipulator();
            //调用 factorial 方法
            Console.WriteLine("6 的阶乘是: {0}", n.factorial(6));
            Console.WriteLine("7 的阶乘是: {0}", n.factorial(7));
            Console.WriteLine("8 的阶乘是: {0}", n.factorial(8));
            Console.ReadLine();

        }
    }
}

修饰符

  • public 全部都可访问
  • internal 同一程序集可以访问
  • protected 子类可以访问
  • private 只有自己可以访问
  • sealed 标识类不能被继承或方法不能被重写
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值