C#慢慢学 (一)(e文转)

80酷酷网    80kuku.com

  C# is a simple, modern, object oriented, and type-safe programming language derived from C and C++. C# (pronounced “C sharp”) is firmly planted in the C and C++ family tree of languages, and will immediately be familiar to C and C++ programmers. C# aims to combine the high productivity of Visual Basic and the raw power of C++.
C# is provided as a part of Microsoft Visual Studio 7.0. In addition to C#, Visual Studio supports Visual Basic, Visual C++, and the scripting languages VBScript and JScript. All of these languages provide access to the Next Generation Windows Services (NWGS) platform, which includes a common execution engine and a rich class library. The .NET software development kit defines a "Common Language Subset" (CLS), a sort of lingua franca that ensures seamless interoperability between CLS-compliant languages and class libraries. For C# developers, this means that even though C# is a new language, it has complete access to the same rich class libraries that are used by seasoned tools such as Visual Basic and Visual C++. C# itself does not include a class library.
The rest of this chapter describes the essential features of the language. While later chapters describe rules and exceptions in a detail-oriented and sometimes mathematical manner, this chapter strives for clarity and brevity at the expense of completeness. The intent is to provide the reader with an introduction to the language that will facilitate the writing of early programs and the reading of later chapters.
1.1 Hello, world
The canonical “Hello, world” program can be written in C# as follows:
using System;
class Hello
{
    static void Main() {
        Console.WriteLine("Hello, world");
    }
}
The default file extension for C# programs is .cs, as in hello.cs. Such a program can be compiled with the command line directive
csc hello.cs
which produces an executable program named hello.exe. The output of the program is:
Hello, world
Close examination of this program is illuminating:
 The using System; directive references a namespace called System that is provided by the .NET runtime. This namespace contains the Console class referred to in the Main method. Namespaces provide a hierarchical means of organizing the elements of a class library. A “using” directive enables unqualified use of the members of a namespace. The “Hello, world” program uses Console.WriteLine as a shorthand for System.Console.WriteLine. What do these identifiers denote?  System is a namespace, Console is a class defined in that namespace, and WriteLine is a static method defined on that class.
 The Main function is a static member of the class Hello. Functions and variables are not supported at the global level; such elements are always contained within type declarations (e.g., class and struct declarations).
 The “Hello, world” output is produced through the use of a class library. C# does not itself provide a class library. Instead, C# uses a common class library that is also used by other languages such as Visual Basic and Visual C++.
For C and C++ developers, it is interesting to note a few things that do not appear in the “Hello, world” program.
 The program does not use either “::” or “->” operators. The “::” is not an operator in C# at all, and the “->” operator is used in only a small fraction of C# programs. C# programs use “.” as a separator in compound names such as Console.WriteLine.
 The program does not contain forward declarations. Forward declarations are never needed in C# programs, as declaration order is not significant.
 The program does not use #include to import program text. Dependencies between programs are handled symbolically rather than with program text. This system eliminates barriers between programs written in different languages. For example, the Console class could be written in C# or in some other language.

1.2 Automatic memory management
Manual memory management requires developers to manage the allocation and de-allocation of blocks of memory. Manual memory management is both time consuming and difficult. C# provides automatic memory management so that developers are freed from this burdensome task. In the vast majority of cases, this automatic memory management increases code quality and enhances developer productivity without negatively impacting either expressiveness or performance.
The example
using System;
public class Stack
{
    private Node first = null;
    public bool Empty {
        get {
            return (first == null);
        }
    }
    public object Pop() {
        if (first == null)
            throw new Exception("Can't Pop from an empty Stack.");
        else {
            object temp = first.Value;
            first = first.Next;
            return temp;
        }
    }
    public void Push(object o) {
        first = new Node(o, first);
    }
    class Node
    {
        public Node Next;
        public object Value;
        public Node(object value): this(value, null) {}
        public Node(object value, Node next) {
            Next = next;
            Value = value;
        }
    }
}
shows a Stack class implemented as a linked list of Node instances. Node instances are created in the Push method and are garbage collected when no longer needed. A Node instance becomes eligible for garbage collection when it is no longer possible for any code to access it. For instance, when an item is removed from the Stack, the associated Node instance becomes eligible for garbage collection.
The example
class Test
{
    static void Main() {
        Stack s = new Stack();
        for (int i = 0; i < 10; i++)
            s.Push(i);
        while (!s.Empty)
            Console.WriteLine(s.Pop());
    }
}
shows a test program that uses the Stack class. A Stack is created and initialized with 10 elements, and then assigned the value null. Once the variable s is assigned null, the Stack and the associated 10 Node instances become eligible for garbage collection. The garbage collector is permitted to clean up immediately, but is not required to do so.
For developers who are generally content with automatic memory management but sometimes need fine-grained control or that extra iota of performance, C# provides the ability to write “unsafe” code. Such code can deal directly with pointer types, and fix objects to temporarily prevent the garbage collector from moving them. This “unsafe” code feature is in fact “safe” feature from the perspective of both developers and users. Unsafe code must be clearly marked in the code with the modifier unsafe, so developers can't possibly use unsafe features accidentally, and the C# compiler and the execution engine work together to ensure that unsafe code cannot masquerade as safe code.
The example
using System;
class Test
{
    unsafe static void WriteLocations(byte[] arr) {
        fixed (byte *p_arr = arr) {
            byte *p_elem = p_arr;
            for (int i = 0; i < arr.Length; i++) {
                byte value = *p_elem;
                string addr = int.Format((int) p_elem, "X");
                Console.WriteLine("arr[{0}] at 0x{1} is {2}", i,  addr, value);
                p_elem++;
            }
        }
    }
    static void Main() {
        byte[] arr = new byte[] {1, 2, 3, 4, 5};
        WriteLocations(arr);
    }
}
shows an unsafe method named WriteLocations that fixes an array instance and uses pointer manipulation to iterate over the elements and write out the index, value, and location of each. One possible output of the program is:
arr[0] at 0x8E0360 is 1
arr[1] at 0x8E0361 is 2
arr[2] at 0x8E0362 is 3
arr[3] at 0x8E0363 is 4
arr[4] at 0x8E0364 is 5
but of course the exact memory locations are subject to change.
1.3 Types
C# supports two major kinds of types: value types and reference types. Value types include simple types (e.g., char, int, and float), enum types, and struct types. Reference types include class types, interface types, delegate types, and array types.
Value types differ from reference types in that variables of the value types directly contain their data, whereas variables of the reference types store references to objects. With reference types, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With value types, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other.
The example
using System;
class Class1
{
    public int Value = 0;
}
class Test
{
    static void Main() {
        int val1 = 0;
        int val2 = val1;
        val2 = 123;
        Class1 ref1 = new Class1();
        Class1 ref2 = ref1;
        ref2.Value = 123;
        Console.WriteLine("Values: {0}, {1}", val1, val2);
        Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value);
    }
}
shows this difference. The output of the program is
Values: 0, 123
Refs: 123, 123
The assignment to the local variable val1 does not impact the local variable val2 because both local variables are of a value type (int) and each local variable of a value type has its own storage. In contrast, the assignment ref2.Value = 123; affects the object that both ref1 and ref2 reference.
Developers can define new value types through enum and struct declarations, and can define new reference types via class, interface, and delegate declarations. The example
using System;
public enum Color
{
    Red, Blue, Green
}
public struct Point
{
    public int x, y;
}
public interface IBase
{
    void F();
}
public interface IDerived: IBase
{
    void G();
}
public class A
{
    protected void H() {
        Console.WriteLine("A.H");
    }
}
public class B: A, IDerived
{
    public void F() {
        Console.WriteLine("B.F, implementation of IDerived.F");
    }
    public void G() {
        Console.WriteLine("B.G, implementation of IDerived.G");
    }
}
public delegate void EmptyDelegate();
shows an example or two for each kind of type declaration. Later sections describe type declarations in greater detail.

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: