Csharp基本语法速通
Csharp基本语法速通
平台版本
.Net Framework 出得最早,只能用在Windows平台上
微软把.NET Framework 跨平台的部分单独拿出来,就是.NET Standard,它是一套标准库.Net Core就是 以它为标准开发的版本。
之后.Net Core 3.1之后 就改名成统一大版本.Net 5、6、7、8 而 .Net Framework 在4.8.1之后 就没有发过新版本,.Net 6和8 都有3年长期支持年限,速度快、支持跨平台,使用MIT协议开源
也不知道该放哪
装箱比拆箱更耗性能,装箱需要在堆上分配内存 在unity中,脚本是挂载在unity sense下的unity对象上才能执行的? 所有派生自MonoBehaviour的类都可以直接(拖拽)到unity对象上挂载
脚本名必须和类名一样,否则是挂载不上去的
use UnityEngine;
Debug.Log("I am a log"); // 还是常规的日志级别
在unity中使用日志,在unity中的Console窗口中显示,但是右上角有级别过滤,打印不出来的时候看看是不是手贱过滤了
驼峰命名:
int HpValue =1000; // 驼峰+语义,其实驼峰有很多种,C#的习惯来看这样比较好,需要可以再查
int iHpValue= 1000; // 带上了数据类型,有更多信息
char a='a'// char 是单引号,单字符
打印字符串的时候尽量format而不是直接拼接,因为字符串是不可变变量,更占内存
// 常用的前缀:
// str = string
// ch= char
// i = int
// ll = longlong
// b or is =bool true false
C#的数组内存是线性连续的,且虽托管但是直接访问内存 C#的数组长度是整体长度,不是一维的长度,如果要取每个维度的长度,就需要getLength(n)
联合调试的时候首先要在Rider里面进行断点,然后unity里面也要运行程序才进入debug
unity设置个人偏好: play mode的全球变灰很难受直接选全白就不会有影响了,然后注意play的时候上面蓝色的播出箭头 search->play mode->play mode tint
unity中的钩子函数
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class parc9_func : MonoBehaviour
{ public int iHp = 1000; // MonoBehaviour类的public变量可以直接在unity中拖拽修改
// Start is called before the first frame update # 初始化的时候会调用一次 执行完会自动GC一次
void Start()
{
}
// Update is called once per frame # 每一帧都被调用
void Update()
{
}
}
int a1 = 10;
int a2 = 10;
int a3 = a1++; // 先赋值再调用++所以是10
int a4 = ++a2; // 先调用++再赋值所以是11
Console.WriteLine(a3); //10
Console.WriteLine(a4); //11
readonly和const冲突基本自定义类型和自定义类型 readonly不允许非构造函数或声明的时候初始化
const static的区别 const 本质上是保持 必须赋初始值,并且只能通过类来访问不能通过变量访问 static用于实现单例或者缓存等 static分配于堆上,实际上是可以被改变的,也是必须要分配初始值,static只能在static中定义
静态构造函数只被调用一次,无论子类或本类示例化多少次 静态类不允许在外部被实例化,静态类里面的成员必须是静态的,或者常量 无法作为基类派生子类
抽象类abstact的类多层继承的时候是从上往下的继承链去创建的 抽象类中允许虚函数,虚函数和abstract是不能有函数体的
密封类sealed
到此为止,其他的类不能继承此类 是必须实现的类,不能声明为抽象类 密封类的成员函数不得声明为sealed 目的是防止重写接口或者类影像稳定性
大对象(大于85,000字节)会被放在专门的大对象堆中,它们的垃圾回收通常比较耗时。尽量避免创建过多的大对象。
打包的时候所有的dll都被打包进我的bin文件夹里面,我如果在其他的目录通过快捷方式运行我的exe实际上是先把workdir定位到我的程序目录然后运行,运行已编译的可执行文件时,它通常会在其所在的目录作为working dir相对路径寻找依赖项,包括开发时所依赖的 DLL 文件。对于 .NET Core 和 .NET 5+,这种行为是默认的。所以不要使用绝对路径引入自己的DLL
在 C# 中,where
关键字用于在泛型类型或泛型方法中对类型参数进行约束。通过 where
关键字,你可以指定泛型参数必须满足的条件,以确保代码的安全性和可靠性。
where
子句可以用于泛型类、泛型方法和泛型委托中,用来限制泛型参数的类型。以下是一些 where
子句的使用场景:
- 基类约束:
where T : 基类名
,指定泛型参数必须是指定基类或该基类的派生类。 - 接口约束:
where T : 接口名
,指定泛型参数必须实现指定接口。 - 构造函数约束:
where T : new()
,指定泛型参数必须有一个无参数的公共构造函数。 - 值类型约束:
where T : 结构体
,指定泛型参数必须是值类型。 - 引用类型约束:
where T : class
,指定泛型参数必须是引用类型。 - 特定类型约束:
where T : 特定类型
,可以是具体的类名、值类型、接口名等,用来限定泛型参数的类型。
委托多播的时候,类似于责任链,都会执行一遍,可以使用+-和-=给委托加上和减去 委托属于引用类型,引用类型默认为null,如果对委托多播进行了操作,记得判空!!
event智能
CLR在.NET中,静态构造函数或静态变量的初始化是在类第一次被引用时执行的,而不是在程序启动时。这种行为有时被称为“懒汉式”初始化,因为它推迟了对象的创建,直到真正需要时才创建。这种方式可以帮助减少程序启动时的开销,并且可以避免创建那些可能永远不会被使用的对象。
//泛型可以一次性规定多个
public class Pair<TFirst, TSecond>
{
public TFirst First { get; set; }
public TSecond Second { get; set; }
public Pair(TFirst first, TSecond second)
{
First = first;
Second = second;
}
}
Pair<int, string> pair = new Pair<int, string>(10, "hello");
Console.WriteLine($"First: {pair.First}, Second: {pair.Second}");
泛型类也可以被继承,子类也可以扩展
抽象类可以声明构造函数和变量
引用类型是一定存储在堆上的,但是值类型要看所处的命名空间在哪里,参考结构体在类中声明的时候会跟随类被 分配到堆上
字符串判等,判空,拼接,包含,切片 C# 字符串(String) | 菜鸟教程
使用base访问的是父类的方法,this查找的是当前类的属性
在 C# 中,字符串拼接有几种不同的方法,每种方法都有其优缺点,具体取决于你的应用场景和性能需求。以下是几种常见的字符串拼接方式:
字符串运算符
+
- 用法: 使用
+
运算符将多个字符串直接拼接。 - 优点: 简洁易读,适合拼接少量的字符串。
- 缺点: 当拼接大量字符串时,由于字符串的不可变性,每次拼接都会创建新的字符串对象,性能较差。
string result = "Hello" + " " + "World!";
- 用法: 使用
string.Concat
方法- 用法: 调用
string.Concat
来拼接字符串。 - 优点: 与
+
类似,但可以更高效地处理多参数拼接。 - 缺点: 同样因为不可变性,拼接大量字符串时性能较差。
string result = string.Concat("Hello", " ", "World!");
- 用法: 调用
string.Format
方法- 用法: 使用格式字符串和占位符进行拼接。
- 优点: 更适合格式化拼接,能灵活插入不同类型的数据。
- 缺点: 性能不如简单拼接,且在需要拼接大量字符串时开销较高。
string name = "Alice"; int age = 25; string result = string.Format("Name: {0}, Age: {1}", name, age);
StringBuilder
类- 用法: 创建
StringBuilder
对象,通过Append
等方法拼接字符串。 - 优点: 适合需要频繁拼接和修改字符串的场景,性能高。
- 缺点: 相较其他拼接方法,代码稍显复杂。
var builder = new System.Text.StringBuilder(); builder.Append("Hello"); builder.Append(" "); builder.Append("World!"); string result = builder.ToString();
- 用法: 创建
插值字符串 (
$"..."
)- 用法: 使用
$
前缀的字符串,通过{}
插入变量值。 - 优点: 语法简洁,易于理解和维护。
- 缺点: 与
string.Format
类似,在大量拼接或复杂计算的情况下性能较差。
string name = "Alice"; int age = 25; string result = $"Name: {name}, Age: {age}";
- 用法: 使用
总结
- 少量拼接:使用
+
运算符或string.Concat
。 - 格式化拼接:使用
string.Format
或字符串插值。 - 大量拼接或循环拼接:使用
StringBuilder
以获得最佳性能。
如果在父类中定义了一个函数,那么子类可以直接调用这个函数而无需重新定义。这种情况下,子类直接继承了父类的功能。然而,虚函数和重写在面向对象编程中起到关键作用,尤其在实现多态性和动态方法绑定(dynamic binding)方面。
什么是虚函数
虚函数(virtual function)是一种允许子类在继承父类函数的同时,重新定义其行为的机制。在父类中定义虚函数,然后在子类中重写(override),可以让子类提供自己的实现。
为什么需要虚函数和重写
虚函数和重写的关键作用是实现多态性,即在运行时能够根据对象的实际类型调用相应的方法。这在设计灵活的面向对象系统时非常重要。
动态方法绑定
在没有虚函数的情况下,编译器在编译时决定调用哪个方法,这叫做静态方法绑定(static binding)。即使子类定义了一个与父类同名的方法,也不会发生多态行为,调用仍然是基于编译时的类型。
而有了虚函数,编译器会根据实际对象的类型在运行时决定调用哪个方法。这叫做动态方法绑定(dynamic binding)或后期绑定(late binding)。
示例
public class BaseClass
{
public void RegularMethod()
{
Console.WriteLine("Base class method");
}
public virtual void VirtualMethod()
{
Console.WriteLine("Base class virtual method");
}
}
public class DerivedClass : BaseClass
{
public void RegularMethod()
{
Console.WriteLine("Derived class regular method");
}
public override void VirtualMethod()
{
Console.WriteLine("Derived class overridden virtual method");
}
}
在这个示例中,RegularMethod
在子类中重写,但不是虚函数。这意味着如果使用父类引用访问子类对象,调用的仍然是父类的RegularMethod
。
而VirtualMethod
是虚函数,子类通过override
重写了这个方法。如果使用父类引用访问子类对象,调用的将是子类的VirtualMethod
。
测试虚函数行为
BaseClass baseClass = new BaseClass();
DerivedClass derivedClass = new DerivedClass();
BaseClass baseRefToDerived = new DerivedClass();
// 静态方法绑定,调用父类的 RegularMethod
baseRefToDerived.RegularMethod(); // 输出 "Base class method"
// 动态方法绑定,调用子类的 VirtualMethod
baseRefToDerived.VirtualMethod(); // 输出 "Derived class overridden virtual method"
在这个示例中,baseRefToDerived
是父类类型的引用,但指向子类实例。如果没有虚函数和重写,调用RegularMethod
和VirtualMethod
会根据引用类型,而不是实际对象的类型。这会导致行为与预期不符。
结论
虚函数和重写在实现多态性方面至关重要。它们使得在运行时根据对象的实际类型调用正确的方法成为可能,而不依赖于编译时的静态绑定。通过使用虚函数,面向对象编程可以更灵活、更易于扩展。
直接打印对象的时候打印出来的是什么
在 C# 中,直接打印对象时,会调用对象的 ToString()
方法来获取对象的字符串表示形式,然后将其输出到控制台。
如果你没有为特定类型的对象提供自定义的 ToString()
方法,那么将会使用默认的实现。默认情况下,ToString()
方法返回对象的类型的完全限定名。
例如,如果你有一个名为 MyClass
的类,如果你直接打印一个 MyClass
类的实例,它将会输出类的完全限定名,如下所示:
using System;
class MyClass
{
// 类的成员
}
class Program
{
static void Main()
{
MyClass obj = new MyClass();
Console.WriteLine(obj); // 输出:命名空间.MyClass
}
}
如果你想要自定义对象的字符串表示形式,你可以重写 ToString()
方法,并在其中返回你希望打印出的字符串:
using System;
class MyClass
{
public override string ToString()
{
return "This is MyClass";
}
}
class Program
{
static void Main()
{
MyClass obj = new MyClass();
Console.WriteLine(obj); // 输出:This is MyClass
}
}
通过重写 ToString()
方法,你可以为对象提供更有意义的字符串表示形式,以便于调试和输出。
游戏的暂存
【不是特别重要,进阶可以稍微看一看《CLR Via C#》 4.1:所有类型都从 System.Object 派生-哔哩哔哩】《CLR Via C#》 12.1.5:泛型参数的逆变和协变_哔哩哔哩_bilibili
unity最完美的检测脚底板是否贴地代码_哔哩哔哩_bilibili Mono内存问题 【可以找找某些经验 为了做游戏,我花了10年自学程序、美术、音乐、策划-哔哩哔哩】 https://b23.tv/IIyzwjo 这个人水平应该还行 unity引擎程序员职业规划_哔哩哔哩_bilibili
引擎的使用不仅要熟悉常用的组件和AIP, 输入系统,UI系统,动画系统,导航系统,资源管理,渲染系统这些最好也能掌握(这个是入行的技能要求了) 另外你现在是有项目实战能力的, 可以查漏补缺一下常用的系统模块(背包,商店,道具,装备,小地图,任务,NPC, 战斗可以等等,时间充裕的话也可以一并开发)