方法
引用参数
引用参数时,必须在方法的声明和调用时引用ref修饰符
// 方法声明
void ModifyValue(ref int value)
{
value = 5; // 修改原始变量的值
}
// 方法调用
int value = 10;
ModifyValue(ref value); // 打印结果为 value = 5
接口显示调用
如何调用一个对象的方法?
可以通过new一个对象,然后调用这个对象的方法。伪代码如下
public class ClassObjectExample
{
public void Method1()
{
Debug.Log("方法一");
}
}
客户端
var obj = new ClassObjectExample();
obj.Method1();
根据依赖倒置原则(高层模块依赖抽象,底层模块也依赖抽象),我们一般会将通过接口去调用方法。为此我们需要定义方法接口。这样子高层模块就不会依赖于实现,而依赖于抽象。修改后的代码如下:
public interface IMethod
{
void Method1();
}
public class ClassObjectExample : IMethod
{
public void Method1()
{
Debug.Log("方法一");
}
}
但如果一个类实现两个接口,而这两个接口会有同一个签名方法的话,我们就不知道到底是实现哪一个接口的方法?
public interface IMethod
{
void Method1();
}
public interface IMethod2
{
void Method1();
}
public class ClassObjectExample : IMethod, IMethod2
{
public void Method1()
{
Debug.Log("方法一");
}
}
如上面的代码显示,Method1方法到底是实现的是IMethod的方法还是IMethod2的方法。
为此我们可以做一个实验。
IMethod i = new ClassObjectExample();
i.Method1();
IMethod2 i2 = new ClassObjectExample();
i2.Method1();
// 结果
方法一
方法一
通过运行,我们可以得出结论:ClassObjectExample的Method1方法同时实现了两个接口的具体逻辑。
我们也可以通过显示调用的方式,来消除这种问题。但是类实例无法直接访问显式实现的成员,必须通过接口类型的引用来调用。
public interface IMethod
{
void Method1();
}
public interface IMethod2
{
void Method1();
}
public class ClassObjectExample : IMethod, IMethod2
{
void IMethod.Method1()
{
Debug.Log("IMethod的方法");
}
void IMethod2.Method1()
{
Debug.Log("接口2方法");
}
}
通过接口引用来调用
ClassObjectExample obj = new ClassObjectExample();
// obj无法调用对应的方法
IMethod i = new ClassObjectExample();
i.Method1();
IMethod2 i2 = new ClassObjectExample();
i2.Method1();
故可以得出结论:显示调用可以解决接口方法命名冲突问题。
字符串
ToString()方法
将一个数字变成一个标准数字字符串,且不显示小数部分
字符串.ToString("N0");
// 比如:3000=》3,000
// 比如:10000 =》 10,000
将一个数字变成字符串,且限制数字的小数
F表示希望得到的字符串保留1为小数,如果有多为小数,它会四舍五入到一位。如果少于一位小数,则会补零。
(一个数字).ToString("F1");
// number的值为1.234567, 则最终结果为1.2
EndWith方法
判断字符串的结尾是否是指定的字符
string str1 = "FileG";
if(str1.EndWith("G")){
Debug.Log("末尾有大G");
}
TrimEnd方法
移除字符串末尾的指定字符
string str1 = "FileG";
string str2 = str1.TrimEnd("G");
// 输出File
数组
数组无非就是声明、赋值、访问
数组的声明
// 一维数组的声明
datatype[] arrayName;
// 二维数组的声明
string [,] names;
数组的赋值
方式一:声明数组的同时,赋值
double[] balance = { 2340.0, 4523.69, 3421.0};
方式二: 使用new关键字
// int [] marks = new int[5] { 99, 98, 92, 97, 95};
// 可以省略数组的大小
int [] marks = new int[] { 99, 98, 92, 97, 95};
泛型
泛型就是将类型做参数的一门技术,可以将其作为一个类型占位符。
// 传入的T是什么类型,T就是什么类型。所以说它是将类型作为参数
class Test<T>
{
public T value;
}
泛型的分类
1. 泛型类
class Test2<T>
{
public T value;
}
2. 泛型函数(结构:函数名<泛型占用字符>(参数列表))
// 普通类的泛型方法
class Test3
{
public void Test3Fun<T>()
{
}
}
// 泛型类的泛型方法
class Test4<T>
{
public void Test3Fun<K>(K v)
{
}
}
泛型约束
1. 值类型约束
class Animal<T> where T : struct // 限制 T 为值类型,只能是枚举和结构int、float
{
}
2. 引用类型约束
class Aniaml<T> where T : class // 限制 T 为引用类型,只能是类、接口、委托
{
}
3. 公共无参构造方法约束
约束传入的类型T必须有无参构造函数
class Test1<T>where T : new()
{
public T value;
}
4. 类约束
传入的类型只能是Test2类型或者是Test2的子类
class Test2{}
class Test1<T>where T : Test2
{
public T value;
}
5. 接口约束
传入的类型T只能是Imove接口,或者是实现Imove接口的类
interface IMove{}
class Test1<T>where T : IMove
{
public T value;
}
6. 另一个泛型约束
传入的类型T只能是U类,或者是U类的子类
class Test1<T,U>where T : U
{
public T value;
}
7. 多个泛型有约束
class Test1<T,K>where T : class where K : new()
{
public T value;
}
8. 泛型的组合
传入的参数T必须满足2个条件:引用类型的、且必须有无参构造函数
class Test1<T>where T : class,new()
{
public T value;
}
逆变和协变
逆变和协变,都是修饰泛型的。逆变in、协变out
1. in修饰的泛型
<in T>
T只能作为参数,不能作为返回值
public delegate void myFun<in T>(T t);
public delegate int Comparison<in T>(T x, T y);
2. out修饰的泛型
当out修饰T时,T只能被作为返回值
public delegate T myFun<out T>();
3. out和in的组合
T只能被作为参数,K只能被作为返回值
public delegate K myFun<in T, out K>(T t);
面向对象
迭代器
特性
委托、事件、匿名函数、Lamda表达式
容器
ArrayList
常用API
Add
AddRange
Remove
RemoveAt
Clear
List
Stack
Queue
HashTable
Set
HashSet
Dictionary
LinkedList
反射
DateTime类
DateTime.MinValue
返回一个日期为 0001年01月01日 00:00:00
ToString("O")
标准时间格式:yyyy-MM-ddTHH-mm-ss.fffffffK
时区
时区:在一个区域内的标准时间。
UTC:全球统一的时间,通过偏移可以得到时区。
UTC+8:中国的标准时间
UTC-5:美国东部标准时间
解析字符串
DateTime.Parse(日期字符串)
将一个表示日期和时间的字符串解析为DateTime对象
(DateTime对象).Date
Date属性会返回日期对象的部分,yyyy-MM-dd 00:00:00,会忽略时间部分。
AddDays(天数)
日期向后延长得到的DateTime对象
文件系统与IO
拼接路径Path.Combine
将一个路径和多个路径拼接起来,这个方法可以确保路径分隔符正确无误,无论是在 Windows、MacOS 还是 Linux 等不同操作系统上都能正常工作,这使得路径的拼接更加方便和可靠。
string s1 = "hello";
string s2 = "world";
string s3 = Path.Combine(s1, s2);
输出:
TimeSpan类
延迟加载Lazy
当我们需要一个对象的时候,并不立即创建它,而是在第一次访问的时候才创建。这样可以提高程序的性能,特别是当对象的创建成本很高,或者可能在某些情况下根本不需要用到这个对象的时候。这样的话,延迟加载可以节省资源,加快程序启动速度。
构造函数
// 创建Lazy实例,提供初始化逻辑
Lazy<ExpensiveObject> lazyObject = new Lazy<ExpensiveObject>(() => new ExpensiveObject());
// 访问Value属性触发初始化
ExpensiveObject obj = lazyObject.Value;
检测是否已经初始化
if (lazyObject.IsValueCreated)
{
Console.WriteLine("对象已初始化。");
}
评论区