c#学习

目录

控制台的输出输入

类型:

可空类型:

数组:

多维数组:

交错数组:(可以认为是不定列长度的二维数组)

参数数组:(感觉功能上有点类似可变参数)

array类:

字符串

C# 结构的特点

类 vs 结构

类内的静态成员

继承

多态

运算符重载

接口

异常处理

文件读写

简单的文件操作:FileStream类

文本文件的数据读写  StreamReader 和 StreamWriter

二进制文件的读写  BinaryReader 和 BinaryWriter 

对windows系统文件夹的操作 DirectoryInfo 和 FileInfo 类

特性与反射

特性

反射

属性和索引器

委托和事件

匿名函数与lambda

lambda表达式   " ( )  =>  { }; "

集合与泛型

不安全代码

多线程


这是我在学习了c++之后学习的c#,大体就是以c++为对比对象,一样的的写法,特性就不写下来了,c#独有的就记下来。

控制台的输出输入

System 命名空间中的 Console 类提供了一个函数 ReadLine(),用于接收来自用户的输入,并把它存储到一个变量中。

num = Convert.ToInt32(Console.ReadLine())

Console.WriteLine(i.ToString());在命令行输出到窗口中  这2个都是参数都是string类型。

类型:

c#中有值类型,引用类型和指针类型,值类型是内置类型(不包括数组),引用类型是自己构建的自定义类型(不包括结构体)。

所有的值类型对象永远只在栈中分配,即使你对值类型使用了new ;

 

可空类型:

int? i = NULL;基本上是用来判断是否赋值的。

数组:

数组使用new和不适用new都一样?(规则是:引用类型是要new,值类型不用)

声明 --- 初始化 ---- 访问

int【】 arror = new int【】 {1,2,3,4,5};

foreach(int i  in arror){console.writeLine("arror[{0}] = {1}",j,i)}; 

c#的数组可以直接复制哦   int [] arror2 = arror;

多维数组:

string [,] names; 二维数组   int [ , , ] m;  三维数组

初始化:int [,] a = new int [3,4] {
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};

一样的下标访问。

交错数组:(可以认为是不定列长度的二维数组)

与二维数组的区别:

二维数组是按照你定义的类型的一组数,比如int [2,3]那就是说一个两行三列,每一个元素都是一个整型数的数组,但是交错数组int[2][],意思是这个数组有两个元素,每一个元素都是一个整型的数组,但是长度可以不一样,比如int [][] arr= new int[2][];因为每个数组的元素不一样,所以后面的[]不能填值
int [][]=new int[10];
int [1][]=new int[8];                                                                                                                                                                            int[][] scores = new int[2][]{new int[]{92,93,94},new int[]{85,66,87,88}};

参数数组:(感觉功能上有点类似可变参数)

当你不确定传递参数的数量时,可以用参数数组,参数前面要带 params关键字

例子:

public int AddElements(params int[] arr)
      {
         int sum = 0;
         foreach (int i in arr)
         {
            sum += i;
         }
         return sum;
      }

array类:

这是个数组的基类,里面包含了很多数组的操作,比如对数组逆序 Array.Reverse(list);,排序  Array.Sort(list); 等。

具体方法可以去参阅微软的 C# 文档。

下表列出了 Array 类中一些最常用的方法:

序号方法 & 描述
1Clear
根据元素的类型,设置数组中某个范围的元素为零、为 false 或者为 null。
2Copy(Array, Array, Int32)
从数组的第一个元素开始复制某个范围的元素到另一个数组的第一个元素位置。长度由一个 32 位整数指定。
3CopyTo(Array, Int32)
从当前的一维数组中复制所有的元素到一个指定的一维数组的指定索引位置。索引由一个 32 位整数指定。
4GetLength
获取一个 32 位整数,该值表示指定维度的数组中的元素总数。
5GetLongLength
获取一个 64 位整数,该值表示指定维度的数组中的元素总数。
6GetLowerBound
获取数组中指定维度的下界。
7GetType
获取当前实例的类型。从对象(Object)继承。
8GetUpperBound
获取数组中指定维度的上界。
9GetValue(Int32)
获取一维数组中指定位置的值。索引由一个 32 位整数指定。
10IndexOf(Array, Object)
搜索指定的对象,返回整个一维数组中第一次出现的索引。
11Reverse(Array)
逆转整个一维数组中元素的顺序。
12SetValue(Object, Int32)
给一维数组中指定位置的元素设置值。索引由一个 32 位整数指定。
13Sort(Array)
使用数组的每个元素的 IComparable 实现来排序整个一维数组中的元素。
14ToString
返回一个表示当前对象的字符串。从对象(Object)继承。

 

字符串

 System.String

用法和c++基本一样,自带2个属性,lenght(字符串长度)和char(字符串指针位置)。(它和数组一样可new可不new的不懂为什么)

一些常用的静态方法:

序号方法名称 & 描述
1public static int Compare( string strA, string strB )
比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。该方法区分大小写。
2public static int Compare( string strA, string strB, bool ignoreCase )
比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。但是,如果布尔参数为真时,该方法不区分大小写。
3public static string Concat( string str0, string str1 )
连接两个 string 对象。
4public static string Concat( string str0, string str1, string str2 )
连接三个 string 对象。
5public static string Concat( string str0, string str1, string str2, string str3 )
连接四个 string 对象。
6public bool Contains( string value )
返回一个表示指定 string 对象是否出现在字符串中的值。
7public static string Copy( string str )
创建一个与指定字符串具有相同值的新的 String 对象。
8public void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count )
从 string 对象的指定位置开始复制指定数量的字符到 Unicode 字符数组中的指定位置。
9public bool EndsWith( string value )
判断 string 对象的结尾是否匹配指定的字符串。
10public bool Equals( string value )
判断当前的 string 对象是否与指定的 string 对象具有相同的值。
11public static bool Equals( string a, string b )
判断两个指定的 string 对象是否具有相同的值。
12public static string Format( string format, Object arg0 )
把指定字符串中一个或多个格式项替换为指定对象的字符串表示形式。
13public int IndexOf( char value )
返回指定 Unicode 字符在当前字符串中第一次出现的索引,索引从 0 开始。
14public int IndexOf( string value )
返回指定字符串在该实例中第一次出现的索引,索引从 0 开始。
15public int IndexOf( char value, int startIndex )
返回指定 Unicode 字符从该字符串中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。
16public int IndexOf( string value, int startIndex )
返回指定字符串从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。
17public int IndexOfAny( char[] anyOf )
返回某一个指定的 Unicode 字符数组中任意字符在该实例中第一次出现的索引,索引从 0 开始。
18public int IndexOfAny( char[] anyOf, int startIndex )
返回某一个指定的 Unicode 字符数组中任意字符从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。
19public string Insert( int startIndex, string value )
返回一个新的字符串,其中,指定的字符串被插入在当前 string 对象的指定索引位置。
20public static bool IsNullOrEmpty( string value )
指示指定的字符串是否为 null 或者是否为一个空的字符串。
21public static string Join( string separator, string[] value )
连接一个字符串数组中的所有元素,使用指定的分隔符分隔每个元素。
22public static string Join( string separator, string[] value, int startIndex, int count )
连接接一个字符串数组中的指定位置开始的指定元素,使用指定的分隔符分隔每个元素。
23public int LastIndexOf( char value )
返回指定 Unicode 字符在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。
24public int LastIndexOf( string value )
返回指定字符串在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。
25public string Remove( int startIndex )
移除当前实例中的所有字符,从指定位置开始,一直到最后一个位置为止,并返回字符串。
26public string Remove( int startIndex, int count )
从当前字符串的指定位置开始移除指定数量的字符,并返回字符串。
27public string Replace( char oldChar, char newChar )
把当前 string 对象中,所有指定的 Unicode 字符替换为另一个指定的 Unicode 字符,并返回新的字符串。
28public string Replace( string oldValue, string newValue )
把当前 string 对象中,所有指定的字符串替换为另一个指定的字符串,并返回新的字符串。
29public string[] Split( params char[] separator )
返回一个字符串数组,包含当前的 string 对象中的子字符串,子字符串是使用指定的 Unicode 字符数组中的元素进行分隔的。
30public string[] Split( char[] separator, int count )
返回一个字符串数组,包含当前的 string 对象中的子字符串,子字符串是使用指定的 Unicode 字符数组中的元素进行分隔的。int 参数指定要返回的子字符串的最大数目。
31public bool StartsWith( string value )
判断字符串实例的开头是否匹配指定的字符串。
32public char[] ToCharArray()
返回一个带有当前 string 对象中所有字符的 Unicode 字符数组。
33public char[] ToCharArray( int startIndex, int length )
返回一个带有当前 string 对象中所有字符的 Unicode 字符数组,从指定的索引开始,直到指定的长度为止。
34public string ToLower()
把字符串转换为小写并返回。
35public string ToUpper()
把字符串转换为大写并返回。
36public string Trim()
移除当前 String 对象中的所有前导空白字符和后置空白字符。

结构体

c#只有一个结构体写法

//定义1:
struct Books
{
   public string title;
   public string author;
   public string subject;
   public int book_id;
};  

//定义2:

struct Books
{
   private string title;
   private string author;
   private string subject;
   private int book_id;
   public void getValues(string t, string a, string s, int id)
   {
      title = t;
      author = a;
      subject = s;
      book_id =id;
   }
   public void display()
   {
      Console.WriteLine("Title : {0}", title);
      Console.WriteLine("Author : {0}", author);
      Console.WriteLine("Subject : {0}", subject);
      Console.WriteLine("Book_id :{0}", book_id);
   }

}; 

C# 结构的特点

您已经用了一个简单的名为 Books 的结构。在 C# 中的结构与传统的 C 或 C++ 中的结构不同。C# 中的结构有以下特点:

  • 结构可带有方法、字段、索引、属性、运算符方法和事件。
  • 结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义无参构造函数。无参构造函数(默认)是自动定义的,且不能被改变。
  • 与类不同,结构不能继承其他的结构或类。
  • 结构不能作为其他结构或类的基础结构。
  • 结构可实现一个或多个接口。
  • 结构成员不能指定为 abstract、virtual 或 protected。
  • 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。
  • 如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。

类 vs 结构

类和结构有以下几个基本的不同点:

  • 类是引用类型,结构是值类型。
  • 结构不支持继承。
  • 结构不能声明默认的构造函数。

(我好像没见过typename关键字)

c#枚举是值类型,使用和c++一样。

每个方法,成员变量都要写访问标识符。

构造函数不能写无参数的构造函数(这是默认构造要做的事),没有拷贝,移动等构造函数

类内的静态成员

静态变量可在成员函数或类的定义外部进行初始化。你也可以在类的定义内部初始化静态变量。

c++与c#静态成员对比与理解 :https://www.cnblogs.com/sols/p/8761590.html

继承

c#只有is-a关系的public继承,所以不需要写访问修饰符,c#没有多重继承(可用其他方法实现),c#继承的时候要调用父类的构造函数,且写法是base()。

例子:

using System;
namespace RectangleApplication
{
   class Rectangle
   {
      // 成员变量
      protected double length;
      protected double width;
      public Rectangle(double l, double w)
      {
         length = l;
         width = w;
      }
      public double GetArea()
      {
         return length * width;
      }
      public void Display()
      {
         Console.WriteLine("长度: {0}", length);
         Console.WriteLine("宽度: {0}", width);
         Console.WriteLine("面积: {0}", GetArea());
      }
   }//end class Rectangle  
   class Tabletop : Rectangle
   {
      private double cost;
      public Tabletop(double l, double w) : base(l, w)
      { }
      public double GetCost()
      {
         double cost;
         cost = GetArea() * 70;
         return cost;
      }
      public void Display()
      {
         base.Display();
         Console.WriteLine("成本: {0}", GetCost());
      }
   }
   class ExecuteRectangle
   {
      static void Main(string[] args)
      {
         Tabletop t = new Tabletop(4.5, 7.5);
         t.Display();
         Console.ReadLine();
      }
   }
}

多态

c#静态多态和c++一样用重载实现。

c#动态多态要 抽象类 和 虚方法 一起实现的。 ??

抽象类的3个规则:

  • 您不能创建一个抽象类的实例。
  • 您不能在一个抽象类外部声明一个抽象方法。
  • 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。(不能继承,不能实例化,不懂有什么意意义,可能可以自己定义静态方法)
using System;
namespace PolymorphismApplication
{
   abstract class Shape
   {
       abstract public int area();
   }
   class Rectangle:  Shape
   {
      private int length;
      private int width;
      public Rectangle( int a=0, int b=0)
      {
         length = a;
         width = b;
      }
      public override int area ()
      {
         Console.WriteLine("Rectangle 类的面积:");
         return (width * length);
      }
   }

   class RectangleTester
   {
      static void Main(string[] args)
      {
         Rectangle r = new Rectangle(10, 7);
         double a = r.area();
         Console.WriteLine("面积: {0}",a);
         Console.ReadKey();
      }
   }
}

一个计算图形面积的类的写法:

using System;
namespace PolymorphismApplication
{
   class Shape
   {
      protected int width, height;
      public Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      public virtual int area()
      {
         Console.WriteLine("父类的面积:");
         return 0;
      }
   }
   class Rectangle: Shape
   {
      public Rectangle( int a=0, int b=0): base(a, b)
      {

      }
      public override int area ()
      {
         Console.WriteLine("Rectangle 类的面积:");
         return (width * height);
      }
   }
   class Triangle: Shape
   {
      public Triangle(int a = 0, int b = 0): base(a, b)
      {
     
      }
      public override int area()
      {
         Console.WriteLine("Triangle 类的面积:");
         return (width * height / 2);
      }
   }
   class Caller
   {
      public void CallArea(Shape sh)
      {
         int a;
         a = sh.area();
         Console.WriteLine("面积: {0}", a);
      }
   }  
   class Tester
   {
     
      static void Main(string[] args)
      {
         Caller c = new Caller();
         Rectangle r = new Rectangle(10, 7);
         Triangle t = new Triangle(10, 5);
         c.CallArea(r);
         c.CallArea(t);
         Console.ReadKey();
      }
   }
}

运算符重载

c#运算符重载不分类的成员函数和非类成员函数,统一使用静态成员方法(类似于非成员?)

接口

抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。

接口和抽象类的区别:

c#接口的特点:

  •  (1)通过接口可以实现多重继承,C# 接口的成员不能有 public、protected、internal、private 等修饰符。原因很简单,接口里面的方法都需要由外面接口实现去实现方法体,那么其修饰符必然是 public。C# 接口中的成员默认是 public 的,java 中是可以加 public 的。
  •  (2)接口成员不能有 new、static、abstract、override、virtual 修饰符。有一点要注意,当一个接口实现一个接口,这2个接口中有相同的方法时,可用 new 关键字隐藏父接口中的方法。
  •  (3)接口中只包含成员的签名,接口没有构造函数,所有不能直接使用 new 对接口进行实例化。接口中只能包含方法、属性、事件和索引的组合。接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类。
  •  (4)C# 是单继承,接口是解决 C# 里面类可以同时继承多个基类的问题。
using System;

interface IMyInterface
{
        // 接口成员
    void MethodToImplement();
}

class InterfaceImplementer : IMyInterface
{
    static void Main()
    {
        InterfaceImplementer iImp = new InterfaceImplementer();
        iImp.MethodToImplement();
    }

    public void MethodToImplement()
    {
        Console.WriteLine("MethodToImplement() called.");
    }
}

预处理

预处理器指令描述
#define它用于定义一系列成为符号的字符。
#undef它用于取消定义符号。
#if它用于测试符号是否为真。
#else它用于创建复合条件指令,与 #if 一起使用。
#elif它用于创建复合条件指令。
#endif指定一个条件指令的结束。
#line它可以让您修改编译器的行数以及(可选地)输出错误和警告的文件名。
#error它允许从代码的指定位置生成一个错误。
#warning它允许从代码的指定位置生成一级警告。
#region它可以让您在使用 Visual Studio Code Editor 的大纲特性时,指定一个可展开或折叠的代码块。
#endregion它标识着 #region 块的结束。

c#的预处理不是用来创建宏的,所以,他们define后面是没有替换值的。主要是用来执行  #ifdel  预处理指令der

实例

#define PI
using System;
namespace PreprocessorDAppl
{
   class Program
   {
      static void Main(string[] args)
      {
         #if (PI)
            Console.WriteLine("PI is defined");
         #else
            Console.WriteLine("PI is not defined");
         #endif
         Console.ReadKey();
      }
   }
}

异常处理

异常处理和c++的几乎一样,下面是几个常用的异常处理类,你也可以自己继承重写。

异常类描述
System.IO.IOException处理 I/O 错误。
System.IndexOutOfRangeException处理当方法指向超出范围的数组索引时生成的错误。
System.ArrayTypeMismatchException处理当数组类型不匹配时生成的错误。
System.NullReferenceException处理当依从一个空对象时生成的错误。
System.DivideByZeroException处理当除以零时生成的错误。
System.InvalidCastException处理在类型转换期间生成的错误。
System.OutOfMemoryException处理空闲内存不足生成的错误。
System.StackOverflowException处理栈溢出生成的错误。

这些都是派生于System.SystemException的类,用户自定义的异常类是派生于 ApplicationException类的。

例子:

using System;
namespace UserDefinedException
{
   class TestTemperature
   {
      static void Main(string[] args)
      {
         Temperature temp = new Temperature();
         try
         {
            temp.showTemp();
         }
         catch(TempIsZeroException e)
         {
            Console.WriteLine("TempIsZeroException: {0}", e.Message);
         }
         Console.ReadKey();
      }
   }
}
public class TempIsZeroException: ApplicationException
{
   public TempIsZeroException(string message): base(message)
   {
   }
}
public class Temperature
{
   int temperature = 0;
   public void showTemp()
   {
      if(temperature == 0)
      {
         throw (new TempIsZeroException("Zero Temperature found"));
      }
      else
      {
         Console.WriteLine("Temperature: {0}", temperature);
      }
   }
}

用户自定义异常处理

 

using System;
namespace UserDefinedException
{
   class TestTemperature
   {
      static void Main(string[] args)
      {
         Temperature temp = new Temperature();
         try
         {
            temp.showTemp();
         }
         catch(TempIsZeroException e)
         {
            Console.WriteLine("TempIsZeroException: {0}", e.Message);
         }
         Console.ReadKey();
      }
   }
}
public class TempIsZeroException: ApplicationException
{
   public TempIsZeroException(string message): base(message)
   {
   }
}
public class Temperature
{
   int temperature = 0;
   public void showTemp()
   {
      if(temperature == 0)
      {
         throw (new TempIsZeroException("Zero Temperature found"));
      }
      else
      {
         Console.WriteLine("Temperature: {0}", temperature);
      }
   }
}

文件读写

c#对文件的操作基本都在System.i/o中,

简单的文件操作:FileStream类

 

例子:

using System;
using System.IO;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            FileStream F = new FileStream("test.dat",
            FileMode.OpenOrCreate, FileAccess.ReadWrite);

            for (int i = 1; i <= 20; i++)
            {
                F.WriteByte((byte)i);
            }

            F.Position = 0;

            for (int i = 0; i <= 20; i++)
            {
                Console.Write(F.ReadByte() + " ");
            }
            F.Close();
            Console.ReadKey();
        }
    }
}

文本文件的数据读写  StreamReader 和 StreamWriter

例子:

using System;
using System.IO;

namespace FileApplication
{
    class Program
    {
        static void Main(string[] args)
        {

            string[] names = new string[] {"Zara Ali", "Nuha Ali"};
            using (StreamWriter sw = new StreamWriter("names.txt"))
            {
                foreach (string s in names)
                {
                    sw.WriteLine(s);

                }
            }

            // 从文件中读取并显示每行
            string line = "";
            using (StreamReader sr = new StreamReader("names.txt"))
            {
                while ((line = sr.ReadLine()) != null)
                {
                    Console.WriteLine(line);
                }
            }
            Console.ReadKey();
        }
    }
}

注意几个常用的成员函数就行了,Read(), ReadLine(), Write(), WriteLine(), Close().

 

二进制文件的读写  BinaryReader 和 BinaryWriter 

例子:

 

using System;
using System.IO;

namespace BinaryFileApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            BinaryWriter bw;
            BinaryReader br;
            int i = 25;
            double d = 3.14157;
            bool b = true;
            string s = "I am happy";
            // 创建文件
            try
            {
                bw = new BinaryWriter(new FileStream("mydata",
                FileMode.Create));
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message + "\n Cannot create file.");
                return;
            }
            // 写入文件
            try
            {
                bw.Write(i);
                bw.Write(d);
                bw.Write(b);
                bw.Write(s);
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message + "\n Cannot write to file.");
                return;
            }

            bw.Close();
            // 读取文件
            try
            {
                br = new BinaryReader(new FileStream("mydata",
                FileMode.Open));
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message + "\n Cannot open file.");
                return;
            }
            try
            {
                i = br.ReadInt32();
                Console.WriteLine("Integer data: {0}", i);
                d = br.ReadDouble();
                Console.WriteLine("Double data: {0}", d);
                b = br.ReadBoolean();
                Console.WriteLine("Boolean data: {0}", b);
                s = br.ReadString();
                Console.WriteLine("String data: {0}", s);
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message + "\n Cannot read from file.");
                return;
            }
            br.Close();
            Console.ReadKey();
        }
    }
}

从上面的例子我们可以知道,二进制读写需要使用FileStream类来打开文件,读取时需要指定好格式,写入时,会自动重写,不需要我们管他。

需要关注的成员是Close(),  write(),  Read()。  

对windows系统文件夹的操作 DirectoryInfo 和 FileInfo 类

 

using System;
using System.IO;

namespace WindowsFileApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个 DirectoryInfo 对象
            DirectoryInfo mydir = new DirectoryInfo(@"c:\Windows");

            // 获取目录中的文件以及它们的名称和大小
            FileInfo [] f = mydir.GetFiles();
            foreach (FileInfo file in f)
            {
                Console.WriteLine("File Name: {0} Size: {1}",
                    file.Name, file.Length);
            }
            Console.ReadKey();
        }
    }
}

  https://www.runoob.com/csharp/csharp-windows-file-system.html

特性与反射

特性

c#定义常用的几种特性,

  • AttributeUsage    给自定义特性留下的接口。
  • Conditional          和#if一个功能(尽量使用它)
  • Obsolete(废弃)              标记不能调用该方法,并提示信息,可设为error(默认警告)
  • DebuggerStepThrough   单步调试代码时,不希望进入函数,只希望调用他

  • ......

其基本功能都是留一个标记,当程序运行到该标记时,判断该特性选择如何运行。

使用:

 

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]
  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

例子:

[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]
public class DeBugInfo : System.Attribute

该例子自定义了个特性,该特性适用范围是所有元素,多用,不被继承

可以简写成

[AttributeUsage(AllowMultiple = true)]
public class DeBugInfo : System.Attribute

[Conditional("DEBUG")]    DEBUG是标记之一,其他标记请上网查询 
[Obsolete( message, iserror )] (提示信息。 枚举,ture是error,false是wrring)

特性的更详细用法 https://www.cnblogs.com/moonache/p/7532006.html

 

反射

反射是指动态的获得程序集(可以认为是类,程序集包含模块,而模块包含类型,类型又包含成员)

使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

通常反射多用于dll的动态加载和工厂模式。

最简单的用法是:可以找出对象所包含成员变量,属性,方法等,然后进行赋值,调用等。
但这些成员变量,属性,方法的名称在编程的时候都是不知道的。

举个例子,

你提供接口别人提供这个接口的实现,但是不给你源码不给你方法说明,那么这个时候你就只有用反射调用它的实现,以接口形式来使用了。常见的IOC就是这样组织人们这样干的东西。

使用:

反射常用的类有Assembly,Activator,Type这几个类

Assembly加载dll(当前程序集就不需要)

 

Activator  动态创建程一个类的实例

 

Type(最常用)表示一个类型, 获得类的元素。

using System;
using System.Reflection;
namespace BugFixApplication
{
   // 一个自定义特性 BugFix 被赋给类及其成员
   [AttributeUsage(AttributeTargets.Class |
   AttributeTargets.Constructor |
   AttributeTargets.Field |
   AttributeTargets.Method |
   AttributeTargets.Property,
   AllowMultiple = true)]

   public class DeBugInfo : System.Attribute
   {
      private int bugNo;
      private string developer;
      private string lastReview;
      public string message;

      public DeBugInfo(int bg, string dev, string d)
      {
         this.bugNo = bg;
         this.developer = dev;
         this.lastReview = d;
      }

      public int BugNo
      {
         get
         {
            return bugNo;
         }
      }
      public string Developer
      {
         get
         {
            return developer;
         }
      }
      public string LastReview
      {
         get
         {
            return lastReview;
         }
      }
      public string Message
      {
         get
         {
            return message;
         }
         set
         {
            message = value;
         }
      }
   }
   [DeBugInfo(45, "Zara Ali", "12/8/2012",
        Message = "Return type mismatch")]
   [DeBugInfo(49, "Nuha Ali", "10/10/2012",
        Message = "Unused variable")]
   class Rectangle
   {
      // 成员变量
      protected double length;
      protected double width;
      public Rectangle(double l, double w)
      {
         length = l;
         width = w;
      }
      [DeBugInfo(55, "Zara Ali", "19/10/2012",
           Message = "Return type mismatch")]
      public double GetArea()
      {
         return length * width;
      }
      [DeBugInfo(56, "Zara Ali", "19/10/2012")]
      public void Display()
      {
         Console.WriteLine("Length: {0}", length);
         Console.WriteLine("Width: {0}", width);
         Console.WriteLine("Area: {0}", GetArea());
      }
   }//end class Rectangle  
   
   class ExecuteRectangle
   {
      static void Main(string[] args)
      {
         Rectangle r = new Rectangle(4.5, 7.5);
         r.Display();
         Type type = typeof(Rectangle);
         // 遍历 Rectangle 类的特性
         foreach (Object attributes in type.GetCustomAttributes(false))
         {
            DeBugInfo dbi = (DeBugInfo)attributes;
            if (null != dbi)
            {
               Console.WriteLine("Bug no: {0}", dbi.BugNo);
               Console.WriteLine("Developer: {0}", dbi.Developer);
               Console.WriteLine("Last Reviewed: {0}",
                                        dbi.LastReview);
               Console.WriteLine("Remarks: {0}", dbi.Message);
            }
         }
         
         // 遍历方法特性
         foreach (MethodInfo m in type.GetMethods())
         {
            foreach (Attribute a in m.GetCustomAttributes(true))
            {
               DeBugInfo dbi = (DeBugInfo)a;
               if (null != dbi)
               {
                  Console.WriteLine("Bug no: {0}, for Method: {1}",
                                                dbi.BugNo, m.Name);
                  Console.WriteLine("Developer: {0}", dbi.Developer);
                  Console.WriteLine("Last Reviewed: {0}",
                                                dbi.LastReview);
                  Console.WriteLine("Remarks: {0}", dbi.Message);
               }
            }
         }
         Console.ReadLine();
      }
   }
}

输出结果:

一些写的很好的总结https://www.cnblogs.com/ckym/p/9777956.html    https://blog.csdn.net/hanjun0612/article/details/80430365

属性和索引器

属性简单的说可以理解为类的成员,你可以使用属性访问器get;set;来设置属性(成员变量),一般用来设置私有变量

为什么需要属性?---》可能是不用属性,外界想访问私有变量要写公共接口访问比较麻烦

面是一个抽象类的属性使用例子,抽象类的属性前面要加abstract修饰词。

using System;
namespace Demo.cs
{
    class Program
    {
        public abstract class Person
        {
            public abstract string Name { get; set; }
            public abstract int Age { get; set; }
        }
        public class Student : Person
        {
            public string Code { get; set; } = "N.A";
            public override string Name { get; set; } = "N.A";
            public override int Age { get; set; } = 0;
            public override string ToString()
            {
                return $"Code:{Code},Name:{Name},Age:{Age}";
            }
        }

        static void Main(string[] args)
        {
            var s = new Student()
            {
                Code = "001",
                Name = "Zara",
                Age = 10
            };
            System.Console.WriteLine($"Student Info:={s}");

            s.Age++;
            System.Console.WriteLine($"Student Info:={s}");
        }
    }
}

索引简单来说就是允许使用者可以以 “类名+下标”来访问类中的成员变量,访问的成员变量由你来定,设置的方法是使用属性访问器的get;set来设置。(能使用索引的类都是要建立索引的,不是天生就能用)

using System;
namespace IndexerApplication
{
   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;
            }
         }
      }

      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";
         for ( int i = 0; i < IndexedNames.size; i++ )
         {
            Console.WriteLine(names[i]);
         }
         Console.ReadKey();
      }
   }
}

索引还可以被重载,一般是把int下标重载成string下标,然后get那里返回int的下标地址(使用名字确定下标位置)。

//和前面的一样
 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";
        
         // 使用带有 string 参数的第二个索引器
         Console.WriteLine(names["Nuha"]);   //dos窗口显示 2
         Console.ReadKey();
      }
   }
}

委托和事件

委托事件和qt的信号与槽都差不多,都是实现类与类的通信。注意下写法就行了。

C#事件机制的实现包括以下几步:

1、 事件发布者定义一个委托类型;

 //定义委托
 public delegate void delegateRun();

......

 public void Run() //和委托函数类型一样的函数(返回值,参数一样,类似delegateRun()是函数指针,Run()是实际函数)
        {
            Console.WriteLine("运动员开始比赛");
        }

2、 事件发布者定义一个事件,并且关联到已经定义的委托上。

//定义一个事件(发布事件)
 public event delegateRun eventRun;

关键字是event, delegateRun是委托函数的类型, eventRun是事件的标志,或者说名字。

3、 事件订阅者需要产生一个委托实例,并把它添加到委托列表。

 //3.订阅事件,或者说注册一个事件 。c#重载了+=作为订阅事件的方法
 judgment.eventRun += new Judgment.delegateRun(runsport.Run); 

c#重载了 += 运算符来作为事件和委托关联的符号。

例子·:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 练习9_事件2_
{
    class Judgment
    {
        //1.定义委托
        public delegate void delegateRun();
        //2.定义一个事件(发布事件)
        public event delegateRun eventRun;
        //3.引发事件的方法,或者条件 参数不能用const??
        public void Begin( ref int i)
        {
            //被引发的事件
            if (i == 1)
                eventRun();
            else
                Console.WriteLine("比赛还没开始,稍等一下");
        }
    }
    //处理事件的类,即事件的发布者
    class RunSporte
    {
        //4.定义事件的处理方法(既是委托的方法)这个一般会单独开一个处理类
        public void Run()
        {
            Console.WriteLine("运动员开始比赛");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //int i = 0;
            //1.实例化事件发布者
            RunSporte runsport = new RunSporte();
            //2.实例化事件订阅者
            Judgment judgment = new Judgment();
            //3.订阅事件,或者说注册一个事件 。c#重载了+=作为订阅事件的方法
            judgment.eventRun += new Judgment.delegateRun(runsport.Run);
            //4.引发事件,既是调用该函数
            Console.WriteLine("开始比赛就输入1,");
            int i = Convert.ToInt32(Console.ReadLine());//输入整数,c#的输入只有readline,输入字符串,之后转换成i
            judgment.Begin(ref i);
            Console.ReadKey();
        }
    }
}

例2:

using System;
namespace SimpleEvent
{
  using System;
  /***********发布器类***********/
  public class EventTest
  {
    private int value;

    public delegate void NumManipulationHandler();


    public event NumManipulationHandler ChangeNum;
    protected virtual void OnNumChanged()
    {
      if ( ChangeNum != null )
      {
        ChangeNum(); /* 事件被触发 */
      }else {
        Console.WriteLine( "event not fire" );
        Console.ReadKey(); /* 回车继续 */
      }
    }


    public EventTest()
    {
      int n = 5;
      SetValue( n );
    }


    public void SetValue( int n )
    {
      if ( value != n )
      {
        value = n;
        OnNumChanged();
      }
    }
  }


  /***********订阅器类***********/

  public class subscribEvent
  {
    public void printf()
    {
      Console.WriteLine( "event fire" );
      Console.ReadKey(); /* 回车继续 */
    }
  }

  /***********触发***********/
  public class MainClass
  {
    public static void Main()
    {
      EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
      subscribEvent v = new subscribEvent(); /* 实例化对象 */
      e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
      e.SetValue( 7 );
      e.SetValue( 11 );
    }
  }
}

匿名函数与lambda

c#有匿名函数 (匿名方法是通过使用 delegate 关键字创建委托实例来声明的。),也有lambda表达式,比较来说lambda表达式更好用,建议多使用c#的lambda表达式就行了

前面声明委托时就是用的匿名函数  public delegate void delegateRun(); 关键字是delegate。

//匿名函数的写法是: 

pubilc delegate mydelegate(int i);

mydelegate test (int i)

{

  // 函数主体。

}

这样太麻烦了,可以进华为

Func<int > test = delegate (int i)

{

  // 函数主体。

}

省略掉声明,更简单的就是lamdba

Func< int > test =  { } => { //函数主体  }; //换成Lambda表达式形式 

注意:Func后面可以跟很多类型,最后一个类型则是返回值类型,如Func<int, int, int >前面2个是参数,最后一个是返回值,只有一个参数时看函数是否有返回值,总之,有返回值最后一个是返回值,没有返回值最后一个是参数。

lambda表达式   " ( )  =>  { }; "

Lambda 表达式是一种可用于创建 委托 或 表达式目录树 类型的 匿名函数 ,

Lambda 表达式对于编写 LINQ 查询表达式特别有用。

写法:lamdba写法

集合与泛型

c#提供了非泛型的动态数组(ArrayList),栈,队列,哈希表等集合,这里就不多讲了

c#也提供泛型,使用也比较简单;
 

using System;
using System.Collections.Generic;

namespace GenericMethodAppl
{
    class Program
    {
        static void Swap<T>(ref T lhs, ref T rhs)
        {
            T temp;
            temp = lhs;
            lhs = rhs;
            rhs = temp;
        }
        static void Main(string[] args)
        {
            int a, b;
            char c, d;
            a = 10;
            b = 20;
            c = 'I';
            d = 'V';

            // 在交换之前显示值
            Console.WriteLine("Int values before calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values before calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);

            // 调用 swap
            Swap<int>(ref a, ref b);
            Swap<char>(ref c, ref d);

            // 在交换之后显示值
            Console.WriteLine("Int values after calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values after calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);
            Console.ReadKey();
        }
    }
}

泛型的集合List<T>也是非常常用的,特别是出现linq后。

   //创建
            List<Student> list = new List<Student>();
            //添加
            list.Add(new Student { RealName = "学生001" });
            //读取
            Student student = list[0];
            //删除
            list.RemoveAt(0);
            //清空
            list.Clear();

关于array,arraylist和list的区别

集合总结

不安全代码

这是c#weile使用指针搞出来的,

在unsafe指定的域内就可以使用指针

using System;
namespace UnsafeCodeApplication
{
   class Program
   {
      public static void Main()
      {
         unsafe
         {
            int var = 20;
            int* p = &var;
            Console.WriteLine("Data is: {0} " , var);
            Console.WriteLine("Data is: {0} " , p->ToString());
            Console.WriteLine("Address is: {0} " , (int)p);
         }
         Console.ReadKey();
      }
   }
}

多线程

多线程的使用都一样的,记住一些常用的成员(属性)和成员方法,比如start()开启线程,  About() 销毁线程,  sleep()暂停线程,  Join()等待线程结束,  IsAlive 得到线程状态, CurrentThread得到正在运行的线程属性。等

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

例2:

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            try
            {

                Console.WriteLine("Child thread starts");
                // 计数到 10
                for (int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("Child Thread Completed");

            }
            catch (ThreadAbortException e)
            {
                Console.WriteLine("Thread Abort Exception");
            }
            finally
            {
                Console.WriteLine("Couldn't catch the Thread Exception");
            }

        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            // 停止主线程一段时间
            Thread.Sleep(2000);
            // 现在中止子线程
            Console.WriteLine("In Main: Aborting the Child thread");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

里面的写法可以写成

            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(CallToChildThread);
            childThread.Start();
            Console.ReadKey();

其实这才是c#的语法,前面那种是.net支持的语法,c#编译器互译帮你写成那样。

下表列出了 Thread 类的一些常用的 属性

属性描述
CurrentContext获取线程正在其中执行的当前上下文。
CurrentCulture获取或设置当前线程的区域性。
CurrentPrincipal获取或设置线程的当前负责人(对基于角色的安全性而言)。
CurrentThread获取当前正在运行的线程。
CurrentUICulture获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。
ExecutionContext获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。
IsAlive获取一个值,该值指示当前线程的执行状态。
IsBackground获取或设置一个值,该值指示某个线程是否为后台线程。
IsThreadPoolThread获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId获取当前托管线程的唯一标识符。
Name获取或设置线程的名称。
Priority获取或设置一个值,该值指示线程的调度优先级。
ThreadState获取一个值,该值包含当前线程的状态。

下表列出了 Thread 类的一些常用的 方法

序号方法名 & 描述
1public void Abort()
在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。
2public static LocalDataStoreSlot AllocateDataSlot()
在所有的线程上分配未命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
3public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
在所有线程上分配已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
4public static void BeginCriticalRegion()
通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常的影响可能会危害应用程序域中的其他任务。
5public static void BeginThreadAffinity()
通知主机托管代码将要执行依赖于当前物理操作系统线程的标识的指令。
6public static void EndCriticalRegion()
通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常仅影响当前任务。
7public static void EndThreadAffinity()
通知主机托管代码已执行完依赖于当前物理操作系统线程的标识的指令。
8public static void FreeNamedDataSlot(string name)
为进程中的所有线程消除名称与槽之间的关联。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
9public static Object GetData( LocalDataStoreSlot slot )
在当前线程的当前域中从当前线程上指定的槽中检索值。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
10public static AppDomain GetDomain()
返回当前线程正在其中运行的当前域。
11public static AppDomain GetDomainID()
返回唯一的应用程序域标识符。
12public static LocalDataStoreSlot GetNamedDataSlot( string name )
查找已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
13public void Interrupt()
中断处于 WaitSleepJoin 线程状态的线程。
14public void Join()
在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。此方法有不同的重载形式。
15public static void MemoryBarrier()
按如下方式同步内存存取:执行当前线程的处理器在对指令重新排序时,不能采用先执行 MemoryBarrier 调用之后的内存存取,再执行 MemoryBarrier 调用之前的内存存取的方式。
16public static void ResetAbort()
取消为当前线程请求的 Abort。
17public static void SetData( LocalDataStoreSlot slot, Object data )
在当前正在运行的线程上为此线程的当前域在指定槽中设置数据。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
18public void Start()
开始一个线程。
19public static void Sleep( int millisecondsTimeout )
让线程暂停一段时间。
20public static void SpinWait( int iterations )
导致线程等待由 iterations 参数定义的时间量。
21public static byte VolatileRead( ref byte address )
public static double VolatileRead( ref double address )
public static int VolatileRead( ref int address )
public static Object VolatileRead( ref Object address )

读取字段值。无论处理器的数目或处理器缓存的状态如何,该值都是由计算机的任何处理器写入的最新值。此方法有不同的重载形式。这里只给出了一些形式。
22public static void VolatileWrite( ref byte address, byte value )
public static void VolatileWrite( ref double address, double value )
public static void VolatileWrite( ref int address, int value )
public static void VolatileWrite( ref Object address, Object value )

立即向字段写入一个值,以使该值对计算机中的所有处理器都可见。此方法有不同的重载形式。这里只给出了一些形式。
23public static bool Yield()
导致调用线程执行准备好在当前处理器上运行的另一个线程。由操作系统选择要执行的线程。

关于线程同步这里就不写了,有机会会在写一篇线程同步的(懒了=  =)

 

大部分笔记来自runoob的c#学习模块,有兴趣的可以直接去看,推荐书籍《c#入门经典》

后续补充。。。。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值