CLR via C# 总结之Chap5 Primitive, Reference, and Value Types

Primitive Types

Any data types the compiler directly supports are called primitive types. Primitive types map directly to types existing in the Framwork Class Library(FCL).

图1

  1. The primitive type names are short and thus giving some terms of convinience. However, the author doesn’t support the use of it and here are his reasons:

    • In common sense of many programmers, int represents a 32-bit integer on a 32-bit OS and a 64-bit integer on a 64-bit OS. But this is NOT true for C#. In C#, int always maps to System.Int32. Using Int32 will remove potential confusion.
    • In C#, long maps to System.Int64, while in a different language, it maps to an Int16 or Int32. Someone reading source code in one language could easily misinterpret the code’s intention if he is used to programming in a different language. This is also true for other language, not just C#.
    • The FCL has many methods that have type names as part of their method names. So you may feel unnatural to use float val = br.ReadSingle(); although it’s right. (In my view, if you feel OK, then it doesn’t matter a lot)

    So, it does have some effect on the developing of coding, we can try to use its real name then.

  2. 2.
Int32 i = 5;
Int64 j = i; // Implicit cast does happen

Based on the casting rules, this code should NOT be able to compile because nither one of this two type derives from the other. However, it does compile because C# compiler has intimate knowledge of primitive types and applies special rules when compiling the code —— It produces necessary IL to make things happen as expected when it recognize common programming patterns like this.

  1. C# allows implicit casts if the conversion is “safe”, which means no loss of data is possible. If the conversion is potentially unsafe, it requires explicit cast. Keep in mind that different compilers can apply different casting rules so you need to be careful.

  2. Primitive types can be written as literals. A literal is considered to be an instance of the type itself, so it can call the methods of its corresponding type directly.

Checked and unchecked primitive type operations
  1. Byte b = 100;
    b = (Byte) (b + 200); // Implicit cast in the right parenthesis because no data loss, b now contains 44

    The first step requires b be expanded to 32-bit value(or 64-bit value if any operand requires more than 32 bits).

    In most programming scenarios, silent overflow is undesirable. However, in some rare programming scenarios, such as calculating a hash value or a checksum, this overflow is not only acceptable but is also desired.

    By default, overflow is NOT checked in C#. But if you use checking version, the IL instructions like add will be replaced by their checking versions, like add.ovf. You make the choice.

    The way to check overflow:

    1. use the /checked+ compiler switch to turn on/off gloabally
    2. use code like : checked(...); unchecked(...); checked{...}
    Byte b = 100;
    
    b = checked((Byte) (b + 200)); // OverflowException is thrown
    b = (Byte) checked(b + 200); // No OverflowException
    
    checked{
        Byte c = 100;
        c += 200; // Can use += if we use checked block
    }

    Attention: Calling a method within a checked operator or statement has no impact on that method

    checked{
        SomeMethod(300);
        // Assume SomeMethod tries to load 300 into a Byte
        // It will NOT throw an OverflowException if it is not compiled with checked instructions
    }
  2. Programming Recommendation

    • Use signed data types wherever possible. This allows the compiler to detect more overflow/underflow errors. Also, some parts of the class library(such as Length properties of Array and String are hard-coded to return signed values.
    • Explicitly use checked around blocks where an unwanted overflow might occur due to invalid input data. Also, OverflowException can be caught as well.
    • Explicitly use unchecked around blocks where overflow is OK.

Reference Types and Value Types

  1. Value type instances don’t use GC(garbage collector), so their use reduces pressure in the managed heap and reduces the number of collections and application requires over its lifetime.
  2. In .NET Framework SDK ducumentation, any type called a class is a reference type, while each value type is referred to as a structure or an enumeration. All of the structures are immediately derived from System.ValueType abstract type, which is itself immediately derived from System.Object type.

    struct SomeVal{
        public Int32 x;}
    static void Demo(){
       SomeVal v1 = new SomeVal(); // Allocated on the thread's stack!!! new doesn't mean heap as it does in C++
    }

    In C#, types declared using struct are value types, and types declared using class are reference types.

    SomeVal v1 = new SomeVal(); // line 1
    Int32 a = v1.x;
    
    SomeVal v2; // line 3
    Int32 b = v2.x; // error CS0170: Use of possibly unassigned field 'x'

    In fact, both line 1 and 3 produce IL that allocates the instance on the thread’s stack and zeroes the fields. The only difference is that C# “thin

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值