C#知识点笔记

349 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

把C#部分的知识点记录下来,以供日后复习。
菜鸟教程

可空类型

总结:
声明类型后面加?可以让不能为空的值为空。
??和?.使用方法看代码示例。

可空类型

示例代码:

            int? a;
            a = null;

			b = a??1;//如果a为空则返回1
			
			//如果testClass为空则不会调用DoSomething方法
			//省掉了判断testClass是否为空的步骤
			testClass?.DoSomething();

继承

总结:
C#不支持多重继承,但是可以继承多个接口。
实例只能向上兼容(子实父)。
派生类必须继承父类的构造函数,默认继承的无参构造函数。派生类继承构造函数时需要传入父类构造函数所需的参数,具体看代码示例。

继承

示例代码:

    class Father
    {
        public Father(int a)
        {
            Console.WriteLine("Father:{0}",a);
        }
        public virtual void DoFather()
        {
            Console.WriteLine("Father");
        }
    }
    
    class Son:Father
    {
        public Son(int a) : base(a) // a都是这里的int a
        {
            Console.WriteLine("Son:{0}", a);
        }

        public override void DoFather()
        {
            Console.WriteLine("Son");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Father father = new Father(1);
            father.DoFather(); //输出 Father:1    Father

            Son son = new Son(1);
            son.DoFather(); //输出 Father:1    Son1   Son

            //Error 子类无法用父类实例
            //Son sf = new Father() as Son;
            //sf.DoFather();

            Father fs = new Son(1);
            fs.DoFather(); //输出 Father1   Son1   Son   和上一个相同
        }
    }

多态

总结:
1.多态是同一个行为具有多个不同表现形式或形态的能力。实现多态的手段:抽象类、虚方法、接口。
2.abstract类中可以包含抽象字段,抽象方法。也可以包含实现了的方法和字段(非abstract)。
3.abstract和virtual的区别:abstract是抽象,只需要声明不需要实现部分,它必须被覆写。virtual是虚方法。必须有实现部分(即使是空的大括号),可以不被覆写。
4.new和override的区别:new是隐藏方法,根据声明的class调用方法,如果声明是父类,即使是用子类实例的,也调用的父类。override是重写方法,看用的哪个类实例调用哪个类。
5.方法重载(overload)有三个条件,方法名必须相同,参数列表必须不相同,返回值类型可以不相同。(二必须一可以)

多态

运算符重载

总结:
1.是静态方法,是重载不是覆写。
2.注意需要的参数和返回值。
3.使用operator关键字,后面跟上要重载的操作符。
4.不是所有操作符都可以重载,详细内容看教程。

运算符重载

示例代码:

    public class Program
    {
        static void Main(string[] args)
        {
            OperatorTest a = new OperatorTest();
            a.num = 5;
            OperatorTest b = new OperatorTest();
            b.num = 6;
            OperatorTest c = a + b;
            Console.WriteLine(c.num);
            Console.ReadKey();
        }
    }

    public class OperatorTest
    {
        public int num;
        
        //运算符重载
        public static OperatorTest operator +(OperatorTest a, OperatorTest b)
        {
            OperatorTest test = new OperatorTest();
            test.num = a.num + b.num;
            return test;
        }
    }

接口(Interface)

总结:
1.接口只是声明,不能有实现部分。继承类负责实现。
2.接口中可以定义事件,索引器,属性,方法。
3.默认访问权限为public。
4.接口之间可以继承。
5.通常接口命名以“I”开头。
6.is用于判断是否能转换,如if(a is b);as用于转换,不会报错,如果转换不了会返回null,如P p = a as P;

接口(Interface)

示例代码:

    //简单使用
    class Program
{
    static void Main(string[] args)
    {
        IWorker james1 = new James1();
        IWorker james2 = new James2();
        james1.work("设计");
        james2.work("编程");
        //从这个例子我体会到了有接口的好处,可以想象如果又来了新的员工。
        //如果不采用接口,而是每个员工都有一个单独的类,这样就会容易出错。
        //如果有接口这种协议约束的话,那么只要实现了接口就肯定有接口里声明的方法,我们只需拿来调用。
    }
}
public interface IWorker{ void work(string s); }
class James1 : IWorker
{
    public void work(string s)
    {
        Console.WriteLine("我的名字是James1,我的工作是" +s);
    }
}
class James2 : IWorker
{
    public void work(string s)
    {
        Console.WriteLine("我的名字是James2,我的工作是"+s);
    }
}

	//接口继承
	interface IParentInterface
	{
    	void ParentInterfaceMethod();
	}
	interface IMyInterface : IParentInterface
	{
    	void MethodToImplement();
	}
	class InterfaceImplementer : IMyInterface
	{
    	static void Main()
    {
        InterfaceImplementer iImp = new InterfaceImplementer();
        iImp.MethodToImplement();
        iImp.ParentInterfaceMethod();
    }
    public void MethodToImplement()
    {
        Console.WriteLine("MethodToImplement() called.");
    }
    public void ParentInterfaceMethod()
    {
        Console.WriteLine("ParentInterfaceMethod() called.");
    }
    
	//接口中定义事件,索引器,属性,方法;
    delegate void Delegate();
    interface I<T>
    {
        event Delegate DE;
        T this[int i]
        {
            get;
            set;
        }  
        List<T> age
        {
            get;
            set;
        }
        void Say();
    }
    class c : I<c>
    {
        public event Delegate DE;
      public  c this[int i]
        {
            get {  return age[i]; }
            set { DE(); Say(); age[i] = value; }
        }
      public  List<c> age
        {
            get;
            set;
        }
      public   void Say()
      {
          Console.WriteLine("123");
      }
    }
    class b : I<c>
    {
        public event Delegate DE;
        public c this[int i]
        {
            get { return age[i]; }
            set { DE();Say(); age[i] = value; }
        }
        public List<c> age
        {
            get;
            set;
        }
        public void Say()
        {
            Console.WriteLine("456");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            I<c> Ic = new c();
            I<c> lb = new b();
            
            Ic.DE += deC;
            lb.DE += deB;
            lb = Ic;
            lb.age = new List<c>();
            lb.age.Add(new c());
            lb[0] = new c();
            Console.WriteLine("S");
            Console.ReadKey();
        }
      static  void deC()
        {
            Console.WriteLine("c");
        }
       static void deB()
        {
            Console.WriteLine("B");
        }
    }

预处理指令

总结:
1.一行只能有一个预处理器指令。
2.#define只在当前声明类生效。
3.预处理指令一般用于程序的调试和运行。
4.Unity可以使用#if UNITY_EDITOR等判断运行环境来确定是否执行后续代码。

预处理指令

示例代码:

#define test
using System;
namespace 预处理指令
{
    class Program
	{
        static void Main(string[] args)
        {
#if (test)
            Console.WriteLine("test is define");
#else
            Console.WriteLine("test is not define");
#endif
//#region可以折叠代码
#region 折叠
            Console.ReadKey();
#endregion
        }
    }
}

C# 特性(Attribute)

总结:
1.特性分为两种,预定义特性和自定义特性。
2.预定义特性有AttributeUsage、Conditional、Obsolete、DllImport(用来标记非.NET的函数,表明该方法在一个外部的DLL中定义)。
3.每个特性至少有一个构造函数。
4.具体使用看下面代码,详细讲解看菜鸟教程。

C# 特性(Attribute)

示例代码:

    // 描述如何使用一个自定义特性 SomethingAttribute
    //参数1:支持在什么类型上加的特性;参数2:是否允许重复加该特性;参数3:是否支持特性继承;
    [AttributeUsage(AttributeTargets.All , AllowMultiple = true , Inherited = true )]
    //********自定义特性SomethingAttribute**************//
    public class SomethingAttribute : Attribute
    {
        private string name; // 名字
        private string data; // 日期
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public string Data
        {
            get { return data; }
            set { data = value; }
        }
        public SomethingAttribute(string name)
        {
            this.name = name;
        }
    }
    //实例化自定义
    //自动忽略名字中的Attribute部分,但是当作类使用的时候需要全名
    [Something("Amy", Data = "2018-06-18")]
    [Something("Jack", Data = "2018-06-18")]
    class Test { }
    class Program
    {
        static void Main(string[] args)
        {
            //获取自定义特性的中的变量
            Type t = typeof(Test);
            //这里是全名
            var something = t.GetCustomAttributes(typeof(SomethingAttribute), true);
            foreach (SomethingAttribute each in something)
            {
                Console.WriteLine("Name:{0}", each.Name);
                Console.WriteLine("Data:{0}", each.Data);
                Program p = new Program();
            	p.TestConditional();
            	p.OldMethod();
            }
            //如果存在此预定义符号(#define TEST),则执行此方法,没有则不执行,有点类似#if
            //只能用于方法或特性类
        	[Conditional("TEST")]
        	public void TestConditional()
        	{
            	Console.WriteLine("Conditional test");
        	}
        	//true直接报错 false只是提醒,默认为false
        	[Obsolete("这个方法过时了!",false)]
        	public void OldMethod()
        	{
            	Console.WriteLine("Old");
        	}
        }
//引用第三方类库 比如unity接入IOS就有用到
        [DllImport("User32.dll")]
        public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
    }

C# 索引器(Indexer)

总结:
1.索引器的用法类似属性。
2.索引器可以带有多个参数,并且支持多种类型。
3.作用相当于根据类的索引取对应的值。

C# 索引器(Indexer)

示例代码:

   class IndexedNames
   {
      private string[] namelist = new string[size];
      static public int size = 10;
      public IndexedNames()
      {
         for (int i = 0; i < size; i++)
         {
          namelist[i] = "N. A.";
         }
      }
      public string this[int index]
      {
         get
         {
            string tmp;

            if( index >= 0 && index <= size-1 )
            {
               tmp = namelist[index];
            }
            else
            {
               tmp = "";
            }

            return ( tmp );
         }
         set
         {
            if( index >= 0 && index <= size-1 )
            {
               namelist[index] = value;
            }
         }
      }
      public int this[string name]
      {
         get
         {
            int index = 0;
            while(index < size)
            {
               if (namelist[index] == name)
               {
                return index;
               }
               index++;
            }
            return index;
         }

      }

      static void Main(string[] args)
      {
         IndexedNames names = new IndexedNames();
         names[0] = "Zara";
         names[1] = "Riz";
         names[2] = "Nuha";
         names[3] = "Asif";
         names[4] = "Davinder";
         names[5] = "Sunil";
         names[6] = "Rubic";
         // 使用带有 int 参数的第一个索引器
         for (int i = 0; i < IndexedNames.size; i++)
         {
            Console.WriteLine(names[i]);
         }
         // 使用带有 string 参数的第二个索引器
         Console.WriteLine(names["Nuha"]);
         Console.ReadKey();
      }
   }
   //输出
   //Zara
   //Riz
   //Nuha
   //Asif
   //Davinder
   //Sunil
   //Rubic
   //N. A.
   //N. A.
   //N. A.
   //2

C# 委托(Delegate)

总结:
1.起到存储方法的作用。
2.委托可指向一个与其具有相同标签的方法。
3.委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。
4.可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播,通过+=、-=删除。
5.定义一个委托相当于定义一个新类,所有可以定义类的地方都可以定义委托。

C# 委托(Delegate)

示例代码:

delegate int NumberChanger(int n);
namespace DelegateAppl
{
   class TestDelegate
   {
      static int num = 10;
      public static int AddNum(int p)
      {
         num += p;
         return num;
      }

      public static int MultNum(int q)
      {
         num *= q;
         return num;
      }
      public static int getNum()
      {
         return num;
      }

      static void Main(string[] args)
      {
         // 创建委托实例
         NumberChanger nc;
         NumberChanger nc1 = new NumberChanger(AddNum);
         NumberChanger nc2 = new NumberChanger(MultNum);
         nc = nc1;
         nc += nc2;
         // 调用多播
         nc(5);
         Console.WriteLine("Value of Num: {0}", getNum());
         Console.ReadKey();
      }
   }
}

C# 事件(Event)

总结:
1.用法与委托类似。
2.Event只能+=或-=,不允许=;委托允许使用=覆盖。
3.event 只能在类内定义,或者接口内定义,而delegate可以在类外类外定义,但不能在接口内,因为接口中只能包含成员。
4.委托定义的是一个类型,而事件定义的是一个成员。

C# 事件(Event)

示例代码:

    class DelegateTest 
    {
        public delegate void delegate_tz();
        public event delegate_tz delegate_tz0;
        public void start()
        {
            Console.WriteLine("启动事件");
            delegate_tz0();      // 得调用该事件呀
            Console.ReadKey();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            DelegateTest DelegateTest0 = new DelegateTest();
            //DelegateTest0.delegate_tz0 += DelegateTest.delegate_tz(test);  // 必须new一下才行,因为它是另外一个类呀
            DelegateTest0.delegate_tz0 += new DelegateTest.delegate_tz(test);
            DelegateTest0.start();                                                  // 启动事件
        }
        static public void test()
        {
            Console.WriteLine("这是一个被注册的函数,按任意键继续...");
        }
    }

C# 泛型(Generic)

总结:
1.它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
2.可以创建泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
3.可以对泛型类进行约束以访问特定数据类型的方法。
4.where指定对用作泛型类型、方法、委托或本地函数中类型参数的参数类型的约束。[where] (docs.microsoft.com/zh-cn/dotne…)

C# 泛型(Generic)

示例代码:

	delegate T NumberChanger<T>(T n);
    public class MyGenericArray<T> where T : notnull
    {
        private T[] array;
        public MyGenericArray(int size)
        {
            array = new T[size + 1];
        }
        public T getItem(int index)
        {
            return array[index];
        }
        public void setItem(int index, T value)
        {
            array[index] = value;
        }
    }

C# 匿名方法

对方法的简写。不过基本被Lambda表达式代替,实际开发中很少使用(对我而言)。
C# 匿名方法

示例代码:

	delegate void Test(int n);
	Test t = delegate(int x)
	{
    	Console.WriteLine(x);
	};

C# Lambda表达式

匿名方法的升级版,更简单粗暴。

示例代码:

	delegate void Test(int n);
	Test t =  _ => Console.WriteLine(_);
	//_为参数
	//执行代码只有一行时可以省略大括号,如果有多行执行代码需要加大括号。