您的位置:首页 > 编程语言 > C#

CLR.via.Csharp.3rd Chapter 5: Primitive, Reference, and Value Types

2010-07-11 21:24 525 查看

Programming Language Primitive Types

Any data types the compiler directly supports are called primitive types .

I prefer to use the FCL type names(System.Int64) and completely avoid the primitive type names(long) .A primitive type name could map to different FCL type in different programming languages.

Checked and Unchecked Primitive Type Operations

The CLR has an instruction performs no overflow checking  and another performs overflow checking for add,sub,mul,conv.Overflow checking operations execute a little slower because the CLR is checking these operations to determine whether an overflow occurred .

Use the /checked+ compiler switch ,checked operator,checked and unchecked statements to control overflows .

Reference Types and Value Types

Reference type:class;Value Type:structure or an enumeration .

For reference types:

The memory must be allocated from the managed heap .
Each object allocated on the heap has some additional overhead members associated with it that must be initialized (the type object pointer and the sync block index).
The other bytes in the object (for the fields) are always set to zero.
Allocating an object from the managed heap could force a garbage collection to occur .
Value type instances are usually allocated on a thread’s stack (although they can also be embedded as a field in a reference type object) . the variable contains the fields of the instance itself .

All of the structures are immediately derived from the System.ValueType.All enumerations are derived from the System.Enum abstract type, which is itself derived from System.ValueType .

Declare a type as a value type when:

The type acts as a primitive type .
The type doesn’t need to inherit from any other type .
The type won’t have any other types derived from it .
Instances of the type are small (approximately 16 bytes or less) .
Instances of the type are large (greater than 16 bytes) and are not passed as method parameters or returned from methods .
When you assign a value type variable to another value type variable, a field-by-field copy is made . When you assign a reference type variable to another reference type variable, only the memory address is copied .

Boxing and unboxing Value Types

It’s possible to convert a value type to a reference type by using a mechanism called boxing .

Memory is allocated from the managed heap .
The value type’s fields are copied to the newly allocated heap memory .
The address of the object is returned .
When convert a reference type to a value type:

The address of the fields in the boxed object is obtained . This process is called unboxing .
The values of these fields are copied from the heap to the stack-based value type instance .
Boxing and unboxing/copy operations hurt application’s performance.

The value type instance being used to invoke the virtual method(such as Equals, GetHashCode, or ToString) is not boxed . However, if your override of the virtual method calls into the base type's implementation of the method, then the value type instance does get boxed.

Calling a nonvirtual inherited method (such as GetType or MemberwiseClone)  always requires the value type to be boxed .

Casting an unboxed instance of a value type to one of the type’s interfaces  requires the instance to be boxed.

Changing Fields in a Boxed Value Type by Using Interfaces (and Why You Shouldn’t Do This)

Some languages, such as C++/CLI, let you change the fields in a boxed value type, but C# does not . However, you can fool C# into allowing this by using an interface . An interface method is able to modify the fields of a boxed(pre-boxed) value type .

Value types should be immutable so shouldn’t do this.

Object Equality and Identity

You should always call ReferenceEquals if you want to check for identity (if two references point to the same object) .

Microsoft(and we) should have implemented Object’s Equals like this:

public class Object {
public virtual Boolean Equals(Object obj) {
// The given object to compare to can't be null
if (obj == null) return false;
// If objects are different types, they can't be equal.
if (this.GetType() != obj.GetType()) return false;
// If objects are same type, return true if all of their fields match
// Since System.Object defines no fields, the fields match
return true;
}
}


But, since Microsoft didn’t implement Equals this way, when a type overrides Equals, the override should call its base class’s implementation of Equals unless it would be calling Object’s implementation .

System.ValueType does override Object’s Equals method and is correctly implemented to perform a value equality check (not an identity check) . Internally, ValueType’s Equals method uses reflection .

When defining your own type, if you decide to override Equals, you must ensure that:

Equals must be reflexive; that is, x.Equals(x) must return true .

Equals must be symmetric; that is, x.Equals(y) must return the same value as y.Equals(x) .

Equals must be transitive; that is, if x.Equals(y) returns true and y.Equals(z)  returns true, then x.Equals(z) must also return true .

Equals must be consistent . Provided that there are no changes in the two values being compared, Equals should consistently return true or false .

Object Hash Codes

If you define a type and override the Equals method, you should also override the GetHashCode method . The reason is that the implementation of the Hashtable type, the Dictionary type, and some other collections require that any two objects that are equal must have the same hash code value .

Guidelines for selecting an algorithm for calculating hash codes :

Use an algorithm that gives a good random distribution for the best performance of the hash table .

Your algorithm can also call the base type’s GetHashCode method, including its return value . However Object’s or ValueType’s method doesn’t lend itself to highperformance hashing algorithms .

Your algorithm should use at least one instance field .

Ideally, the fields you use in your algorithm should be immutable.

Your algorithm should execute as quickly as possible .

Objects with the same value should return the same code .

You should never, ever persist hash code values . The reason is that hash code values are subject to change .

The dynamic Primitive Type

A dynamic expression is really the same type as System.Object . The compiler  assumes that whatever operation you attempt on the expression is legal, so the compiler will not generate any warnings or errors . However, exceptions will be thrown at runtime if you attempt  to execute an invalid operation .
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: