C# 核心编程结构Ⅱ 笔记

Note To avoid any ambiguity, C# demands a method only support a single params argument, which must be the final argument in the parameter list.

说明  为了避免歧义,C#要求方法只支持一个params参数,而且必须是参数列表中的最后一个参数.

 

Note Do be aware that if you declare an array, but do not explicitly fill each index, each item will be set to the default value of the data type (e.g., an array of bools will be set to false, an array of ints will be set to 0, and so forth).

说明  如果我们声明数组,而不显式填充每个索引,那么,每一个项都会被设置为数据类型的默认值(如,bool的数组就被设置为false, int的数组就被设置为0, 以此类推).

 

The System.Enum Type

The interesting thing about .NET enumerations is that they gain functionality from the System.Enum class type. This class defines a number of methods that allow you to interrogate and transform a given enumeration. One helpful method is the static Enum.GetUnderlyingType(), which as the name implies returns the data type used to store the values of the enumerated type (System.Byte in the case of the current EmpType declaration).

static void Main(string[] args)
{
    Console.WriteLine("**** Fun with Enums *****");
    // Make a contractor type.
    EmpType emp = EmpType.Contractor;
    AskForBonus(emp);
    // Print storage for the enum.
    Console.WriteLine("EmpType uses a {0} for storage",
    Enum.GetUnderlyingType(emp.GetType()));
    Console.ReadLine();
}

If you were to consult the Visual Studio 2008 object browser, you would be able to verify that the Enum.GetUnderlyingType()method requires you to pass in a System.Type as the first parameter. Type represents the metadata description of a given .NET entity. One possible way to obtain metadata (as shown previously) is to use the GetType()method, which is common to all types in the .NET base class libraries. Another approach is to make use of the C# typeof operator. One benefit of doing so is that you do not need to have a variable of the entity you wish to obtain a metadata description of:

// This time use typeof to extract a Type.
Console.WriteLine("EmpType uses a {0} for storage",
    Enum.GetUnderlyingType(typeof(EmpType)));

 

System.Enum类型

.NET枚举从System.Enum类类型获得了很多功能。这个类定义了许多用来查询和转换某个枚举的方法。一个很有用的方法就是静态的Enum.GetUnderlyingType()方法,顾名思义,它返回用于保存枚举类型值的数据类型(对于当前的EmpType声明就是System.Byte)。

static void Main(string[] args)
{
    Console.WriteLine("**** Fun with Enums *****");
    // 创建职员的类型
    EmpType emp = EmpType.Contractor;
    AskForBonus(emp);
    // 输出枚举的存储
    Console.WriteLine("EmpType uses a {0} for storage",
    Enum.GetUnderlyingType(emp.GetType()));
    Console.ReadLine();
}

如果我们查阅Visual Studio 2008对象浏览器,就会发现Enum.GetUnderlyingType()方法需要我们传入System.Type作为第一个参数。Type表示某个.NET实体的元数据描述。

获取元数据的一个可行的方式(前面提到过)是使用GetType()方法,这个方法是所有.NET基类库中类型所共有的。另外一种方式是使用C#的typeof运算符。这样做的好处是,不需要我们有希望获取元数据描述的实体的变量。

// 使用typeof获取一个Type。
Console.WriteLine("EmpType uses a {0} for storage",
    Enum.GetUnderlyingType(typeof(EmpType)));

 

System.Enum also defines another static method named GetValues(). This method returns an instance of System.Array. Each item in the array corresponds to a member of the specified enumeration. Consider the following method, which will print out each name/value pair within any enumeration you pass in as a parameter:

// This method will print out the details of any enum.
static void EvaluateEnum(System.Enum e)
{
    Console.WriteLine("=> Information about {0}", e.GetType().Name);
    Console.WriteLine("Underlying storage type: {0}",
    Enum.GetUnderlyingType(e.GetType()));
    // Get all name/value pairs for incoming parameter.
    Array enumData = Enum.GetValues(e.GetType());
    Console.WriteLine("This enum has {0} members.", enumData.Length);
    // Now show the string name and associated value.
    for (int i = 0; i < enumData.Length; i++)
    {
        Console.WriteLine("Name: {0}, Value: {0:D}",
        enumData.GetValue(i));
    }
    Console.WriteLine();
}

To test this new method, update your Main()method to create variables of several enumeration types declared in the System namespace (as well as an EmpType enumeration for good measure). For example:

static void Main(string[] args)
{
    Console.WriteLine("**** Fun with Enums *****");
    EmpType e2;
    // These types are enums in the System namespace.
    DayOfWeek day;
    ConsoleColor cc;
    EvaluateEnum(e2);
    EvaluateEnum(day);
    EvaluateEnum(cc);
    Console.ReadLine();
}

System.Enum还定义了另外一个名为GetValues()的静态方法.这个方法返回System.Array的一个实例.数组中每一项都对应指定枚举的一个成员.考虑如下方法,它会输出作为参数传入的任何枚举中的每一个名称/值对:

// 这个方法会输出任何枚举的细节
static void EvaluateEnum(System.Enum e)
{
    Console.WriteLine("=> Information about {0}", e.GetType().Name);
    Console.WriteLine("Underlying storage type: {0}",
    Enum.GetUnderlyingType(e.GetType()));
    // 获取传入参数的名称/值对.
    Array enumData = Enum.GetValues(e.GetType());
    Console.WriteLine("This enum has {0} members.", enumData.Length);
    // 现在显示字符串名和关联的值.
    for (int i = 0; i < enumData.Length; i++)
    {
        Console.WriteLine("Name: {0}, Value: {0:D}",
        enumData.GetValue(i));
    }
    Console.WriteLine();
}

为了测试这个新方法,我们更新Main()方法来创建几个在System命名空间中声明的枚举类型(EmpType枚举也可以用来测试).例如:

static void Main(string[] args)
{
    Console.WriteLine("**** Fun with Enums *****");
    EmpType e2;
    //这些类型在System命名空间中.
    DayOfWeek day;
    ConsoleColor cc;
    EvaluateEnum(e2);
    EvaluateEnum(day);
    EvaluateEnum(cc);
    Console.ReadLine();
}


when you make use of any enumeration, always remember that you are able to interact with the name/value pairs using the members of System.Enum.

使用枚举时,可以通过System.Enum的成员来和名称/值对进行交互.

 

Structures can define constructors, can implement interfaces, and can contain any number of properties, methods, events, and overloaded operators.

结构可以定义构造函数,实现接口,还可以包含许多属性,方法,事件以及重载运算符。

 

Note If you have a background in OOP , you can think of a structure as a “lightweight class type,” given that structures provide a way to define a type that supports encapsulation, but cannot be used to build a family of related types (as structures are implicitly sealed).When you need to build a family of related types through inheritance, you will need to make use of class types.

说明  如果你有OOP背景,可以把结构看成是“轻量级的类类型”,因为结构提供了一种方式来定义这样一种类型,它们会支持封装,但不能用来构建一族相关类型(因为结构是隐式封装的)。如果我们需要通过继承来构建一族相关类型,就需要使用类类型。

 

Structure是存储在栈上,Class是存储在堆上。所以Stucture是值类型,Class是引用类型。

如果一个Structure中包含类的对象,那么将这个结构体的实例赋值给另一个实例的时候,他俩共享同一个类的实例。即其中一个结构体实例对该类对象的修改,都将影响另一个实例的该类对象成员的值的改变。(浅复制)

 

Passing Reference Types by Value

static void SendAPersonByValue(Person p)
{
    // Change the age of "p"
    p.personAge = 99;
    // Will the caller see this reassignment? No
    p = new Person("Nikki", 99);
}

As you can see, the value of personAge has been modified. This behavior seems to fly in the face of what it means to pass a parameter “by value.” Given that you were able to change the state of the incoming Person, what was copied? The answer: a copy of the reference to the caller’s object. Therefore, as the SendAPersonByValue()method is pointing to the same object as the caller, it is possible to alter the object’s state data. What is not possible is to reassign what the reference is pointing to.

按值传递引用类型

static void SendAPersonByValue(Person p)
{
    // 改变"p"的年龄
    p.personAge = 99;
    // 调用者能看到这个重新赋值吗?不能
    p = new Person("Nikki", 99);
}

可以看出,personAge的值被修改了。这个行为看起来似乎违反了“按值”传递的语义。如果能够改变传入的Person的状态,那么复制到是什么?答案是:复制了指向调用者对象的引用。由于SendAPersonByValue()方法与调用者指向同一个对象,所以改变对象的状态数据是可能的。但是无法把引用重新赋值给一个新的对象。

 

Passing Reference Types by Reference

static void SendAPersonByReference(ref Person p)
{
    // Change some data of "p".
    p.personAge = 555;
    // "p" is now pointing to a new object on the heap!
    p = new Person("Nikki", 999);
}

The golden rule to keep in mind when passing reference types:

• If a reference type is passed by reference, the callee may change the values of the object’s state data as well as the object it is referencing.

• If a reference type is passed by value, the callee may change the values of the object’s state data but not the object it is referencing.

按引用传递引用类型

static void SendAPersonByReference(ref Person p)
{
    // 改变 "p"的一些数据.
    p.personAge = 555;
    // "p"现在指向了堆上的一个新对象!
    p = new Person("Nikki", 999);
}

按引用传递引用类型时需要记住的黄金法则如下:

  • 如果按引用传递引用类型,被调用者可能改变对象的状态数据的值和所引用的对象;
  • 如果按值传递引用类型,被调用者可能改变对象的状态数据的值,但不能改变所引用的对象。

 

Value Types and Reference Types Side by Side

Intriguing   Question

Value   Type

Reference   Type

Where is this type allocated?

Allocated on the stack.

Allocated on the managed heap

How is a variable represented?

Value type variables are local copies.

Reference type variables are pointing to the memory occupied   by the allocated instance.

What is the base type?

Must derive from System.ValueType.

Can derive from any other type (except   System. ValueType), as long as that type is not “sealed”

Can this type function as a base to other   types?

No. Value types are always sealed and   cannot be extended.

Yes. If the type is not sealed,  it may function as a base to other types.

What is the default parameter passing   behavior?

Variables are passed by value (i.e., a   copy of the variable is passed into the called function).

Variables are passed by reference (i.e.,   the address of the variable is passed into the called function).

Can this type override System.Object.Finalize()?

No. Value types are never placed onto the   heap and therefore do not need to be finalized.

Yes, indirectly

Can I define constructors for this type?

Yes, but the default constructor is   reserved (i.e., your custom constructors must all have arguments).

But of course

When do variables of this type die?

When they fall out of the defining scope.

When the object is garbage collected.

值类型和引用类型的比较

问题

值类型

引用类型

这个类型分配在哪里?

分配在栈上

分配在托管堆上

变量是怎样表示的?

值类型变量是局部复制

引用类型变量指向被分配的实例所占用的内存

基类型是什么?

必须派生自System.ValueType

可以派生自除了System.ValueType以外任何类型,只要那个类型不是密闭的。

这个类型能作为其他类型的基类吗

不能。值类型总是密闭的,不能被继承

能。如果这个类型不是密闭的,它可以作为其他类型的基类

默认的参数传递行为是什么?

变量是按值传递的(也就是,一个变量的副本传入被调用的喊声)

变量按引用传递(也就是,变量的地址传入被调用的函数)

这个类能重写System.Object.Finalize()吗?

不能。值类型不会放在堆上,因此不需要被终结

可以间接地重写

可以为这个类型定义构造函数吗?

是的,但是默认的构造函数被保留(也就是自定义构造函数必须全部带有参数)

当然

这个类型的变量什么时候消亡?

当它们越出定义的作用域时

档托管堆被垃圾回收时

 

C# Nullable Types

Since the release of .NET 2.0, it has been possible to create nullable data types. Simply put, a nullable type can represent all the values of its underlying type, plus the value null. Thus, if we declare a nullable System.Boolean, it could be assigned a value from the set {true, false, null}.

To define a nullable variable type, the question mark symbol (?) is suffixed to the underlying data type. Do note that this syntax is only legal when applied to value types. Like a nonnullable variable, local nullable variables must be assigned an initial value:

static void LocalNullableVariables()
{
    // Define some local nullable types.
    int? nullableInt = 10;
    double? nullableDouble = 3.14;
    bool? nullableBool = null;
    char? nullableChar = 'a';
    int?[] arrayOfNullableInts = new int?[10];
    // Error! Strings are reference types!
    // string? s = "oops";
}

C#可空类型

自从.NET 2.0发布以后,我们就可以创建可空数据类型了。简而言之,可空类型可以表示所有基础类型的值加上null。因此,如果声明一个可空的System.Boolean,就可以从集合{true,false,null}进行赋值。

为了定义一个可空变量类型,应在底层数据类型中添加问号(?)作为后缀。注意,这种语法只对值类型是合法的。与非可空变量一样,局部可空变量必须赋一个初始值。

static void LocalNullableVariables()
{
    //定义一些局部可空类型。
    int? nullableInt = 10;
    double? nullableDouble = 3.14;
    bool? nullableBool = null;
    char? nullableChar = 'a';
    int?[] arrayOfNullableInts = new int?[10];
    // 错误!字符串是引用类型!
    // string? s = "oops";
}

 

You are able to programmatically discover whether the nullable variable indeed has been assigned a null value using the HasValue property or the != operator. The assigned value of a nullable type may be obtained directly or via the Value property. Given that the ? suffix is just a shorthand for using Nullable, you could implement your LocalNullableVariables()method as follows:

static void LocalNullableVariables()
{
    // Define some local nullable types using Nullable.
    Nullable<int> nullableInt = 10;
    Nullable<double> nullableDouble = 3.14;
    Nullable<bool> nullableBool = null;
    Nullable<char> nullableChar = 'a';
    Nullable<int>[] arrayOfNullableInts = new int?[10];
}

可以通过编程,用HasValue属性或者 != 运算符判断,一个可空变量是否确实被赋予了一个null值。可空类型被赋的值可以通过Value属性获得或直接获得。因为?后缀只是使用Nullable的一种简化表示,所以可以按如下实现:

static void LocalNullableVariables()
{
    // 使用Nullable定义一些局部可空变量.
    Nullable<int> nullableInt = 10;
    Nullable<double> nullableDouble = 3.14;
    Nullable<bool> nullableBool = null;
    Nullable<char> nullableChar = 'a';
    Nullable<int>[] arrayOfNullableInts = new int?[10];
}

 

The ?? Operator

This operator allows you to assign a value to a nullable type if the retrieved value is in fact null. For this example, assume you wish to assign a local nullable integer to 100 if the value returned from GetIntFromDatabase() is null:

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Nullable Data *****\n");
    DatabaseReader dr = new DatabaseReader();
    ...
    // If the value from GetIntFromDatabase() is null,
    // assign local variable to 100.
    int? myData = dr.GetIntFromDatabase() ?? 100;
    Console.WriteLine("Value of myData: {0}", myData.Value);
    Console.ReadLine();
}

??运算符

在获取的值实际上是null时,我们可以用这个运算符给一个可空类型赋值。对上面这个示例来说,假设从GetIntFromDatabase()返回的值是null,希望给一个局部可空整型赋值为100:

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Nullable Data *****\n");
    DatabaseReader dr = new DatabaseReader();
    ...
    // 从GetIntFromDatabase()返回的值为null时,将局部变量赋值为100.
    int? myData = dr.GetIntFromDatabase() ?? 100;
    Console.WriteLine("Value of myData: {0}", myData.Value);
    Console.ReadLine();
}

 

转载于:https://www.cnblogs.com/tangzhengyue/archive/2012/05/21/2511435.html

你可能感兴趣的:(c#)