Getting Started with C# 06: Hodgepodge - C# Programming Guide on MSDN

Constructor

Static constructors are used to initialize any static data , or to perform specific operations that only need to be performed once . The static constructor is automatically called before the first instance is created or any static members are referenced.

class SimpleClass
{
    // Static constructor
    static SimpleClass()
    {
        //...
    }
}

Static constructors have the following characteristics:
Static constructors have neither access modifiers nor parameters.
The static constructor is automatically called to initialize the class before the first instance is created or any static members are referenced.
Static constructors cannot be called directly.
In a program, the user has no control over when a static constructor is executed.
A typical use of a static constructor is when a class uses a log file, it is used to write entries to the log file.
The static constructor is also useful when creating wrapper classes for unmanaged code, where the constructor can call the LoadLibrary method.

Unlike some languages, C# does not provide a copy constructor. If you create a new object and want to copy values ​​from an existing object, you must write the appropriate methods yourself.

    class Person
    {
        private string name;
        private int age;

        // Copy constructor.
        public Person(Person previousPerson)
        {
            name = previousPerson.name;
            age = previousPerson.age;
        }
    }

destructor

Destructors are used to destruct an instance of a class.
Destructors cannot be defined in structs. You can only use destructors on classes.
A class can have only one destructor.
Destructors cannot be inherited or overloaded.
Destructor cannot be called. They are called automatically.
Destructors have neither modifiers nor parameters.
For example, here is the declaration of the destructor for class Car:

class Car
{
    ~ Car()  // destructor
    {
        // cleanup statements...
    }
}

This destructor implicitly calls Finalize on the object's base class. This way, the preceding destructor code is implicitly converted to:

protected override void Finalize()
{
    try
    {
        // cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

This means that the Finalize method is called recursively on all instances in the inheritance chain (from the most derived to the least derived).
An empty destructor should not be used. If the class contains a destructor, an item is created in the Finalize queue. When the destructor is called, the garbage collector is called to dispose of the queue. If the destructor is empty, it just causes unnecessary performance loss.
The programmer has no control over when the destructor is called, since that is determined by the garbage collector. The garbage collector checks for objects that are no longer used by the application. If the garbage collector deems an object eligible for destruction, it calls the destructor (if any) and reclaims the memory used to store this object. The destructor is also called when the program exits.
Explicit Release of Resources
If your application is using expensive external resources, it is also recommended that you provide a way to explicitly release the resource before the garbage collector releases the object. This is done by implementing the Dispose method from the IDisposable interface, which performs the necessary cleanup for the object. This can greatly improve the performance of the application. Even with this explicit control over the resource, the destructor is a safeguard that can be used to clean up the resource if a call to the Dispose method fails.
1) Implement the Dispose method;
2) Use the using statement.

static classes and members

Static classes and class members are used to create data and functions that can be accessed without creating an instance of the class. Static class members can be used to separate data and behavior independent of any object identity: no matter what happens to the object, those data and functions do not change with it. Static classes are used when there is no data or behavior in the class that depends on object identity.
A class can be declared static to indicate that it contains only static members. Instances of static classes cannot be created using the new keyword. Static classes are automatically loaded by the .NET Framework common language runtime (CLR) when the program or namespace that contains the class is loaded.
Use static classes to contain methods that are not associated with a specific object. For example, it is a common requirement to create a set of methods that do not manipulate instance data and are not associated with a specific object in code. You should use a static class to contain those methods.
The main function of static classes is as follows:
they contain only static members.
They cannot be instantiated.
They are sealed.
They cannot contain instance constructors (C# Programming Guide).
So creating a static class is about the same as creating a class with only static members and a private constructor. Private constructors prevent the class from being instantiated.
The advantage of using static classes is that the compiler can perform checks to ensure that instance members are not accidentally added. The compiler will guarantee that no such utility will be created.
Static classes are sealed and therefore cannot be inherited. Static classes cannot contain constructors, but static constructors can still be declared to assign initial values ​​or set some static state. For more information, see Static Constructors (C# Programming Guide).

Static members
Even without creating an instance of the class, you can call a static method, field, property, or event in that class. If any instance of the class is created, the instance cannot be used to access static members. There is only one copy of static fields and events, and static methods and properties can only access static fields and static events. Static members are often used to represent data or computations that do not vary with the state of an object; for example, a math library might contain static methods for computing sine and cosine.

    //静态类中必须全部是静态成员函数
    static class CompanyInfo
    {
        public static readonly string name;
        public static readonly string addr;

        //使用静态构造函数初始化静态类成员
        static CompanyInfo()
        {
            name = "BGI";
            addr = "Shenzhen";
            Console.WriteLine("InitCompanyInfo");
        }
    }

indexer

Indexers allow instances of a class or structure to be indexed in the same way as arrays. Indexers are similar to properties, except that their accessors take parameters.
In the following example, a generic class is defined and provided with simple get and set accessor methods (as methods for assigning and retrieving values). The Program class creates an instance of this class for storing strings.

class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get
        {
            return arr[i];
        }
        set
        {
            arr[i] = value;
        }
    }
}

// This class shows how client code uses the indexer
class Program
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        stringCollection[0] = "Hello, World";
        System.Console.WriteLine(stringCollection[0]);
    }
}

Indexers allow objects to be indexed in a similar way to arrays.

  • The get accessor returns the value. The set accessor assigns the value.
  • The this keyword is used to define an indexer.
  • The value keyword is used to define the value assigned by the set indexer.
  • The indexer doesn't have to index based on integer values, it's up to you to define your specific lookup mechanism.
  • Indexers can be overloaded.
  • Indexers can have multiple parameters, such as when accessing a two-dimensional array.

There are two main ways to improve the safety and reliability of an indexer:
1) Always ensure that your code performs range and type checking when setting and retrieving values ​​from any buffer or array accessed by the indexer.
2) As many restrictions as possible should be placed on the accessibility of get and set accessors. This is especially important for set accessors.
Differences between properties and indexers: Enter image description

entrust

Delegates are similar to C++ function pointers, but are type-safe.
Delegates allow methods to be passed as parameters.
Delegates can be used to define callback methods.
Delegates can be chained together; for example, multiple methods can be called on an event.
The method does not need to exactly match the delegate signature. For more information, see Covariance and Contravariance.
C# version 2.0 introduced the concept of anonymous methods, which allow blocks of code to be passed as parameters in place of individually defined methods.
Unlike function pointers in C, delegates are object-oriented, type-safe, and insured. Common delegate declarations and calls:

// Create a method for a delegate.
public static void DelegateMethod(string message)
{
    System.Console.WriteLine(message);
}

// Instantiate the delegate.
Del handler = DelegateMethod;

// Call the delegate.
handler("Hello World");

Delegate call method:

1) Naming method

    public delegate void TestDelegate();

    class Program
    {
        //打印当前日期和时间
        static void PrintPoint()
        {
            for (int i = 0; i < 5; i++)
            {
                DateTime dtCurr = DateTime.Now;
                Console.WriteLine("{0:yyyy-MM-dd HH:mm:ss.fff}", dtCurr);
                Thread.Sleep(1000);
            }
        }
    }

    //使用类Program的静态函数PrintPoint()实例化委托
    TestDelegate method = Program.PrintPoint;
    method();

2) Anonymous delegates
In versions of C# prior to 2.0, the only way to declare a delegate was to use a named method. C# 2.0 introduced anonymous methods. Creating an anonymous method is the only way to pass code blocks as delegate parameters. E.g:

// Create a handler for a click event
button1.Click += delegate(System.Object o, System.EventArgs e)
                   { System.Windows.Forms.MessageBox.Show("Click!"); };

或:

// Create a delegate instance
delegate void Del(int x);

// Instantiate the delegate using an anonymous method
Del d = delegate(int k) { /* ... */ };

If you use anonymous methods, you don't have to create a separate method, thus reducing the coding overhead required to instantiate the delegate. For example, specifying a block of code in place of the delegate is useful if the overhead required to create the method is unnecessary. Starting a new thread is a good example. Without creating more methods for the delegate, the thread class can create a thread and contain the code that the thread executes.

void StartThread()
{
    System.Threading.Thread t1 = new System.Threading.Thread
      (delegate()
            {
                System.Console.Write("Hello, ");
                System.Console.WriteLine("World!");
            });
    t1.Start();
}

When to use delegates instead of interfaces: Use delegates
in the following situations:
1) When using the event design pattern.
2) When encapsulating static methods is desirable.
3) When the caller does not need to access other properties, methods or interfaces in the object implementing the method.
4) A convenient combination is required.
5) When the class may need multiple implementations of the method.

Interfaces are used in the following situations:
1) When there is a set of related methods that may be called.
2) When the class only needs a single implementation of the method.
3) When a class using an interface wants to cast that interface to another interface or class type.
4) When the method being implemented is linked to the type or identity of the class: eg compare methods.

Covariance of
delegation: Covariance of delegation refers to a delegate call mode in which the return type is a base class type, and the return type of the function implemented by the caller is a derived class of the delegate return type:

class Mammals
{
}

class Dogs : Mammals
{
}

class Program
{
    // Define the delegate.
    public delegate Mammals HandlerMethod();

    public static Mammals FirstHandler()
    {
        return null;
    }

    public static Dogs SecondHandler()
    {
        return null;
    }

    static void Main()
    {
        HandlerMethod handler1 = FirstHandler;

        // Covariance allows this delegate.
        HandlerMethod handler2 = SecondHandler;
    }
}

Delegate contravariance:
Delegated contravariance is the opposite of covariance. The parameter of the delegate is a base class type, and the parameter type passed by the caller is a derived class of the delegate parameter type:

System.DateTime lastActivity;
public Form1()
{
    InitializeComponent();

    lastActivity = new System.DateTime();
    this.textBox1.KeyDown += this.MultiHandler; //works with KeyEventArgs
    this.button1.MouseClick += this.MultiHandler; //works with MouseEventArgs

}

// Event hander for any event with an EventArgs or
// derived class in the second parameter
private void MultiHandler(object sender, System.EventArgs e)
{
    lastActivity = System.DateTime.Now;
}

event

An event has the following characteristics: the
publisher determines when the event is raised, and the subscriber determines what action to take in response to the event.
An event can have multiple subscribers. A single subscriber can handle multiple events from multiple publishers.
Events without subscribers are never called.
Events are typically used to notify user actions (eg, button clicks or menu selections in GUIs).
If an event has multiple subscribers, multiple event handlers are called synchronously when the event is raised. To invoke events asynchronously, see Invoking synchronous methods asynchronously.
You can use events to synchronize threads.
In the .NET Framework class library, events are based on the EventHandler delegate and the EventArgs base class.

Publishing events that conform to the .NET Framework guidelines:
All events in the .NET Framework class library are based on the EventHandler delegate, defined as follows:

public delegate void EventHandler(object sender, EventArgs e);

Events can only be used in the class that declares the event, even in derived classes, which can only subscribe to events. If you need to trigger the event directly in the derived class, you need to encapsulate the trigger of the event in the base class and turn it into a protected method, and then the derived class can trigger the event:

    //声明委托
    public delegate void PrintHandle(string msg);

    class Animal
    {
        //声明事件
        public event PrintHandle PrintEvent;

        //基类方法
        public virtual void Eat()
        {
            PrintHandle p = PrintEvent;
            if (p != null)
            {
                p(Name);
            }
        }

        //基类属性
        public virtual string Name { get; set; }
    }

    class Dog : Animal
    {
        public Dog()
        {
            Name = "Dog";
        }
    }

    class Program
    {
        static void Main()
        {
            Dog d = new Dog();
            d.PrintEvent += delegate (string msg)
            {
                Console.WriteLine("Your name is {0}", msg);
            };
            d.Eat();

            Console.Read();
        }
    }

iterator

The most common way to create an iterator is to implement the GetEnumerator method on the IEnumerable interface, for example:

public System.Collections.IEnumerator GetEnumerator()
{
    for (int i = 0; i < max; i++)
    {
        yield return i;
    }
}

static void Main()
{
    ListClass listClass1 = new ListClass();

    foreach (int i in listClass1.GetEnumerator())
    {
        System.Console.WriteLine(i);
    }
}

Iterator with parameters:

// Implementing the enumerable pattern
public System.Collections.IEnumerable SampleIterator(int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        yield return i;
    }
}

ListClass test = new ListClass();
foreach (int n in test.SampleIterator(1, 10))
{
    System.Console.WriteLine(n);
}

use unsafe code

The following example demonstrates the Windows ReadFile function by reading and displaying a text file. The ReadFile function requires unsafe code because it expects a pointer as a parameter.
The byte array passed to the Read function is a managed type. This means that the common language runtime (CLR) garbage collector may arbitrarily relocate the memory used by the array. To prevent this, use fixed to get a pointer to memory and mark it so that the garbage collector doesn't move it. At the end of the fixed block, the memory is automatically returned so that it can be moved through garbage collection.
This feature is called "Declarative Locking". The benefit of locking is that there is very little overhead unless garbage collection occurs in fixed blocks (which is unlikely).

class FileReader
{
    const uint GENERIC_READ = 0x80000000;
    const uint OPEN_EXISTING = 3;
    System.IntPtr handle;

    [System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
    static extern unsafe System.IntPtr CreateFile
    (
        string FileName,          // file name
        uint DesiredAccess,       // access mode
        uint ShareMode,           // share mode
        uint SecurityAttributes,  // Security Attributes
        uint CreationDisposition, // how to create
        uint FlagsAndAttributes,  // file attributes
        int hTemplateFile         // handle to template file
    );

    [System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
    static extern unsafe bool ReadFile
    (
        System.IntPtr hFile,      // handle to file
        void* pBuffer,            // data buffer
        int NumberOfBytesToRead,  // number of bytes to read
        int* pNumberOfBytesRead,  // number of bytes read
        int Overlapped            // overlapped buffer
    );

    [System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
    static extern unsafe bool CloseHandle
    (
        System.IntPtr hObject // handle to object
    );

    public bool Open(string FileName)
    {
        // open the existing file for reading       
        handle = CreateFile
        (
            FileName,
            GENERIC_READ,
            0,
            0,
            OPEN_EXISTING,
            0,
            0
        );

        if (handle != System.IntPtr.Zero)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public unsafe int Read(byte[] buffer, int index, int count)
    {
        int n = 0;
        fixed (byte* p = buffer)
        {
            if (!ReadFile(handle, p + index, count, &n, 0))
            {
                return 0;
            }
        }
        return n;
    }

    public bool Close()
    {
        return CloseHandle(handle);
    }
}

class Test
{
    static int Main(string[] args)
    {
        if (args.Length != 1)
        {
            System.Console.WriteLine("Usage : ReadFile <FileName>");
            return 1;
        }

        if (!System.IO.File.Exists(args[0]))
        {
            System.Console.WriteLine("File " + args[0] + " not found.");
            return 1;
        }

        byte[] buffer = new byte[128];
        FileReader fr = new FileReader();

        if (fr.Open(args[0]))
        {
            // Assume that an ASCII file is being read.
            System.Text.ASCIIEncoding Encoding = new System.Text.ASCIIEncoding();

            int bytesRead;
            do
            {
                bytesRead = fr.Read(buffer, 0, buffer.Length);
                string content = Encoding.GetString(buffer, 0, bytesRead);
                System.Console.Write("{0}", content);
            }
            while (bytesRead > 0);

            fr.Close();
            return 0;
        }
        else
        {
            System.Console.WriteLine("Failed to open requested file");
            return 1;
        }
    }
}

performance impact

Here are two operations that have a great impact on performance:
1) Boxing and unboxing Both boxing and unboxing
are processes that require a lot of computation. When boxing a value type, a brand new object must be created. This operation can take up to 20 times longer than an assignment operation. When unboxing, the coercion process can take up to four times as long as the assignment operation.
2) Do not create empty destructors
Empty destructors should not be used. If the class contains a destructor, an item is created in the Finalize queue. When the destructor is called, the garbage collector is called to dispose of the queue. If the destructor is empty, it will only cause performance degradation.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324990588&siteId=291194637