• 二、对象和类型(第四部分)


    四、匿名类型

      var关键字,它用于表示隐式类型化的变量。var与new关键字一起使用时,可以创建匿名类型。匿名类型只是继承自Object且没有名称的类。该类的定义从初始化器中推断,类似于隐式类型化的变量。

     

    五、结构

      结构是值类型,而不是引用类型。它们存储在栈中或存储为内联(inline)(如果它们是存储在堆中的另一个对象的一部分),其生存周期的限制与简单的数据类性一样。

      *.结构不支持继承。

      *.对于结构,构造函数的工作有一些区别。尤其是在编译器总是提供了一个无参数的默认构造函数,它是不允许替换的。

      *.使用结构,可以指定字段如何在内存中布局。因为结构实际上是把数据项组合在一起,有时大多数或者全部字段都声明为public。严格来说,这与编写.Net代码的规则相反------根据Mircrosoft,字段(除了const字段之外)应总是私有的,并由公有属性封装。但是,对于简单的结构,许多开发人员都认为共有字段是可接受的编程方式。

    1.结构是值类

      虽然结构是值类型,但是在语法上常常可以把它们当作类来处理。

      注意,因为结构是值类型,所以new运算符与类和其他引用类型的工作方式不同。new运算符并不分配堆中的内存,而是指调用相应的构造函数,根据传送给它的参数,初始化所有的字段。

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _003
    {
        public class MyClass
        {
            const int a = 123456;
            readonly int b;
            int c = 0;
    
            private MyClass()
            {
                b = 123456;
                c = 654321;
            }
    
            public static MyClass CreateMyClass()
            {
                MyClass my = new MyClass();
                return my;
            }
    
            public void Printf()
            {
                Console.WriteLine(a + "  " + b + "  " + c);
            }
        }
    
        struct MyStruct
        {
            public int a;
            public int b;
    
            public MyStruct(int _a, int _b)
            {
                a = 100;
                b = 101;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                //const int a = 100;
                //readonly int b;
                MyClass mc = MyClass.CreateMyClass();
                mc.Printf();
    
                MyStruct ms = new MyStruct(1, 2);
                Console.WriteLine(ms.a + "  " + ms.b);
    
                Console.ReadKey();
            }
        }
    }

     

      结构遵循其它数据类型都遵循的规则:在使用前所有的元素都必须初始化。在结构上调用new运算符,或者给所有的字段分别赋值。结构就完全初始化了。当然,如果结构定义为类的成员字段,在初始化包含的对象时,该结构会自动初始化为0。

      结构是会影响性能的值类型,但根据使用的结构方式。这种影响可能是正面的,也可能时负面的。正面的影响是为结构分配内存时,速度非常快,因为它们将内联或者保存在栈中。在结构超出了作用域被删除时,速度也很快,不需要等待垃圾回收。负面影响是,只要把结构作为参数来传递或者把一个结构赋予另一个结构(如A = B, 其中A 和 B 是结构),结构的所有内容就被复制,而对于类,则只复制引用。这样就会有性能损失,根据结构的大小,性能损失也不同。注意,结构主要用于小的数据结构。但当把结构作为参数传递给方法时,应把它作为ref参数传递,以避免性能损失------此时只传递了结构在内存中的地址,这样传递速度就与在类中的传递速度一样快了。但如果这样做,就必须注意被调用的方法可以改变结构的值。 

    2.结构和继承

      结构不是为继承而设计的。这意味着:它不能从一个结构中继承。唯一的例外是对应的结构(和C#中的其它类型一样)最终派生于System.Object。因此,结构也可以访问System.Object的方法。在结构中,甚至可以重写System.Object中的方法------如重写ToString()方法。结构的继承链是:每个结构派生自System.ValueType类,System.ValueType类又派生自System.Object。ValueType并没有给Object添加任何新成员,但提供了一些更适合结构的实现方式。注意:不能为结构提供其它基类:每个结构都派生自ValueType。

    3.结构的构造函数

      为结构定义构造函数的方式与为类定义构造函数的方式相同,但不允许定义无参数的构造函数。这看起来似乎没有意义,但其原因隐藏在.NET运行库的实现方式中。在一些罕见的情况中,.NET运行库不能调用用户提供的自定义无参构造函数,因此Microsoft干脆采用一种非常简单的方式:禁止在C#的结构内使用无参数的构造函数。

      默认构造函数把数值的字段初始化为0,把引用类型字段初始化为null,且总是隐式地给出,即使提供了其他带参数的构造函数,也是如此。提供字段的初始值也不能绕过默认构造函数。

     

    6.弱引用

      在应用程序代码内实例化一个类或结构时,只要有代码引用它,就会形成强引用。例如,如果有一个类MyClass(),并创建了一个变量 myClassVariable来引用该类的对象,那么只要myClassVariable在作用域内,就存在对MyClass对象的强引用,如下:

                                                                 MyClass myClassVariable = new MyClass();

      这意味着垃圾回收器不会清理MyClass对象使用的内存。一般而言这是好事,因为可能需要访问MyClass对象,但是如果MyClass对象很大,并且不经常访问呢?此时可以创建对象的弱引用。

      弱引用允许创建和使用对象,但是垃圾回收器运行时,就会回收对象并释放内存。由于存在潜在的Bug和性能问题,一般不会这么做,但是在特定情况在使用弱引用时很合理的。

      弱引用是使用WeakReference类创建的。因为对象可能在任意时刻被回收,所以在引用该对象前必须确认它存在。以前面的MathTest类为例,这次使用WeakReference类创建对它的弱引用:

      

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _004
    {
        class MyClass
        {
            private int a;
            private int b;
            public int A
            {
                get
                {
                    return a;
                }
                set
                {
                    a = value;
                }
            }
    
            public int B
            {
                get
                {
                    return b;
                }
                set
                {
                    b = value;
                }
            }
    
            public MyClass()
            {
                a = 100;
                b = 101;
            }
    
            public void Printf()
            {
                Console.WriteLine(A + "   " + B);
            }
        }
      
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass();
                mc.Printf();
    
                WeakReference myWR = new WeakReference(new MyClass());
    
                MyClass mc2;
    
                if (myWR.IsAlive)
                {
                    mc2 = myWR.Target as MyClass;
                    mc.A = 1993;
                    mc.Printf();
                }
    
                Console.ReadKey();
            }
    
        }
    }

      创建myWR时,会向其结构函数传递一个新的MyClass对象。MyClass对象成为了WeakReference对象的目标。想要使用MyClass对象时,就需要检查myWR对象以确保其未被回收。IsAlive属性就用于这个目的。如果IsAlive为true,就从目标属性得到MyClass对象的引用。注意,因为Target属性返回的是Object类型,所以必须将其强制转化为MyClass类型。然后,调用垃圾回收器(CG.Collect()),并尝试再次获得MyClass对象。这一次,IsAlive属性返回false,如果确实想要用MyClass对象,就必须实例化一个新的MyClass对象。

     

    7.部分类

      partial关键字允许把类、结构、方法或借口放在多个文件中。一般情况下,一个类全部驻留在单个文件中,但有时,多个开发人员需要访问同一个类,或者某种类型的代码生成器生成了一个类的某部分,所以把类放在多个文件中是有益的。

      partial关键字的用法是:把partial放在class、struct或interface关键字的前面。在下面的例子中,TheBigClass类驻留在两个不同的源文件BigClassPart1.cs 和 BigClassPart2.cs中。

      编译包含这两个源文件的项目时,会创建一个TheBigClass类,它有两个方法 MethodOne 和 MethodTwo()。

      如果声明类时使用了下面的关键字,这些关键字就必须应用于同一个类的所有部分:

      *public

      *private

      *protected

      *internal

      *abstract

      *sealed

      *new

      *一般约束

      在嵌套的类型中,只要partial关键字位于class关键字的前面,就可以嵌套部分类。在把部分类编译到类型中时,属性、XML注释、接口、泛型类型的参数属性和成员合并。

     

    8.静态类

    如果类只包含静态方法和属性,该类就是静态类。静态类在功能上与使用私有静态构造函数创建的类相同。不能创建静态类的实例。使用static关键字,编译器可以检查用户是否不经意间给该类添加了实例成员。如果是,就生成一个编译错误。这可以确保不创建静态类的实例。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _004
    {
        static class MyClass
        {
            static int a = 123;
            public static void Printf()
            {
                Console.WriteLine("静态类输出{0}", a);
            }
        }
     
        class Program
        {
            static void Main(string[] args)
            {
                MyClass.Printf();
    
                Console.ReadKey();
            }
    
        }
    }

    9.Object类

      前面提到过,所有的.NET类都派生自System.Object。实际上,如果在定义类时没有指定基类,编译器就会自动假定这个类派生自Obejct。本章举的例子没用使用继承,所以前面介绍的每个类都是派生自System.Object。对于结构,这个派生是间接的:就够总是派生自System.ValueType,System.ValueType又派生自System.Object。

      其实际意义在于,除了自己定义的方法和属性等外,还可以访问为Obejct定义的许多公有的和受保护的成员方法。这些方法可用于自己定义的所有其它类中。

      1.System.Object()方法

      下面简要总结每个方法的作用。

      *ToString()方法:是获取对象的字符串表示的一种便捷方式。当只需要快速获取对象的内容,以进行调试时,就可以使用这个方法。在数据的格式化方面,它几乎没有提供选择:例如,在原则上日期可以表示为许多不同的格式,但DateTime.ToString()没有在这方面提供任何选择。如果需要更复杂的字符串表示,例如,考虑用户的格式化首选项或区域性(区域),就应实现IFormattable接口。

      *GetHashCode()方法:如果对象放在名为映射(也称为散列表或字典)的数据结构中,就可以使用这个方法。处理这些结构的类使用该方法确定把对象放在结构的什么地方。如果希望把类用作字典的一个键,就需要重写GetHashCode()方法。实现该方法重载的方式有一些相当严格的限制。

      *Equals()(两个版本)和ReferenceEquals方法:注意有3个用于比较对象相等性的不同方法,这说明.NET Framework 在比较相等性方面有相当复杂的模式。这3个方法和比较运算符“==”在使用方式上有微妙的区别。而且,在重写带一个参数的虚Equals方法时也有一些限制,因为System.Collections名称空间中的一些基类要调用该方法,并希望它以特定的方式执行。

      *Finalize()方法:它最接近C++风格的析构函数,在引用对象作为垃圾被回收以清理资源时调用它。Object中实现的Finalize()方法实际上什么也没有作,因而被垃圾回收器忽略。如果对象拥有对未托管资源的引用,则在该对象被删除时,就需要删除这些引用,此时一般要重写Finalize()。垃圾收集器不能直接删除这些对未托管资源的引用,因为它只负责托管的资源。,于是它只能依赖用户提供的Finalize()。

      *GetType()方法:这个方法返回从System.Type派生的类的一个实例。这个对象可以提供对象成员所属类的更多信息,包括基本类型、方法、属性等。System.Type还提供了.NET的反射技术的入口点。

      *MemberwiseClone()方法:它只复制对象,并返回对副本的一个引用(对于值类型,就是一个装箱的作用)。注意,得到的副本是一个浅表赋值,即它复制了类中的所有值类型。如果类包含内联的引用,就只复制引用,而不复制引用的对象。这个方法是受保护的,所以不能用于复制外部的对象。该方法不是虚方法,所以不能重写它的实现代码。

    2.ToString方法

      ToString()方法,它是快速获取对象的字符串表示的最便捷方式。

      Object.ToString()声明为虚方法,在这些例子中,实现该方法的代码都是为C#预定义数据类型重写过的代码,以返回这些类型的正确字符串表示。Colors枚举是一个预定义的数据类性,它实际上实现为一个派生自System.Enum的结构,而System.Enum有一个相当智能的ToString()重写方法,它处理用户定义的所有枚举。

      如果不在自己定义的类中重写ToString(),该类将只继承System.Object的实现方式------它显示类的名称。如果希望ToString()返回一个字符串,其中包含类中对象的值信息,就需要重写它。

      该例子仅说明了C#的语法特性。C#已经有表示货币量的预定义的类型decimal。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _004
    {
        public class CStudentsInfo
        {
            private string m_Name;
            private int m_Age;
            private string m_Gender;
    
            public string NAME
            {
                get
                {
                    return m_Name;
                }
                set
                {
                    m_Name = value;
                }
            }
    
            public int AGE
            {
                get
                {
                    return m_Age;
                }
                set
                {
                    m_Age = value;
                }
            }
    
            public string GENDER
            {
                get
                {
                    return m_Gender;
                }
                set
                {
                    m_Gender = value;
                }
            }
    
            public CStudentsInfo(string _name, string _gender, int _age)
            {
                m_Name = _name;
                m_Age = _age;
                m_Gender = _gender;
            }
    
            public override string ToString()
            {
                string str = string.Empty;
    
                StringBuilder sb = new StringBuilder();
                sb.Append("我的名字叫:");
                sb.Append(m_Name);
                sb.Append("
    ");
    
                sb.Append("我今年:");            
                sb.Append(m_Age.ToString());
                sb.Append("岁了!
    ");
    
                sb.Append("我是:");
                sb.Append(m_Gender);
    
                return sb.ToString();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                CStudentsInfo studentInfo = new CStudentsInfo("夏洛特! ",  "男的! ", 5);
    
                string str = studentInfo.ToString();
                Console.WriteLine(str);
                Console.ReadKey();
            }
    
        }
    }

    10.扩展方法

     

     

     

     

     

    Dean二十七
  • 相关阅读:
    C# 创建与读写配置文件
    C# 绘图三种方式
    WindowsForms获取服务名称
    Hbase之JAVA API不能远程访问问题解决
    Jenkins之自动构建
    Jenkins配置匿名用户拥有只读权限
    XShell中文乱码问题解决
    mybatis之关联(2)
    mybatis之动态SQL
    mybatis之一对一关联
  • 原文地址:https://www.cnblogs.com/Dean27/p/6062738.html
Copyright © 2020-2023  润新知