C# 学习笔记

字符串转Enum

  string value = defaultValue;
        
  KeyCode code = (KeyCode)Enum.Parse(typeof(KeyCode),value);

   匿名函数:

 public class NamespaceSettingsData
    {
        private readonly static string NAMESPACE_KEY = Application.productName + "@NAMESPACE";
        
        public static string Namespace
        {
            get
            {
                var retNamespace = EditorPrefs.GetString(NAMESPACE_KEY, "DefaultNamespace");
                 
                return string.IsNullOrWhiteSpace(retNamespace) ? "DefaultNamespace" : retNamespace;
            }
            set => EditorPrefs.SetString(NAMESPACE_KEY,value);
        }

        public static bool IsDefaultNamespace => Namespace == "DefaultNamespace";
    }

統計代碼行數:

C#如何统计代码行数_c# 查看代码总行数_juliazhan的博客-CSDN博客

继承自某个接口的抽象类,如果抽象方法和接口里面需要实现的方法一致,可以不需要实现接口里面的同名方法。如:

public interface IClear
{
    void Clear();
}

public abstract class BaseEventArgs : IClear
{
    public abstract void Clear();
}

List<T>Sort排序:

默认List的排序是升序排序

如果要降序排序,也很简单,只需要在前面加一个负号(使用lambda表达式,在前面加个负号就是降序了)

List<int> tmp = new List<int>(){5,1,22,11,4};
tmp.Sort((x, y) => -x.CompareTo(y));

非基本类型排序:

class Hero
{
    public int age;
    public string name;

    public Hero(int age, string name)
    {
        this.age = age;
        this.name = name;
    }
}
class Main
{
    public void Test()
    {
        List<Hero> personList = new List<Hero>();
        personList.Add(new Hero(25, "cxm"));
        personList.Add(new Hero(24, "zqr"));

        //利用匿名方法对list排序(单权重升序)
        personList.Sort(delegate (Hero h1, Hero h2)
        {
            return h1.age.CompareTo(h2.age);
        });
        // 利用Lambda方法对list排序(单权重升序)
        personList.Sort((Hero h1, Hero h2) =>
        {
            return h1.name.CompareTo(h2.name);
        });
    }
}

Dispose

方法本身是用来让你放置资源清理代码的。显然,一个空方法并不代表清理工作本身,真正执行清理工作的是你具体的代码。需要提前进行 dispose 的一定是有原因的,它必须手动 Dispose,或者至少不能等上几秒钟去等待 GC 回收。所有组件基本上都会在 GC 实际释放对象实例之前自动调用 dispose,手动调用 dispose 是(瞬间)阻塞了程序,但是假设有原因必须提前手动调用dispose那么就必须手动调用。而此原因则各不相同,根本不存在什么空洞的“一般”

一般using包含,就是指用完就dispose了
这样好处是不用等gc清理就可以进行释放

在C#中,托管资源的垃圾回收是通过CLR的Garbage Collection来实现的,Garbage Collection会调用堆栈上对象的析构函数完成对象的释放工作;而对于一些非托管资源,比如数据库链接对象等,需要实现IDisposable接口进行手动的垃圾回收。

C#中IDispose接口的实现方法以及为什么这么实现?_实现idispose接口后要显示调用disopose方法吗_张Da夫的博客-CSDN博客

一. 静态常量和动态常量

1.静态常量 : 所谓静态常量就是在编译期间会对变量进行解析,再将常量的值替换成初始化的值。
                      静态常量(Const)是指编译器在编译时候会对常量进行解析,并将常量的值替换成初始化的那个值。

2.动态常量 : 所谓动态常量就是编译期间会将变量标记只读常量,而不用常量的值代替,这样在声明时可以不初始化,可以延迟到构造函数初始化。
                      动态常量(Readonly)的值则是在运行的那一刻才获得的,编译器编译期间将其标示为只读常量,而不用常量的值代替,这样动态常量不必在声明的时候就初始化,而可以延迟到构造函数中初始化。

二. const

1.使用 const 关键字来声明某个常量字段或常量局部变量。常量字段和常量局部变量不是变量并且不能修改。 常量可以为数字、布尔值、字符串或 null 引用(Constants can be numbers, Boolean values, strings, or a null reference)。

2.不允许在常数声明中使用 static 修饰符。如果我们在const修饰的常量前加static的话,会提示错误,因为const编译后就是static常量了。

const修饰的常量是上述中的第一种,即静态常量,而readonly是上述中第二种即动态常量。他们的区别可以从静态常量和动态常量的特性来说明:

  • const修饰的常量在声明时必须初始化值;readonly修饰的常量可以不初始化值,且可以延迟到构造函数。
  • cons修饰的常量在编译期间会被解析,并将常量的值替换成初始化的值;而readonly延迟到运行的时候。
  • const修饰的常量注重的是效率;readonly修饰的常量注重灵活。
  • const修饰的常量没有内存消耗;readonly因为需要保存常量,所以有内存消耗。
  • const只能修饰基元类型、枚举类、或者字符串类型;readonly却没有这个限制。

     

3.常量默认是静态类型,所以不能和static一起使用

4.常量是在编译时计算的,它的表达式中不能包含变量:

       如  const  int a= b+1; 这样编译不能通过的

      如果 const int b=1; const int a=b+1;  这样是正确的

5.常量在编译时已经计算复制了,在程序运行过程中 ,不允许被修改的!

6.常量当被跨程序集直接引用的时候,常量的值会被保存到引用的程序集中,当下次该常量被修改时,只编译常量所在的项目,引用该常量的程序集未编译的话,则引用该常量程序集的常量值不会改变,还是之前的常量值!
7.const对于引用类型的常数,可能的值只能是string和null。readonly可以是任何类型。

8. Const修饰的常量在声明的时候必须初始化;Readonly修饰的常量则可以延迟到构造函数初始化 。

9. Const常量既可以声明在类中也可以在函数体内,但是Static Readonly常量只能声明在类中。Const是静态常量,所以它本身就是Static的,因此不能手动再为Const增加一个Static修饰符。

10. Const修饰的常量在编译期间就被解析,即:经过编译器编译后,我们都在代码中引用Const变量的地方会用Const变量所对应的实际值来代替; Readonly修饰的常量则延迟到运行的时候。

二. readonly

 1.readonly修饰的在构造函数中被赋值后就不可以改变。

 2.readonly是动态常量,在编译期间是不会解析的,所以开始就是默认值


3.ReadOnly 变量是运行时变量,它在运行时第一次赋值后将不可以改变。其中“不可以改变”分为两层意思:

  1. 对于值类型变量,值本身不可以改变(Readonly, 只读)
  2. 对于引用类型变量,引用本身(相当于指针)不可改变。

4. 对于readonly的一个变量,如果是值类型的,那么是这个变量的写操作是受限制的,如果是一个引用类型,那么保存的是这个变量的内存地址,对这个引用的写操作是受限制的,但是对于这个变量里面的成员的读写操作是不受限制的

5.readonly常量是字段,只能在定义类的构造函数内修改(或者变量初始化器),派生类的构造函数不可以!

Const和Readonly的最大区别(除语法外)
Const的变量是嵌入在IL代码中,编译时就加载好,不依赖外部dll(这也是为什么不能在构造方法中赋值)。Const在程序集更新时容易产生版本不一致的情况。
Readonly的变量是在运行时加载,需请求加载dll,每次都获取最新的值。Readonly赋值引用类型以后,引用本身不可以改变,但是引用所指向的实例的值是可以改变的。在构造方法中,我们可以多次对Readonly赋值。

静态常量(Const)和动态常量(Readonly)之间的区别

静态常量(Compile-time Constant)

动态常量(Runtime Constant)

定义

声明的同时要设置常量值。

声明的时候可以不需要进行设置常量值,可以在类的构造函数中进行设置。

类型限制

只能修饰基元类型,枚举类型或者字符串类型。

没有限制,可以用它定义任何类型的常量。

对于类对象而言

对于所有类的对象而言,常量的值是一样的。

对于类的不同对象而言,常量的值可以是不一样的。

内存消耗

无。

要分配内存,保存常量实体。

综述

性能要略高,无内存开销,但是限制颇多,不灵活。

灵活,方便,但是性能略低,且有内存开销。

常量与Readonly字段在c#中的10个主要区别

C#中的常量C#中Readonly
const关键字可以应用于字段或局部变量readonly关键字只应用于字段而不是局部变量
我们必须在公开的时候分配常量字段我们可以在声明或构造函数时指定readonly字段,而不是在任何其他方法中。
没有分配内存,因为在编译后,在IL代码中嵌入了常量值为Readonly字段分配的动态内存,可以在我们运行时获得值。
常量在c#中是默认静态的。只能通过类名访问Readonly属于需要过类实例访问的对象。要使它成为类成员,我们需要在readonly之前添加static关键字。
我们可以声明如下所构建的(基本类型)数据类型为常量 Boolean,Char, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal和string.一样不变
值是常量(因为它属于类)根据使用的构造函数(因为它属于类的对象),其值可能会有所不同
如果我们想要对某些类(非原始类型)声明常量,我们应该将其赋值为null,但是这是没有用的。如果声明一个非基本类型(引用类型),readonly只有引用是不可变的,而不是它包含的对象。(见下面的例子)
不要使用可能导致dll版本问题时发生变化的const字段(参见示例)当在运行时获得的值时,没有dll版本控制问题 Static ReadOnly字段的Const字段不能作为ref或out参数传递

C#中Readonly和Static Readonly:

以下是C#中readonly和static readonly字段之间的主要区别。

C#中的ReadonlyC#中的Static Readonly
可以在声明或构造函数的时候分配在声明或静态构造函数时可以分配
根据使用的构造函数,值可能会不同初始化后值将是常量

在C#中何时使用常量和readonly

当值是绝对不变的时候,使用常量,这在时间上是不变的。例如一周的天数是7。这始终是常数。而在使用static readonly时,要避免dll版本问题。

由于在IL内嵌有不变的值,我们可以使用常量修饰符来获得性能上的好处。

如果我们想要对类(或对象)的不同实例使用不同的常量值,请使用readonly。

四. Static

一、静态类

  静态类与非静态类的重要区别在于静态类不能实例化,也就是说,不能使用 new 关键字创建静态类类型的变量。在声明一个类时使用static关键字,具有两个方面的意义:首先,它防止程序员写代码来实例化该静态类;其次,它防止在类的内部声明任何实例字段或方法。

  1、静态类的主要特性:

  [1] 仅包含静态成员。

  [2] 无法实例化。

  [3] 静态类的本质,是一个抽象的密封类,所以不能被继承,也不能被实例化。

  [4] 不能包含实例构造函数。

  [5] 如果一个类下面的所有成员,都需要被共享,那么可以把这个类定义为静态类。

  2、静态类与私有构造函数区别:

  [1] 私有构造器方式仍然可以从类的内部对类进行实例化,而静态类禁止从任何地方实例化类,其中包括从类自身内部。

  [2] 使用私有构造器的类中,是允许有实例成员的,编译器不允许静态类有任何实例成员。

  [3] 使用静态类的优点在于,编译器能够执行检查以确保不致偶然地添加实例成员,编译器将保证不会创建此 类的实例。

  [4] C#编译器会自动把它标记为sealed。这个关键字将类指定为不可扩展;换言之,不能从它派生出其他类。

二、静态成员

  1、通过static关键字修饰,是属于类,实例成员属于对象,在这个类第一次加载的时候,这个类下面的所有静态成员会被加载。

  2、静态成员只被创建一次,所以静态成员只有一份,实例成员有多少个对象,就有多少份。

  3、类加载的时候,所有的静态成员就会被创建在“静态存储区”里面,一旦创建直到程序退出,才会被回收。

  4、成员需要被共享的时候,方法需要被反复调用的时候,就可以把这些成员定义为静态成员。

  5、在静态方法中,不能直接调用实例成员,因为静态方法被调用的时候,对象还有可能不存在。

  6、this/base 关键字在静态方法中不能使用,因为有可能对象还不存在。

  7、可以创建这个类的对象,制定对象的成员在静态方法中操作。

  8、在实例方法中,可以调用静态成员,因为这个时候静态成员肯定存在。

  9、非静态类可以包含静态的方法、字段、属性或事件;

  10、无论对一个类创建多少个实例,它的静态成员都只有一个副本;

  11、静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例成员;

  12、静态方法只能被重载,而不能被重写,因为静态方法不属于类的实例成员;

  13、虽然字段不能声明为 static const,但 const 字段的行为在本质上是静态的。这样的字段属于类,不属于类的实例。

三、静态方法

  1、静态方法是不属于特定对象的方法;

  2、静态方法可以访问静态成员;

  3、静态方法不可以直接访问实例成员,可以在实例函数调用的情况下,实例成员做为参数传给静态方法;

  4、静态方法也不能直接调用实例方法,可以间接调用,首先要创建一个类的实例,然后通过这一特定对象来调用静态方法。

四、静态构造函数

  1、静态类可以有静态构造函数,静态构造函数不可继承;
  2、静态构造函数可以用于静态类,也可用于非静态类;
  3、静态构造函数无访问修饰符、无参数,只有一个 static 标志;
  4、静态构造函数不可被直接调用,当创建类实例或引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次。

五、静态成员的存储

  使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员static修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。

  静态全局变量

  定义:在全局变量前,加上关键字 static 该变量就被定义成为了一个静态全局变量。

  特点:A、该变量在全局数据区分配内存。   B、初始化:如果不显式初始化,那么将被隐式初始化为0。

  静态局部变量

  定义:在局部变量前加上static关键字时,就定义了静态局部变量。

  特点:A、该变量在全局数据区分配内存。   B、初始化:如果不显式初始化,那么将被隐式初始化为0。   C、它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或 语句块结束时,其作用域随之结束。

  静态数据成员
  特点

  A、内存分配:在程序的全局数据区分配。   

  B、初始化和定义:     a、静态数据成员定义时要分配空间,所以不能在类声明中定义。     b、为了避免在多个使用该类的源文件中,对其重复定义,所在,不能在类的头文件中定义。     c、静态数据成员因为程序一开始运行就必需存在,所以其初始化的最佳位置在类的内部实现。   

  C、特点     a、对相于 public,protected,private 关键字的影响它和普通数据成员一样,     b、因为其空间在全局数据区分配,属于所有本类的对象共享,所以,它不属于特定的类对象,在没产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它。
  D、访问形式     a、 类对象名.静态数据成员名

        E、静态数据成员,主要用在类的所有实例都拥有的属性上。比如,对于一个存款类,帐号相对于每个实例都是不同的,但每个实例的利息是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局区的内存,所以节省存贮空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了,因为它们实际上是共用一个东西。  

  静态成员函数
  特点:   A、静态成员函数与类相联系,不与类的对象相联系。   B、静态成员函数不能访问非静态数据成员。原因很简单,非静态数据成员属于特定的类实例。
  作用:   主要用于对静态数据成员的操作。

  调用形式:   A、类对象名.静态成员函数名()

注意: 

当类第一次被加载时,会对类中的静态变量先按顺序进行分配内存空间,当全部分配完内存空间之后,在对静态变量按顺序赋值。

     首先分为两部分 寄存器和内存(包括缓存)

  内存分为两部分 代码和数据

  数据分为两部分 静态存储区和运行时存储

  运行时存储分为 堆栈 和 堆
  静态存储分为 全局静态存储 和 常量

1、静态构造函数
非静态类中可以包含静态成员。但是,静态类中不能包含任何非静态成员
静态构造函数不能带有任何参数,而非静态构造函数可以有多种参数列表

2、Static使用场合
a、需要保存全局都有效的数据, 如:当前已登录用户信息、系统配置信息、系统设置
b、因为效率相对更高,所以需要快速访问的
c、使用频率非常高的

一、静态类 
1.只能包含静态成员(静态方法或静态变量),非静态成员是不能使用的,而非静态类可以包含静态的方法、字段、属性或事件,且无论对这个非静态类创建多少个实例,它的静态成员都只有一个。 
2.不能对其实例化。 
3.不能被继承,因为静态类本质是一个抽象的密封类。 
4.不能包含实例构造函数。

二、静态变量 
1.static只能修饰成员变量,不能修饰局部变量。 
2.表示每次重新使用该变量所在方法、类或自定义类时,变量的值为程序这次运行最后一次为变量赋值时的值。 
3.静态变量一直记录变量的值,一直到下次赋值时。 
4.不同线程下访问的静态属性总是同一属性,如果某一线程更改了属性值,将造成其他线程访问属性值的错误。因此方法中访问同一静态属性就需要使用lock关键字,或创建互斥对象来保持静态属性在同一时间只能被某一对象的属性或方法访问。 
5.静态成员只被创建一次,所以静态成员只有一份,而实例成员有多少个对象,就有多少个成员。

三、静态方法 
1.在方法(函数)前用static修饰,表示此方法为所在类或所在自定义类所有,而不是这个类的实例所有。 
2.在静态方法中只能直接调用同类中其他的静态成员(包括变量和方法), 而不能直接访问类中的非静态成员。 
3.每一个线程在同一时间访问的静态方法都是不同的,因此静态方法在多线程调用中不会产生冲突。 
4.在静态方法中不能直接调用实例成员,因为静态方法被调用时,对象还有可能不存在。 
5.this/base关键字在静态方法不能使用,因为有可能对象还不存在。 
6.静态方法只能被重载,不能被重写,因为静态方法不属于类的实例成员。

四、静态构造函数 
1.静态类可以有静态构造函数,静态构造函数不可继承。 
2.可以用于静态类,也可用于非静态类。 
3.无访问修饰符、无参数,只有一个static标志。 
4.不可被直接调用,当创建类实例或引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次。

什么时候适合用static修饰? 
1.当变量需要被共享时可以将变量定义为静态变量。 
2.当方法需要被反复调用时可以将方法定义为静态方法。 
3.当一个类中包含的成员都是静态时可以将类定义为静态类。

有时候写程序时常常遇到这样的情况:

1、定义了变量和方法不知道什么时候该加上static修饰符。

2、static变量和方法与非static变量和方法有什么区别?

3、在一个类的静态方法里为什么不能访问该类的非静态成员变量和方法?

4、在一个类的非静态方法中为什么可以访问该类的静态变量和方法?

5、在静态方法中为什么不能用this?

6、静态构造函数有什么作用?

基于以上问题,我发表自己的看法如下:

1、static变量及方法不用实例化对象就可以用类名.静态变量和类名.静态方法这种方式进行访问,这使得访问更加方便,不用手工实例化对象。对于某些只读变量和方法来说很适合共享。

2、static与非static最大的区别就是static类型的变量及方法在调用的时候就在内存中分配了地址,且这个地址只有一份,故static可以直接访问。而非static必需手工去实例化该类,以对象的方式去访问变量和方法。

3、在一个静态方法里去访问该类的非静态变量或方法,由于static是属于类本身的,是在类被调用的时候,static类型就已经生成,而非static此时并没有生成,它不属于这个类本身,它是属于这个类的对象。故在没有实例化成对象的时候,在静态方法中访问非静态是根本找不到它们的,它不属于这个类。

4、在非静态方法中去访问静态,由于类被调用时,静态变量和方法就已经生成,也就是说它们属于这个类。既然已经存在,故不管是在静态方法中,还是非静态方法中都可以访问到它们。

5、this表明对象本身,而在静态方法中并没有对象概念存在,它只有类本身这个概念,它和对象是属于两种互拆的状态,即我中无你,你中无我的情况。也就是说你用对象名.静态变量或对象名.静态方法是不可访问的。

6、每个类都必须有构造函数,否则此类无法实例化成对象。而我们有时定义的类可以不写它的构造函数,这是因为编译器会帮我们加上一个静态的空构造函数。这样才能实例化。也可以用静态构造函数去初始化静态变量。

一、Virtual方法(虚方法)

     virtual 关键字用于在基类中修饰方法。virtual的使用会有两种情况:

     情况1:在基类中定义了virtual方法,但在派生类中没有重写该虚方法。那么在对派生类实例的调用中,该虚方法使用的是基类定义的方法。

     情况2:在基类中定义了virtual方法,然后在派生类中使用override重写该方法。那么在对派生类实例的调用中,该虚方法使用的是派生重写的方法。

二、Abstract方法(抽象方法)

     abstract关键字只能用在抽象类中修饰方法,并且没有具体的实现。抽象方法的实现必须在派生类中使用override关键字来实现。

一个生动的例子 :老爸表示基类(被继承的类) 儿子表示子类(继承的类)
老爸用virtual告诉儿子:"孩子,你要继承我的事业,在这块上面可以自己继续发展你自己的"
儿子用override告诉全世界:"这个我可不是直接拿我爸的,他只是指个路给我,是我自己奋斗出来的"

abstract:抽象方法声明使用,是必须被派生类覆写的方法,抽象类就是用来被继承的;可以看成是没有实现体的虚方法;如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般方法;抽象类不能有实体的。

virtual:标记方法为虚方法
1.可在派生类中以override覆盖此方法
2.不覆盖也可由对象调用
3.无此标记的方法(也无其他标记),重写时需用new隐藏原方法
abstract 与virtual : 方法重写时都使用 override 关键字
接口定义以大写字母I开头。方法只定义其名称,在C#中,方法默认是公有方法;用public修饰方法是不允许的,否则会出现编译错误;接口可以从别的接口继承,如果是继承多个接口,则父接口列表用逗号间隔。
接口可以通过类来实现,当类的基列表同时包含基类和接口时,列表中首先出现的是基类;类必须要实现其抽象方法;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值