C# study notes advanced (in rookie tutorial)

C# Attributes

Attribute (Attribute) is a declarative label used to convey behavioral information of various elements (such as classes, methods, structures, enumerations, components, etc.) in the program at runtime. You can add declarative information to your program by using attributes. A declarative tag is described by placing square brackets ([ ]) in front of the element it applies to.

Attributes are used to add metadata such as compiler directives and comments, descriptions, methods, classes, and other information. The .Net framework provides two types of attributes: predefined attributes and custom attributes.

Prescribed characteristics (Attribute)

The syntax for specifying attributes is as follows:

[attribute(positional_parameters, name_parameter = value, ...)]
element

The name and value of an attribute is specified within square brackets, before the element to which it applies. positional_parameters specifies required information, and name_parameter specifies optional information.

Predefined attributes (Attribute)

The .Net framework provides three predefined features:

  • AttributeUsage
  • Conditional
  • Obsolete

AttributeUsage

The predefined attribute  AttributeUsage  describes how to use a custom attribute class. It specifies the type of item to which the attribute can be applied.

The syntax for specifying this property is as follows:

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]

in:

  • The parameter validon specifies the language elements on which the attribute can be placed.  It is a combination of the values ​​of the enumerator  AttributeTargets . The default value is  AttributeTargets.All .
  • The parameter  allowmultiple (optional) provides a boolean value for the attribute's  AllowMultiple  property. If true, the feature is multipurpose. The default is false (single-use).
  • The parameter  inherited (optional) provides a boolean value for the attribute's  Inherited  property. If true, the attribute can be inherited by derived classes. The default value is false (not inherited).

For example:

[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]

Conditional

This predefined attribute marks a conditional method whose execution depends on the specified preprocessing identifier.

It causes conditional compilation of method calls, depending on the specified value, such as  Debug  or  Trace . For example, displaying the value of a variable while debugging code.

The syntax for specifying this property is as follows:

[Conditional(
   conditionalSymbol
)]

For example:

[Conditional("DEBUG")]

The following example demonstrates this feature:

#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
    [Conditional("DEBUG")]
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }
}
class Test
{
    static void function1()
    {
        Myclass.Message("In Function 1.");
        function2();
    }
    static void function2()
    {
        Myclass.Message("In Function 2.");
    }
    public static void Main()
    {
        Myclass.Message("In Main function.");
        function1();
        Console.ReadKey();
    }
}

When the above code is compiled and executed, it produces the following result:

In Main function.
In Function 1.
In Function 2.

Obsolete

This predefined property marks program entities that should not be used. It allows you to tell the compiler to discard a specific target element. For example, when a new method is used in a class, but you still want to keep the old method in the class, you can mark it as obsolete by displaying a message that the new method should be used instead of the old method. of).

The syntax for specifying this property is as follows:

[Obsolete(
   message
)]
[Obsolete(
   message,
   iserror
)]

in:

  • The parameter  message , is a string describing why the item is obsolete and what should be used instead.
  • The parameter  iserror , is a boolean value. If the value is true, the compiler should treat the use of this item as an error. The default is false (the compiler generates a warning).

The following example demonstrates this feature:

using System;
public class MyClass
{
   [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
   static void OldMethod()
   {
      Console.WriteLine("It is the old method");
   }
   static void NewMethod()
   {
      Console.WriteLine("It is the new method");
   }
   public static void Main()
   {
      OldMethod();
   }
}

When trying to compile the program, the compiler gives an error message stating:

Don't use OldMethod, use NewMethod instead

Create a custom attribute (Attribute)

The .Net framework allows the creation of custom attributes that store declarative information that can be retrieved at runtime. This information can be associated with any target element depending on the design criteria and application needs.

Creating and using custom properties consists of four steps:

  • Declare custom attributes
  • Build custom properties
  • Apply custom attributes on target program elements
  • Access properties via reflection

The last step involves writing a simple program to read the metadata in order to find various symbols. Metadata is data and information used to describe other data. The program should use reflection to access properties at runtime. We will discuss this in detail in the next chapter.

Declare custom attributes

A new custom attribute should be derived from  the System.Attribute  class. For example:

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

public class DeBugInfo : System.Attribute

In the above code, we have declared a   custom attribute called DeBugInfo .

Build custom properties

Let's build a   custom attribute called DebugInfo that will store information obtained by the debugger. It stores the following information:

  • The code number of the bug
  • The name of the developer who identified the bug
  • The date the code was last reviewed
  • A string message storing developer flags

Our  DebugInfo  class will have three private properties to store the first three information and one public property to store the message. So bug number, developer name, and review date will be required positional parameters of the DeBugInfo class, and message will be an optional named parameter.

Every attribute must have at least one constructor. Required positional parameters should be passed through the constructor. The following code demonstrates  the DebugInfo  class:

// 一个自定义特性 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;
      }
  }
}

Apply custom properties

Apply an attribute by placing it immediately before its target:

[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());
  }
}

C# Reflection

Reflection refers to the ability of a program to access, detect, and modify its own state or behavior.

Assemblies contain modules, and modules contain types, which in turn contain members. Reflection provides objects that encapsulate assemblies, modules, and types.

You can use reflection to dynamically create instances of types, bind types to existing objects, or obtain types from existing objects. You can then call methods on the type or access its fields and properties.

Advantages and disadvantages

advantage:

  • 1. Reflection improves the flexibility and scalability of the program.
  • 2. Reduce coupling and improve self-adaptive ability.
  • 3. It allows programs to create and control objects of any class without hard-coding the target class in advance.

shortcoming:

  • 1. Performance issues: Using reflection is basically an interpretation operation, which is much slower than direct code when used for field and method access. Therefore, the reflection mechanism is mainly used in system frameworks that require high flexibility and scalability, and is not recommended for ordinary programs.
  • 2. The use of reflection will blur the internal logic of the program; programmers want to see the logic of the program in the source code, but reflection bypasses the technology of the source code, which will cause maintenance problems, and the reflection code is more complicated than the corresponding direct code .

Uses of Reflection

Reflection has the following uses:

  • It allows viewing attribute information at runtime.
  • It allows inspecting various types in a collection, as well as instantiating those types.
  • It allows late binding of methods and properties (property).
  • It allows to create new types at runtime and then use these types to perform some tasks.

view metadata

We have already mentioned in the above chapters that attribute information can be viewed using Reflection.

The MemberInfo object  of the  System.Reflection class  needs to be initialized to discover the attributes associated with the class. To do this, you can define an object of the target class, as follows:

System.Reflection.MemberInfo info = typeof(MyClass);

The following program demonstrates this:

using System;

[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute : System.Attribute
{
   public readonly string Url;

   public string Topic  // Topic 是一个命名(named)参数
   {
      get
      {
         return topic;
      }
      set
      {

         topic = value;
      }
   }

   public HelpAttribute(string url)  // url 是一个定位(positional)参数
   {
      this.Url = url;
   }

   private string topic;
}
[HelpAttribute("Information on the class MyClass")]
class MyClass
{
}

namespace AttributeAppl
{
   class Program
   {
      static void Main(string[] args)
      {
         System.Reflection.MemberInfo info = typeof(MyClass);
         object[] attributes = info.GetCustomAttributes(true);
         for (int i = 0; i < attributes.Length; i++)
         {
            System.Console.WriteLine(attributes[i]);
         }
         Console.ReadKey();

      }
   }
}

When the above code is compiled and executed, it will display   the custom attributes attached to the class MyClass :

HelpAttribute

In this example, we will use the DebugInfo  feature  created in the previous chapter  , and use reflection (Reflection) to read  the metadata in the Rectangle class.

using System;
using System.Reflection;//System.Reflection 类的 MemberInfo用于发现与类相关的特性(attribute)。
namespace BugFixApplication
{
    // 一个自定义特性 BugFix 被赋给类及其成员
    [AttributeUsage
    #region//定义了特性能被放在那些前面        
        (AttributeTargets.Class |//规定了特性能被放在class的前面
        AttributeTargets.Constructor |//规定了特性能被放在构造函数的前面
        AttributeTargets.Field |//规定了特性能被放在域的前面
        AttributeTargets.Method |//规定了特性能被放在方法的前面
        AttributeTargets.Property,//规定了特性能被放在属性的前面
    #endregion
        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;
        }
        #region//定义对应的调用,返回对应值value
        public int BugNo
        {
            get 
            {
                return bugNo;
            }
        }
        public string Developer
        {
            get
            {
                return developer;
            }
        }
        public string LastReview
        {
            get
            {
                return lastReview;
            }
        }
        //前面有public string message;
        public string Message//定义了可以通过Message = "",来对message进行赋值。
                             //因为不在构造函数中,所以是可选的
        {
            get
            {return message;}
            set
            {message = value;}
        }
        /*
         * 这部分可以简写如下
         * public string Message{get;set;}
        */
    }
    #endregion

    [DeBugInfo(45, "Zara Ali", "12/8/2012",
         Message = "Return type mismatch")]
    [DeBugInfo(49, "Nuha Ali", "10/10/2012",
         Message = "Unused variable")]//前面定义时的AllowMultiple=ture允许了多次使用在同一地方
    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")]//因为message是可选项,所以可以不给出
                                                 //不给出即为null,为空白
        public void Display()
        {
            Console.WriteLine("Length: {0}", length);
            Console.WriteLine("Width:{0}", width);
            Console.WriteLine("Area:{0}", GetArea());//常规打印
        }
    }

    class ExecuteRectangle
    {
        static void Main(string[] args)//程序入口
        {
            Rectangle r = new Rectangle(4.5, 7.5);//实例化
            r.Display();//执行打印长、宽、面积

            Type type = typeof(Rectangle);//让type对应这个Rectangle类
            // 遍历 Rectangle 类的特性
            foreach (Object attributes in type.GetCustomAttributes(false))//遍历Rectangle的所有特性
            {
                DeBugInfo dbi = (DeBugInfo)attributes;//强制转换
                if(null != dbi)//dbi非空
                {
                    Console.WriteLine("Bug on: {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())//遍历Rectangle类下的所有方法
            {
                foreach (Attribute a in m.GetCustomAttributes(true))//遍历每个方法的特性
                {
                    DeBugInfo dbi = a as DeBugInfo;//通过 object 声明对象,是用了装箱和取消装箱的概念.
                                                   //也就是说 object 可以看成是所有类型的父类。
                                                   //因此 object 声明的对象可以转换成任意类型的值。
                                                   //通过拆装箱代替强制转换
                    if (null !=dbi)//同理打印
                    {
                        Console.WriteLine("BugFixApplication 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.ReadKey();
        }
    }
}

When the above code is compiled and executed, it produces the following result:

Length: 4.5
Width: 7.5
Area: 33.75
Bug No: 49
Developer: Nuha Ali
Last Reviewed: 10/10/2012
Remarks: Unused variable
Bug No: 45
Developer: Zara Ali
Last Reviewed: 12/8/2012
Remarks: Return type mismatch
Bug No: 55, for Method: GetArea
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: 

C# property (Property)

Properties  are named members of classes, structures, and interfaces. A member variable or method in a class or structure is called  a field . Properties are an extension of Fields and can be accessed using the same syntax. They use  accessors  to allow private field values ​​to be read, written, or manipulated.

Property (Property) will not determine the storage location. Instead, they have accessors that can read, write, or compute their values  .

For example, there is a class called Student with private fields for age, name, and code. We cannot directly access these fields outside the scope of the class, but we can have properties that access these private fields.

Accessors

A property's accessors contain executable statements that help get (read or compute) or set (write) the property. An accessor declaration can contain a get accessor, a set accessor, or both. For example:

// 声明类型为 string 的 Code 属性
public string Code
{
   get
   {
      return code;
   }
   set
   {
      code = value;
   }
}

// 声明类型为 string 的 Name 属性
public string Name
{
   get
   {
     return name;
   }
   set
   {
     name = value;
   }
}

// 声明类型为 int 的 Age 属性
public int Age
{
   get
   {
      return age;
   }
   set
   {
      age = value;
   }
}

The following example demonstrates the usage of properties:

using System;
namespace runoob
{
   class Student
   {

      private string code = "N.A";
      private string name = "not known";
      private int age = 0;

      // 声明类型为 string 的 Code 属性
      public string Code
      {
         get
         {
            return code;
         }
         set
         {
            code = value;
         }
      }
   
      // 声明类型为 string 的 Name 属性
      public string Name
      {
         get
         {
            return name;
         }
         set
         {
            name = value;
         }
      }

      // 声明类型为 int 的 Age 属性
      public int Age
      {
         get
         {
            return age;
         }
         set
         {
            age = value;
         }
      }
      public override string ToString()
      {
         return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
      }
    }
    class ExampleDemo
    {
      public static void Main()
      {
         // 创建一个新的 Student 对象
         Student s = new Student();
           
         // 设置 student 的 code、name 和 age
         s.Code = "001";
         s.Name = "Zara";
         s.Age = 9;
         Console.WriteLine("Student Info: {0}", s);
         // 增加年龄
         s.Age += 1;
         Console.WriteLine("Student Info: {0}", s);
         Console.ReadKey();
       }
   }
}

When the above code is compiled and executed, it produces the following result:

Student Info: Code = 001, Name = Zara, Age = 9
Student Info: Code = 001, Name = Zara, Age = 10

Abstract Properties

Abstract classes can have abstract properties which should be implemented in derived classes. The following program illustrates this:

using System;
namespace runoob
{
   public abstract class Person
   {
      public abstract string Name
      {
         get;
         set;
      }
      public abstract int Age
      {
         get;
         set;
      }
   }
   class Student : Person
   {

      private string code = "N.A";
      private string name = "N.A";
      private int age = 0;

      // 声明类型为 string 的 Code 属性
      public string Code
      {
         get
         {
            return code;
         }
         set
         {
            code = value;
         }
      }
   
      // 声明类型为 string 的 Name 属性
      public override string Name
      {
         get
         {
            return name;
         }
         set
         {
            name = value;
         }
      }

      // 声明类型为 int 的 Age 属性
      public override int Age
      {
         get
         {
            return age;
         }
         set
         {
            age = value;
         }
      }
      public override string ToString()
      {
         return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
      }
   }
   class ExampleDemo
   {
      public static void Main()
      {
         // 创建一个新的 Student 对象
         Student s = new Student();
           
         // 设置 student 的 code、name 和 age
         s.Code = "001";
         s.Name = "Zara";
         s.Age = 9;
         Console.WriteLine("Student Info:- {0}", s);
         // 增加年龄
         s.Age += 1;
         Console.WriteLine("Student Info:- {0}", s);
         Console.ReadKey();
       }
   }
}

When the above code is compiled and executed, it produces the following result:

Student Info: Code = 001, Name = Zara, Age = 9
Student Info: Code = 001, Name = Zara, Age = 10

C# Indexer (Indexer)

Indexer (Indexer)  allows an object to be accessed using a subscript like an array.

When you define an indexer for a class, the class behaves like a  virtual array  . You can use the array access operator [ ] to access the members of this class.

grammar

The syntax for a one-dimensional indexer is as follows:

element-type this[int index]
{
   // get 访问器
   get
   {
      // 返回 index 指定的值
   }

   // set 访问器
   set
   {
      // 设置 index 指定的值
   }
}

The purpose of the indexer (Indexer)

A declaration of the behavior of an indexer is somewhat similar to a property. Just like properties, you can   define indexers using get  and  set accessors. However, a property returns or sets a specific data member, while an indexer returns or sets a specific value of an object instance. In other words, it divides the instance data into smaller parts, and indexes each part, gets or sets each part.

Defining a property includes providing the property name. An indexer is defined without a name, but with  the this  keyword, which points to the object instance. The following example demonstrates this concept:

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();
      }
   }
}

When the above code is compiled and executed, it produces the following result:

Zara
Riz
Nuha
Asif
Davinder
Sunil
Rubic
N. A.
N. A.
N. A.

Overloading the Indexer (Indexer)

Indexer (Indexer) can be overloaded. Indexers can also be declared with multiple parameters, and each parameter can be of a different type. There is no need for the indexer to be an integer. C# allows indexers to be of other types, such as strings.

The following example demonstrates overloading indexers:

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;
            }
         }
      }
      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();
      }
   }
}

When the above code is compiled and executed, it produces the following result:

Zara
Riz
Nuha
Asif
Davinder
Sunil
Rubic
N. A.
N. A.
N. A.
2

The simplest indexer:

  /// <summary>
    /// 最简单的索引器
    /// </summary>
    public class IDXer
    {
        private string[] name=new string[10];

        //索引器必须以this关键字定义,其实这个this就是类实例化之后的对象
        public string this[int index]
        {
            get 
            {
                return name[index];
            }
            set
            {
                name[index] = value;
            }
        }  
    }
        
    public class Program
    {
        static void Main(string[] args)
        {
            //最简单索引器的使用           
            IDXer indexer = new IDXer();
            //“=”号右边对索引器赋值,其实就是调用其set方法
            indexer[0] = "张三";
            indexer[1] = "李四";
            //输出索引器的值,其实就是调用其get方法
            Console.WriteLine(indexer[0]);
            Console.WriteLine(indexer[1]);
            Console.ReadKey();
        }
    }

C# delegate (Delegate)

A delegate in C# is similar to a pointer to a function in C or C++. A delegate  is a reference type variable that holds a reference to a method. References can be changed at runtime.

Delegate is especially used to implement events and callback methods. All delegates (Delegate) are derived from  the System.Delegate  class.

Declare the delegate (Delegate)

A delegate declaration determines the methods that can be referenced by the delegate. A delegate can point to a method with the same label as it.

For example, suppose there is a delegate:

public delegate int MyDelegate (string s);

The above delegate can be used to refer to any method that takes a single  string  parameter and returns a  variable of type int  .

The syntax for declaring a delegate is as follows:

delegate <return type> <delegate-name> <parameter list>

Instantiate the delegate (Delegate)

Once the delegate type is declared, the delegate object must be created using  the new  keyword and is associated with a specific method. When creating a delegate, the parameters passed to  the new  statement are written just like a method call, but without parameters. For example:

public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);

The following example demonstrates the declaration, instantiation, and use of a delegate that can be used to refer to a method that takes an integer parameter and returns an integer value.

using System;

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 nc1 = new NumberChanger(AddNum);
         NumberChanger nc2 = new NumberChanger(MultNum);
         // 使用委托对象调用方法
         nc1(25);
         Console.WriteLine("Value of Num: {0}", getNum());
         nc2(5);
         Console.WriteLine("Value of Num: {0}", getNum());
         Console.ReadKey();
      }
   }
}

When the above code is compiled and executed, it produces the following result:

Value of Num: 35
Value of Num: 175

Multicasting of a Delegate

Delegate objects can be combined using the "+" operator. A merge delegate calls the two delegates it merges. Only delegates of the same type can be merged. The "-" operator can be used to remove the component delegate from the merged delegate.

Using this useful feature of delegates, you can create an invocation list of methods to invoke when the delegate is invoked. This is known as delegated  multicasting (multicasting) , also known as multicasting. The following program demonstrates delegated multicasting:

using System;

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();
      }
   }
}

When the above code is compiled and executed, it produces the following result:

Value of Num: 75

Purpose of Delegate

The following example demonstrates the use of delegates. The delegate  printString  can be used to reference a method that takes a string as input and returns nothing.

We use this delegate to call two methods, the first to print the string to the console and the second to print the string to a file:

using System;
using System.IO;

namespace DelegateAppl
{
   class PrintString
   {
      static FileStream fs;
      static StreamWriter sw;
      // 委托声明
      public delegate void printString(string s);

      // 该方法打印到控制台
      public static void WriteToScreen(string str)
      {
         Console.WriteLine("The String is: {0}", str);
      }
      // 该方法打印到文件
      public static void WriteToFile(string s)
      {
         fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
         sw = new StreamWriter(fs);
         sw.WriteLine(s);
         sw.Flush();
         sw.Close();
         fs.Close();
      }
      // 该方法把委托作为参数,并使用它调用方法
      public static void sendString(printString ps)
      {
         ps("Hello World");
      }
      static void Main(string[] args)
      {
         printString ps1 = new printString(WriteToScreen);
         printString ps2 = new printString(WriteToFile);
         sendString(ps1);
         sendString(ps2);
         Console.ReadKey();
      }
   }
}

When the above code is compiled and executed, it produces the following result:

The String is: Hello World

example:

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

delegate int NumberChanger(int n);

namespace fuxi
{
   class TestDelegate
    {
        // 小张类
        public class MrZhang
        {
            // 其实买车票的悲情人物是小张
            public static void BuyTicket()
            {
                Console.WriteLine("NND,每次都让我去买票,鸡人呀!");
            }

            public static void BuyMovieTicket()
            {
                Console.WriteLine("我去,自己泡妞,还要让我带电影票!");
            }
        }

        //小明类
        class MrMing
        {
            // 声明一个委托,其实就是个“命令”
            public delegate void BugTicketEventHandler();

            public static void Main(string[] args)
            {
                // 这里就是具体阐述这个命令是干什么的,本例是MrZhang.BuyTicket“小张买车票”
                BugTicketEventHandler myDelegate = new BugTicketEventHandler(MrZhang.BuyTicket);
                myDelegate += MrZhang.BuyMovieTicket;
                // 这时候委托被附上了具体的方法
                myDelegate();
                Console.ReadKey();
            }
        }
    }
   
    }

C# event (Event)

Event (Event)  is basically a user operation, such as keystroke, click, mouse movement, etc., or some prompt information, such as a notification generated by the system. Applications need to respond to events when they occur. For example, interrupt.

In C#, the event mechanism is used to realize the communication between threads.

Using delegates through events

Events are declared and generated in a class, and event handlers are associated by using delegates in the same class or in other classes. Classes containing events are used to publish events. This is called  the publisher  class. Other classes that receive the event are called  subscriber  classes. Events use  a publish-subscribe (publisher-subscriber)  model.

A publisher  is an object that contains event and delegate definitions. The connection between events and delegates is also defined in this object. Objects of the publisher class call this event and notify other objects.

A subscriber  is an object that accepts events and provides event handlers. A delegate in a publisher class calls a method (event handler) in a subscriber class.

Declare Events (Event)

To declare an event inside a class, the delegate type of the event must first be declared. For example:

public delegate void BoilerLogHandler(string status);

Then, declare the event itself, using  the event  keyword:

// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;

The code above defines a delegate named  BoilerLogHandler  and an event named  BoilerEventLog  that calls the delegate when the event is generated.

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 );
    }
  }
}

When the above code is compiled and executed, it produces the following result:

event not fire
event fire
event fire

example:

using System;

/*功能:当起床铃声响起,就引发学生起床/厨师做早餐两个事件
*/

// 定义一个委托(也可以定义在Ring类里面)
public delegate void DoSomething();

// 产生事件的类 
public class Ring
{
    // 声明一个委托事件
    public event DoSomething doIt;

    // 构造函数
    public Ring()
    {
    }

    // 定义一个方法,即"响铃"   引发一个事件
    public void RaiseEvent()
    {
        Console.WriteLine("铃声响了.......");

        // 判断事件是否有调用委托(是不是要求叫学生起床,叫厨师做饭)
        if (null != doIt)
        {
            doIt(); //  如果有注册的对象,那就调用委托(叫学生起床,叫厨师做饭)
        }else{
        Console.WriteLine("无事发生......."); //没有注册,事件没有调用任何委托
    }
    }
}

// 学生类( 处理事件类一)
public class HandleEventOfStudents
{
    // 默认构造函数
    public HandleEventOfStudents()
    {
    }

    //叫学生起床
    public void GetUp()
    {
        Console.WriteLine("[学生]:听到起床铃声响了,起床了。");
    }
}

//  校园厨师类(处理事件类二)  
public class HandleEventOfChefs
{
    // 默认构造函数
    public HandleEventOfChefs()
    {
    }

    //叫厨师做早餐
    public void Cook()
    {
        Console.WriteLine("[厨师]:听到起床铃声响了,为学生做早餐。");
    }
}

// 主类
public class ListenerEvent
{
    public static void Main(String[] args)
    {
        Ring ring = new Ring(); // 实例化一个铃声类[它是主角,都是因为它才牵连一系列的动作]  
        ring.doIt += new HandleEventOfStudents().GetUp; // 注册,学生委托铃声类,铃声响起的时候叫我起床.
        ring.doIt += new HandleEventOfChefs().Cook;     // 注册,厨师告诉铃声类,我也委托你叫我什么时候做早餐
        ring.RaiseEvent(); // 铃声响起来了,它发现学生和厨师都拜托(注册)了自己,然后它就开始叫学生起床,叫厨师做早餐(一个事件调用了两个委托)
    }
}

example:

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

namespace shijian_weituo
{
    class Program
    {
        class Observer
        {
            public Observer(string _name)
            {
                name = _name;
            }
            public string name;

            public void RecieveMagazine(string message)
            {
                Console.WriteLine("{0}收到了{1}, 仔细阅读了一番。", this.name, message);
            }
        }

        //出版社
        class Publisher
        {
            public Publisher(string magName)
            {
                magazineName = magName;
            }

            public string magazineName;

            public delegate void MagazineDelegate(string message);
            public event MagazineDelegate Magazine;

            public void PublishingMagazine()
            {
                //如果没人订,就不用印了
                //此处必须判断Delegate对象是否为空,调用空的Delegate对象会引发异常
                if (Magazine != null)
                {
                    Magazine(magazineName);
                }
            }
        }

        static void Main(string[] args)
        {
            //出版社有一本叫《故事会》的杂志
            Publisher publisher = new Publisher("《故事会》");

            //读者小A订了这本杂志
            Observer observerA = new Observer("小A");
            publisher.Magazine += observerA.RecieveMagazine;

            //读者小B也订了这本杂志
            Observer observerB = new Observer("小B");
            publisher.Magazine += observerB.RecieveMagazine;

            //出版社印刷本月的《故事会》
            publisher.PublishingMagazine();

            Console.ReadLine();

        }
    }
}

C# Collection

Collection (Collection) class is a class dedicated to data storage and retrieval. These classes provide support for stacks, queues, lists, and hash tables. Most collection classes implement the same interface.

Collection (Collection) class serves different purposes like dynamically allocating memory for elements, accessing list items based on index, etc. These classes create collections of objects of the Object class. In C#, the Object class is the base class for all data types.

Various collection classes and their usage

The following are various commonly used  classes in the System.Collection  namespace.

kind Description and usage
Dynamic Array (ArrayList) It represents an ordered collection of objects that can be individually indexed .

It basically replaces an array. However, unlike an array, where you can use an index to add and remove items at specified positions, a dynamic array resizes itself automatically. It also allows dynamic memory allocation, adding, searching, and sorting items in the list.

Hash table (Hashtable) It uses keys to access elements in the collection.

Hashtables are used when you use a key to access an element, and you can identify a useful key value. Each entry in a hash table has a key/value pair. Keys are used to access items in the collection.

Sorted List (SortedList) It can use keys and indexes to access items in the list.

A sorted list is a combination of an array and a hash table. It contains a list whose items can be accessed using a key or an index. It's a dynamic array (ArrayList) if you access items using an index, or a Hashtable (Hashtable) if you access items using a key. Items in a collection are always sorted by key value.

stack It represents a last-in first-out collection of objects.

A stack is used when you need last-in, first-out access to items. When you add an item to the list, it's called pushing an element, and when you remove an item from the list, it's called popping an element.

Queue It represents a first-in first-out collection of objects.

Queues are used when you need first-in, first-out access to items. When you add an item to the list, it's called enqueue , and when you remove an item from the list, it's called dequeue .

Point array (BitArray) It represents a binary array represented by the values ​​1 and 0.

Point arrays are used when you need to store bits, but don't know the number of bits in advance. You can access items from the collection of point arrays using an integer index , starting from zero.

 C# dynamic array (ArrayList)

A dynamic array (ArrayList) represents an ordered collection of objects that can be individually indexed. It basically replaces an array. However, unlike an array, where you can use an index to add and remove items at specified positions, a dynamic array resizes itself automatically. It also allows dynamic memory allocation, adding, searching, and sorting items in the list.

Methods and properties of the ArrayList class

The following table lists   some commonly used  properties of the ArrayList class :

Attributes describe
Capacity Gets or sets the number of elements that the ArrayList can contain.
Count Get the number of elements actually contained in the ArrayList.
IsFixedSize Gets a value indicating whether the ArrayList has a fixed size.
IsReadOnly Gets a value indicating whether the ArrayList is read-only.
IsSynchronized Gets a value indicating whether to access the ArrayList synchronously (thread-safe).
Item[Int32] Gets or sets the element at the specified index.
SyncRoot Get an object for synchronous access to ArrayList.

The following table lists   some commonly used  methods of the ArrayList class :

serial number Method name & description
1 public virtual int Add( object value );
Adds an object at the end of the ArrayList.
2 public virtual void AddRange( ICollection c );
Add the elements of ICollection at the end of ArrayList.
3 public virtual void Clear();
Removes all elements from the ArrayList.
4 public virtual bool Contains( object item );
Determine whether an element is in the ArrayList.
5 public virtual ArrayList GetRange( int index, int count );
Returns an ArrayList representing a subset of the elements in the source ArrayList.
6 public virtual int IndexOf(object);
Returns the index of the first occurrence of a value in the ArrayList, the index starts from zero.
7 public virtual void Insert( int index, object value );
Insert an element at the specified index of ArrayList.
8 public virtual void InsertRange( int index, ICollection c );
At the specified index of ArrayList, insert the elements of a certain collection.
9 public virtual void Remove( object obj );
Removes the first occurrence of the specified object from the ArrayList.
10 public virtual void RemoveAt( int index );
Removes the element at the specified index of the ArrayList.
11 public virtual void RemoveRange( int index, int count );
Removes a range of elements from the ArrayList.
12 public virtual void Reverse();
逆转 ArrayList 中元素的顺序。
13 public virtual void SetRange( int index, ICollection c );
复制某个集合的元素到 ArrayList 中某个范围的元素上。
14 public virtual void Sort();
对 ArrayList 中的元素进行排序。
15 public virtual void TrimToSize();
设置容量为 ArrayList 中元素的实际个数。

下面的实例演示了动态数组(ArrayList)的概念:

using System;
using System.Collections;

namespace CollectionApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            ArrayList al = new ArrayList();

            Console.WriteLine("Adding some numbers:");
            al.Add(45);
            al.Add(78);
            al.Add(33);
            al.Add(56);
            al.Add(12);
            al.Add(23);
            al.Add(9);
           
            Console.WriteLine("Capacity: {0} ", al.Capacity);
            Console.WriteLine("Count: {0}", al.Count);
                     
            Console.Write("Content: ");
            foreach (int i in al)
            {
                Console.Write(i + " ");
            }
            Console.WriteLine();
            Console.Write("Sorted Content: ");
            al.Sort();
            foreach (int i in al)
            {
                Console.Write(i + " ");
            }
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

 当上面的代码被编译和执行时,它会产生下列结果:

Adding some numbers:
Capacity: 8 
Count: 7
Content: 45 78 33 56 12 23 9 
Sorted Content: 9 12 23 33 45 56 78 

C# 哈希表(Hashtable)

Hashtable 类代表了一系列基于键的哈希代码组织起来的键/值对。它使用来访问集合中的元素。当使用访问元素时,则使用哈希表,而且您可以识别一个有用的键值。哈希表中的每一项都有一个键/值对。键用于访问集合中的项目。

Hashtable 类的方法和属性

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

属性 描述
Count 获取 Hashtable 中包含的键值对个数。
IsFixedSize 获取一个值,表示 Hashtable 是否具有固定大小。
IsReadOnly 获取一个值,表示 Hashtable 是否只读。
Item 获取或设置与指定的键相关的值。
Keys 获取一个 ICollection,包含 Hashtable 中的键。
Values 获取一个 ICollection,包含 Hashtable 中的值。

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

序号 方法名 & 描述
1 public virtual void Add( object key, object value );
向 Hashtable 添加一个带有指定的键和值的元素。
2 public virtual void Clear();
从 Hashtable 中移除所有的元素。
3 public virtual bool ContainsKey( object key );
判断 Hashtable 是否包含指定的键。
4 public virtual bool ContainsValue( object value );
判断 Hashtable 是否包含指定的值。
5 public virtual void Remove( object key );
从 Hashtable 中移除带有指定的键的元素。

实例

下面的实例演示了哈希表(Hashtable)的概念:

using System;
using System.Collections;

namespace CollectionsApplication
{
   class Program
   {
      static void Main(string[] args)
      {
         Hashtable ht = new Hashtable();


         ht.Add("001", "Zara Ali");
         ht.Add("002", "Abida Rehman");
         ht.Add("003", "Joe Holzner");
         ht.Add("004", "Mausam Benazir Nur");
         ht.Add("005", "M. Amlan");
         ht.Add("006", "M. Arif");
         ht.Add("007", "Ritesh Saikia");

         if (ht.ContainsValue("Nuha Ali"))
         {
            Console.WriteLine("This student name is already in the list");
         }
         else
         {
            ht.Add("008", "Nuha Ali");
         }
         // 获取键的集合
         ICollection key = ht.Keys;

         foreach (string k in key)
         {
            Console.WriteLine(k + ": " + ht[k]);
         }
         Console.ReadKey();
      }
   }
}

当上面的代码被编译和执行时,它会产生下列结果:

007: Ritesh Saikia
004: Mausam Benazir Nur
005: M. Amlan
008: Nuha Ali
002: Abida Rehman
003: Joe Holzner
001: Zara Ali
006: M. Arif

C# 排序列表(SortedList)

SortedList 类代表了一系列按照键来排序的键/值对,这些键值对可以通过键和索引来访问。排序列表是数组和哈希表的组合。它包含一个可使用键或索引访问各项的列表。如果您使用索引访问各项,则它是一个动态数组(ArrayList),如果您使用键访问各项,则它是一个哈希表(Hashtable)。集合中的各项总是按键值排序。

SortedList 类的方法和属性

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

属性 描述
Capacity 获取或设置 SortedList 的容量。
Count 获取 SortedList 中的元素个数。
IsFixedSize 获取一个值,表示 SortedList 是否具有固定大小。
IsReadOnly 获取一个值,表示 SortedList 是否只读。
Item 获取或设置与 SortedList 中指定的键相关的值。
Keys 获取 SortedList 中的键。
Values 获取 SortedList 中的值。

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

序号 方法名 & 描述
1 public virtual void Add( object key, object value );
向 SortedList 添加一个带有指定的键和值的元素。
2 public virtual void Clear();
从 SortedList 中移除所有的元素。
3 public virtual bool ContainsKey( object key );
判断 SortedList 是否包含指定的键。
4 public virtual bool ContainsValue( object value );
判断 SortedList 是否包含指定的值。
5 public virtual object GetByIndex( int index );
获取 SortedList 的指定索引处的值。
6 public virtual object GetKey( int index );
获取 SortedList 的指定索引处的键。
7 public virtual IList GetKeyList();
获取 SortedList 中的键。
8 public virtual IList GetValueList();
获取 SortedList 中的值。
9 public virtual int IndexOfKey( object key );
返回 SortedList 中的指定键的索引,索引从零开始。
10 public virtual int IndexOfValue( object value );
返回 SortedList 中的指定值第一次出现的索引,索引从零开始。
11 public virtual void Remove( object key );
从 SortedList 中移除带有指定的键的元素。
12 public virtual void RemoveAt( int index );
移除 SortedList 的指定索引处的元素。
13 public virtual void TrimToSize();
设置容量为 SortedList 中元素的实际个数。

实例

下面的实例演示了排序列表(SortedList)的概念:

using System;
using System.Collections;

namespace CollectionsApplication
{
   class Program
   {
      static void Main(string[] args)
      {
         SortedList sl = new SortedList();

         sl.Add("001", "Zara Ali");
         sl.Add("002", "Abida Rehman");
         sl.Add("003", "Joe Holzner");
         sl.Add("004", "Mausam Benazir Nur");
         sl.Add("005", "M. Amlan");
         sl.Add("006", "M. Arif");
         sl.Add("007", "Ritesh Saikia");

         if (sl.ContainsValue("Nuha Ali"))
         {
            Console.WriteLine("This student name is already in the list");
         }
         else
         {
            sl.Add("008", "Nuha Ali");
         }

         // 获取键的集合
         ICollection key = sl.Keys;

         foreach (string k in key)
         {
            Console.WriteLine(k + ": " + sl[k]);
         }
      }
   }
}

当上面的代码被编译和执行时,它会产生下列结果:

001: Zara Ali
002: Abida Rehman
003: Joe Holzner
004: Mausam Banazir Nur
005: M. Amlan 
006: M. Arif
007: Ritesh Saikia
008: Nuha Ali

C# 堆栈(Stack)

堆栈(Stack)代表了一个后进先出的对象集合。当您需要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当您从列表中移除一项时,称为弹出元素。

Stack 类的方法和属性

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

属性 描述
Count 获取 Stack 中包含的元素个数。

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

序号 方法名 & 描述
1 public virtual void Clear();
从 Stack 中移除所有的元素。
2 public virtual bool Contains( object obj );
判断某个元素是否在 Stack 中。
3 public virtual object Peek();
返回在 Stack 的顶部的对象,但不移除它。
4 public virtual object Pop();
移除并返回在 Stack 的顶部的对象。
5 public virtual void Push( object obj );
向 Stack 的顶部添加一个对象。
6 public virtual object[] ToArray();
复制 Stack 到一个新的数组中。

实例

下面的实例演示了堆栈(Stack)的使用:

using System;
using System.Collections;

namespace CollectionsApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Stack st = new Stack();

            st.Push('A');
            st.Push('M');
            st.Push('G');
            st.Push('W');
           
            Console.WriteLine("Current stack: ");
            foreach (char c in st)
            {
                Console.Write(c + " ");
            }
            Console.WriteLine();
           
            st.Push('V');
            st.Push('H');
            Console.WriteLine("The next poppable value in stack: {0}",
            st.Peek());
            Console.WriteLine("Current stack: ");          
            foreach (char c in st)
            {
               Console.Write(c + " ");
            }
            Console.WriteLine();

            Console.WriteLine("Removing values ");
            st.Pop();
            st.Pop();
            st.Pop();
           
            Console.WriteLine("Current stack: ");
            foreach (char c in st)
            {
               Console.Write(c + " ");
            }
        }
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

Current stack: 
W G M A
The next poppable value in stack: H
Current stack: 
H V W G M A
Removing values
Current stack: 
G M A

C# 队列(Queue)

队列(Queue)代表了一个先进先出的对象集合。当您需要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队

Queue 类的方法和属性

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

属性 描述
Count 获取 Queue 中包含的元素个数。

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

序号 方法名 & 描述
1 public virtual void Clear();
从 Queue 中移除所有的元素。
2 public virtual bool Contains( object obj );
判断某个元素是否在 Queue 中。
3 public virtual object Dequeue();
移除并返回在 Queue 的开头的对象。
4 public virtual void Enqueue( object obj );
向 Queue 的末尾添加一个对象。
5 public virtual object[] ToArray();
复制 Queue 到一个新的数组中。
6 public virtual void TrimToSize();
设置容量为 Queue 中元素的实际个数。

实例

下面的实例演示了队列(Queue)的使用:

using System;
using System.Collections;

namespace CollectionsApplication
{
   class Program
   {
      static void Main(string[] args)
      {
         Queue q = new Queue();

         q.Enqueue('A');
         q.Enqueue('M');
         q.Enqueue('G');
         q.Enqueue('W');
         
         Console.WriteLine("Current queue: ");
         foreach (char c in q)
            Console.Write(c + " ");
         Console.WriteLine();
         q.Enqueue('V');
         q.Enqueue('H');
         Console.WriteLine("Current queue: ");        
         foreach (char c in q)
            Console.Write(c + " ");
         Console.WriteLine();
         Console.WriteLine("Removing some values ");
         char ch = (char)q.Dequeue();
         Console.WriteLine("The removed value: {0}", ch);
         ch = (char)q.Dequeue();
         Console.WriteLine("The removed value: {0}", ch);
         Console.ReadKey();
      }
   }
}

当上面的代码被编译和执行时,它会产生下列结果:

Current queue: 
A M G W 
Current queue: 
A M G W V H 
Removing values
The removed value: A
The removed value: M

C# 点阵列(BitArray)

BitArray 类管理一个紧凑型的位值数组,它使用布尔值来表示,其中 true 表示位是开启的(1),false 表示位是关闭的(0)。

当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始。

BitArray 类的方法和属性

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

属性 描述
Count 获取 BitArray 中包含的元素个数。
IsReadOnly 获取一个值,表示 BitArray 是否只读。
Item 获取或设置 BitArray 中指定位置的位的值。
Length 获取或设置 BitArray 中的元素个数。

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

序号 方法名 & 描述
1 public BitArray And( BitArray value );
对当前的 BitArray 中的元素和指定的 BitArray 中的相对应的元素执行按位与操作。
2 public bool Get( int index );
获取 BitArray 中指定位置的位的值。
3 public BitArray Not();
把当前的 BitArray 中的位值反转,以便设置为 true 的元素变为 false,设置为 false 的元素变为 true。
4 public BitArray Or( BitArray value );
对当前的 BitArray 中的元素和指定的 BitArray 中的相对应的元素执行按位或操作。
5 public void Set( int index, bool value );
把 BitArray 中指定位置的位设置为指定的值。
6 public void SetAll( bool value );
把 BitArray 中的所有位设置为指定的值。
7 public BitArray Xor( BitArray value );
对当前的 BitArray 中的元素和指定的 BitArray 中的相对应的元素执行按位异或操作。

实例

下面的实例演示了点阵列(BitArray)的使用:

using System;
using System.Collections;

namespace CollectionsApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建两个大小为 8 的点阵列
            BitArray ba1 = new BitArray(8);
            BitArray ba2 = new BitArray(8);
            byte[] a = { 60 };
            byte[] b = { 13 };
           
            // 把值 60 和 13 存储到点阵列中
            ba1 = new BitArray(a);
            ba2 = new BitArray(b);

            // ba1 的内容
            Console.WriteLine("Bit array ba1: 60");
            for (int i = 0; i < ba1.Count; i++)
            {
                Console.Write("{0, -6} ", ba1[i]);
            }
            Console.WriteLine();
           
            // ba2 的内容
            Console.WriteLine("Bit array ba2: 13");
            for (int i = 0; i < ba2.Count; i++)
            {
                Console.Write("{0, -6} ", ba2[i]);
            }
            Console.WriteLine();
           
           
            BitArray ba3 = new BitArray(8);
            ba3 = ba1.And(ba2);

            // ba3 的内容
            Console.WriteLine("Bit array ba3 after AND operation: 12");
            for (int i = 0; i < ba3.Count; i++)
            {
                Console.Write("{0, -6} ", ba3[i]);
            }
            Console.WriteLine();

            ba3 = ba1.Or(ba2);
            // ba3 的内容
            Console.WriteLine("Bit array ba3 after OR operation: 61");
            for (int i = 0; i < ba3.Count; i++)
            {
                Console.Write("{0, -6} ", ba3[i]);
            }
            Console.WriteLine();
           
            Console.ReadKey();
        }
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

Bit array ba1: 60 
False False True True True True False False 
Bit array ba2: 13
True False True True False False False False 
Bit array ba3 after AND operation: 12
False False True True False False False False 
Bit array ba3 after OR operation: 61
True False True True False False False False 

C# 泛型(Generic)

泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。

You can write the specification of a class or method with substitution parameters of data types. When the compiler encounters a constructor of a class or a function call to a method, it generates code to handle the specified data type. The following simple example will help you understand the concept:

using System;
using System.Collections.Generic;

namespace GenericApplication
{
    public class MyGenericArray<T>
    {
        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;
        }
    }
           
    class Tester
    {
        static void Main(string[] args)
        {
            // 声明一个整型数组
            MyGenericArray<int> intArray = new MyGenericArray<int>(5);
            // 设置值
            for (int c = 0; c < 5; c++)
            {
                intArray.setItem(c, c*5);
            }
            // 获取值
            for (int c = 0; c < 5; c++)
            {
                Console.Write(intArray.getItem(c) + " ");
            }
            Console.WriteLine();
            // 声明一个字符数组
            MyGenericArray<char> charArray = new MyGenericArray<char>(5);
            // 设置值
            for (int c = 0; c < 5; c++)
            {
                charArray.setItem(c, (char)(c+97));
            }
            // 获取值
            for (int c = 0; c < 5; c++)
            {
                Console.Write(charArray.getItem(c) + " ");
            }
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

When the above code is compiled and executed, it produces the following result:

0 5 10 15 20
a b c d e

Generic Features

The use of generics is a technique to enhance program functionality, specifically in the following aspects:

  • It helps you maximize code reuse, protect type safety, and improve performance.
  • You can create generic collection classes. The .NET Framework class library   includes some new generic collection classes in the System.Collections.Generic namespace. You can use these generic collection classes in place of   the collection classes in System.Collections .
  • You can create your own generic interfaces, generic classes, generic methods, generic events, and generic delegates.
  • You can constrain a generic class to access methods of a specific data type.
  • Information about the types used in generic data types can be obtained at runtime by using reflection.

Generic methods

In the above example, we have used generic classes and we can declare generic methods with type parameters. The following program illustrates this concept:

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();
        }
    }
}

When the above code is compiled and executed, it produces the following result:

Int values before calling swap:
a = 10, b = 20
Char values before calling swap:
c = I, d = V
Int values after calling swap:
a = 20, b = 10
Char values after calling swap:
c = V, d = I

Generic delegation

You can define generic delegates with type parameters. For example:

delegate T NumberChanger<T>(T n);

The following example demonstrates the use of delegates:

using System;
using System.Collections.Generic;

delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{
    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<int> nc1 = new NumberChanger<int>(AddNum);
            NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);
            // 使用委托对象调用方法
            nc1(25);
            Console.WriteLine("Value of Num: {0}", getNum());
            nc2(5);
            Console.WriteLine("Value of Num: {0}", getNum());
            Console.ReadKey();
        }
    }
}

When the above code is compiled and executed, it produces the following result:

Value of Num: 35
Value of Num: 175

Guess you like

Origin blog.csdn.net/weixin_48195035/article/details/128103353