Files
Obsidian_Unity/CSharp学习/CSharp入门查漏补缺.md
T
2026-05-03 14:06:26 +08:00

6.2 KiB
Raw Blame History

一.转义字符

常用的转义字符有:

1.斜杠单引号,表示单引号 例子 string str="\'哈哈哈\' " 2.斜杠双引号,表示双引号 例子 string str="\\'哈哈哈\\' " 3.斜杠+n表示换行 4.双斜杠表示单斜杠

不常用转义字符:

1.制表符t 相当于按下tab,空4格 2.光标退格字符 b,是光标位置向后推一个 3.空字符0 无实际效果 4.警报音 a 系统发出提示音

取消转义字符

在字符串前面加上@,将所有转义字符按照字面显示

二:类型转换

隐式类型转换

小转大,不会影响精度,可以自动转换

显示类型转换

1. 括号强转
2.Parse法

将string转成目标类型

3.Convert法
4.其他类型转string ToString()方法

三:枚举

枚举是一组命名的整型常量 它的本质是整数类型的包装,枚举在编译后会被编译为一个密封类(sealed class,继承自 System.Enum(而 System.Enum 又继承自 System.ValueType),因此枚举是值类型。 每个枚举成员本质上是一个常量整数,默认从 0 开始递增(也可手动指定值)。例如:

public enum Time { yi, er, san }

编译后等价于

public sealed class Time : Enum
{ 
	public const int yi = 0;
	public const int er = 1;
	public const int san = 2; 
}

四:接口

常规的接口声明是这样的

interface ICanRun  
{  
    void Run();  
}

我们不需要显式的去写访问修饰符,接口默认的是internal,程序集可见 内部的成员都是public的,继承了接口的方法也是public. 继承了接口的类,他需要实现接口提供的契约方法

public class Dog : ICanRun  
{  
    public void Run()  
    {    
		
    }
}

以上是我们接口中的常规写法,但是会有一些问题出现. 假如有两个接口,他们内部都有一个Run方法

interface ICanFastRun  
{  
    void Run();  
}
interface ICanSlowRun  
{  
    void Run();  
}

那么我们的Dog继承了两个接口,这时候在使用常规的方法去实现接口方法,就会出现一些问题. 我们需要新的方法去解决问题. ==显式接口实现== 我们的Dog在同时继承了两个接口的时候,实现方法应该用显示接口实现

public class Dog:ICanFastRun, ICanSlowRun  
{  
    void ICanSlowRun.Run()  
    {        
	    Console.WriteLine("我可以跑的很慢");  
    }  
    void ICanFastRun.Run()  
    {        
	    Console.WriteLine("我可以跑的很快");  
    }    
}

显示接口实现是非常清晰易懂的,让我们知道这个是那个接口的那个方法,但是他会有一些问题, ==我们不能直接在Dog变量里面调用Run方法,需要将Dog强转为接口类型==

class Program  
{  
    static void Main(string[] args)  
    {        
	    Dog dog = new Dog();  
        dog.Run(); //会报错
    }
}

这就是麻烦的地方,我们需要对Dog进行类型转换才可以调用对应的Run方法

class Program  
{  
    static void Main(string[] args)  
    {        
	    Dog dog = new Dog();  
        ((ICanFastRun)dog).Run();  //将dog转换为ICanFastRun类型,可移植性跑得快的Run方法
        ((ICanSlowRun)dog).Run();  //将dog转换为ICanSlowRun类型,可移植性跑得慢的Run方法
    }
}

显示接口实现,从另一层面来讲,也是对方法的一种隐藏,对API的一种清洁.

五:多态中的类型

public class Animal  
{  

}  
  
public class Dog : Animal  
{  

}  
  
public class Cat : Animal  
{  

}
class Program  
{  
    static void Main(string[] args)  
    {        
	    Animal animal = new Animal();  
        Animal dog = new Dog();  
        Animal cat = new Cat();  
    }
}

在上述代码当中请问animal,dog,cat他们三个都是什么类型? 答案是两个,他们即是Animal又是Dog(Cat),他们两者兼备 这是需要区分情况的

==在编译时==

他们三者都是Animal类型的

==在运行时==

他们是new 后面的类型 dog是Dog类型 cat是Cat类型

六:多态中的new关键字

new关键字在类当中用来隐藏基类的方法 在继承链当中,他会影响运行时多态

public  class Animal  
{  
    public virtual void Eat()  
    {        Console.WriteLine("我正在吃");  
    }
}  
  
public class Dog : Animal  
{  
    public new void Eat()  
    {        
	    Console.WriteLine("狗正在吃");  
    }
}  
class Program  
{  
    static void Main(string[] args)  
    {        
	    Animal animal = new Animal();  
        Animal dog1 = new Dog();  
        Dog dog2 = new Dog();  
        animal.Eat();  
        dog1.Eat();  
        dog2.Eat();  
    }
}

上面代码的输出结果是

我正在吃
我正在吃
狗正在吃

关键点在于Animal dog1 = new Dog(); dog1究竟输出的是什么呢? 答案是"我正在吃" 我们要研究为什么他会这样输出

1. 编译阶段:编译器只认 “声明类型”,确定要调用的方法​

dog1的声明类型是Animal(=左边),编译器在编译时会做两件事:​

- 检查Animal类中是否有Eat()方法:发现Animal有public virtual void Eat()(虚拟方法),符合调用条件,编译通过;​

- 记录 “要调用的是Animal类的Eat()方法”:因为Dog类的Eat()用new修饰,编译器会将其视为 “子类新增的独立方法”,而非对Animal.Eat()的重写,所以不会将dog1与Dog.Eat()关联。​

简单说:编译时,编译器认为dog1是Animal类型,只能调用Animal的方法,根本 “看不到”Dog类用new隐藏的Eat()。​

2. 运行阶段:CLR 执行 “编译时确定的方法”​

运行时,dog1的实际类型是Dog=右边new Dog()),但 CLR 的执行逻辑受new关键字影响:​

- 若子类用override重写:CLR 会优先执行 “实际类型(Dog)的重写方法”(多态生效);​

- 若子类用new隐藏:CLR 会执行 “编译时确定的父类方法(Animal.Eat())”,因为new修饰的方法与父类方法无关联,CLR 不会去子类中找这个 “独立方法”。​

所以,运行时dog1.Eat()最终执行的是Animal类的Eat(),输出 “我正在吃”。

简单的说,new隐藏了方法之后,他在编译时是什么类型,就执行谁的方法.