zoukankan      html  css  js  c++  java
  • C# struct

    简介

    C/C++程序员或多或少都有使用struct的经历,在C++structclass的区别不大,除了默认成员的可访问性,这点在C#中则截然不同。本文将力图说明C#structclass的区别以及如何正确的使用struct

    为什么需要struct?

    众所周知,在java中并没有struct的概念,那么C#为何引入struct呢?最基本原因是可以创建值类型的类型,使在托管环境中有更好的性能。

    区别于javaC#有值类型和引用类型的概念(java只有引用类型)。引用类型的实例分配在堆上,当对象没有被引用时被垃圾回收器回收;值类型的实例分配在栈上,当离开其作用域后内存被回收。值类型本身存储的是值本身,引用类型存储的是引用,C#语言提供的原始类型除了string类型其它都是值类型。

    C#struct是值类型,class是引用类型,可以通过enumstruct关键字创建值类型的对象。使用值类型可以减少托管堆上对象的数量,从而减少垃圾回收器(GC)的负担,提高性能,值类型也有明显的缺点:通过值类型传递较大对象的开销比较昂贵、装箱和拆箱对性能造成影响。

    Classes 和Structs

     public struct Employeestruct
        {
            private int _fooNumber;
    
            public Employeestruct(int fooNumber)
                : this()
            {
                _fooNumber = fooNumber;
            }
    
            public string Name { get; set; }
    
            public int Age { get; set; }
    
            public string GetMessage()
            {
                return string.Format("{0}--{1}", Name, Age);
            }
        }

    从上面可以看到,structclass非常相似,不过它们还是有本质的区别,接下来我们就一一分析。

    1. Structs 和Inheritance

    StructsSystem.ValueType继承而classes继承于System.Object或从System.Object继承的其他类型,当然System.ValueType也从System.Object继承(这不是重点)Structs可以实现接口,但不能从另一个classesstructs继承,而且不能作为其他classes的基类。要知道,当你把structs作为接口使用时,就进行一次隐形装箱,因为接口作为引用类型。请看下面代码:

    struct Foo : IFoo
    { 
          int x;
    }
    
    IFoo iFoo = new Foo();

    Foo的实例被创建并赋值给iFoo(装箱),当iFoo调用时实际上使用的是Foo装箱后的实例。

    2. Constructors

    尽管CLR允许,但是不允许在structs中定义无参的构造函数。对于值类型,编译器默认情况下不生成默认的构造函数,也不调用默认的构造函数,所以C#编译器不允许使用者定义无参的构造函数。由于structs不生成默认的构造函数,所以不能初始化字段。如下(错误):

    Struct MyFoo
    {
            int x = 1;
    }

    记住,编译器把初始化工作放在构造函数中,由于structs没有默认的构造函数,所以不能初始化字段。

    有趣的是,你可以使用下面的语法:

    Foo foo  =  new Foo();

    通过之前的章节了解到,尽管初始化foo使用了new操作符,但是structs的实例分配到栈上。new Foo()不会调用无参的构造函数,仅仅是初始化该struct的字段为默认值。

    struct Foo
    {
           int x;
    
           public Foo(int x)
           {
               this.x = x;
           }
    }    
    
    Foo foo  =  new Foo();

    注意,我已经重载了构造函数,然而我能够调用new Foo()

    3. Destructors

    Structs不允许定义析构函数,如果你扔就去定义一个析构函数,编译器会立即提示一个错误,不过structs可以实现IDisposable接口。

    4. Comparison against null

    Structs不能和null进行比较,这点在.net framework2.0已不再是问题(可空类型),关于可空类型,超出本文讨论范围。

    5. Readonly关键字

    对于引用类型,readonly阻止再为变量赋值,但不阻止你修改当前引用对象的状态。对于值类型,readonly有点类似C++中的const关键字,它阻止你修改引用对象,也意味着不能再为他赋值,因为这会导致重新初始化一次。请看下面代码:

        public class MyReferenceType
        {
            public int State { get; set; }
        }
    
        public struct MyValueType
        {
            public int State { get; set; }
        }
    
       public void TestMethod()
        {
            myReferenceType = new MyReferenceType(); // 错误
            myReferenceType.State = 1234; // Ok
    
            myValueType = new MyValueType(); // 错误
            myValueType.State = 1234; //  错误
        }

    foreach 语句和using语句的变量为隐式readonly,所以如果使用structs,将无法更改其状态。

    何时使用 structs

    通过前面的描述已经清楚classesstructs的区别,那我们来看下适合使用structs的场景:

    l 实例使用起来像C#的基元类型

    l 需要创建大量的、短暂实例(例如在循环体内)

    l 实例不需要大量的传递

    l 不需要从其他类型继承或不希望被其他类型继承

    l 希望被人操作你实例的副本

    不适合使用structs的场景:

    l 实例过大,当实例过大时,传递实例会有很大的开销。微软建议structs的理想大小应该在16bytes以下

    l 当回引起装箱、拆箱操作时。关于装箱、拆箱超出本文讨论范围

  • 相关阅读:
    关于数组的一些面试题目及答案
    java类的定义以及参数传递
    第39届大学生程序设计大赛亚洲区域赛广州站比赛总结
    模拟退火法(吊打XXX)Bzoj3680
    树链剖分(单点更新,求区间最值,区间求和Bzoj1036)
    树链剖分(线段树区间更新求和(lazy操作)hdu3966)
    最大权闭合图(Road constructions)hdu3917
    最大权闭合图最大获益(把边抽象为点)HDU3879
    最大权闭合图hdu3996
    最大密集子图(01分数规划+二分+最小割)POJ3155
  • 原文地址:https://www.cnblogs.com/oxdavidsun/p/4835361.html
Copyright © 2011-2022 走看看