【C#本质论 六】类-从设计的角度去认知(封装)(下)

简介: 【C#本质论 六】类-从设计的角度去认知(封装)(下)

属性作为虚字段

属性还能这么玩儿:属性可以不支持字段,也就是当做虚字段玩儿,也就是根本没往内存中写入Name这个字段值属性被当成方法实现了,其实不太建议这么玩儿。

namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter06.Listing06_22
{
    using System;
    using System.IO;
    public class Program
    {
        public static void Main()
        {
            Employee employee1 = new Employee();
            employee1.Name = "Inigo Montoya";
            System.Console.WriteLine(employee1.Name);
            // ...
        }
    }
    class Employee
    {
        // FirstName property
        public string FirstName
        {
            get
            {
                return _FirstName;
            }
            set
            {
                _FirstName = value;
            }
        }
        private string _FirstName;
        // LastName property
        public string LastName
        {
            get => _LastName;
            set => _LastName = value;
        }
        private string _LastName;
        // ...
        // Name property
        public string Name
        {
            get
            {
                return $"{ FirstName } { LastName }";
            }
            set
            {
                // Split the assigned value into 
                // first and last names
                string[] names;
                names = value.Split(new char[] { ' ' });
                if(names.Length == 2)
                {
                    FirstName = names[0];
                    LastName = names[1];
                }
                else
                {
                    // Throw an exception if the full 
                    // name was not assigned
                    throw new System.ArgumentException(
                        $"Assigned value '{ value }' is invalid",
                        "value");
                }
            }
        }
        public string Initials => $"{ FirstName[0] } { LastName[0] }";
        // Title property
        public string Title { get; set; }
        // Manager property
        public Employee Manager { get; set; }
    }
}

由此引发的思考就是:为啥不能用ref和out来修饰属性呢,你想啊,如果属性被当成虚字段使,内存中根本没地址,怎么传递地址啊,ref是需要初始化的,你这就相当于没有初始化字段

属性的内部构造

事实上,通过观察IL代码,属性就是一种方法实现,我可以理解其为一种语法糖:

构造器和终结器

其实构造函数都比较熟悉了,就聊一点儿需要注意的地方:

  1. 构造函数赋值优先于字段声明赋值,所以构造函数的赋值会覆盖字段声明赋值
  2. C#编译器默认支持无参构造函数,一旦显式添加,即使是手动添加无参的,编译器也不会再提供默认的

构造函数可以重载,一个使用规范是:要优先调用可选参数的方法而不是重载

对象初始化器

对象初始化器,其实就是一种语法糖,需要注意的是,初始化时会按顺序赋值

public static void Main()
        {
            Employee employee = new Employee("Inigo", "Montoya") 
                { Title = "Computer Nerd", Salary = "Not enough" };
        }

同样集合初始化器也一个道理,注意,会按顺序添加哦!

构造函数链

构造函数可以互相调用,避免代码冗余:

public class Employee
    {
        public Employee(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }
        public Employee(
            int id, string firstName, string lastName)
            : this(firstName, lastName)  //实际情况是参数少的调用参数多的
        {
            Id = id;
        }
        public Employee(int id)
        {
            Id = id;
        }
        public int Id { get; private set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Salary { get; set; } = "Not Enough";
        // ...
    }

通常做法是这样的:

public Employee(string firstName, string lastName):this(firstName, lastName,"add")
        {
            FirstName = firstName;
            LastName = lastName;
        }
        public Employee(string firstName, string lastName,string test=null) 
        {
            FirstName = firstName;
            LastName = lastName;
        }

注意:调用方的参数一定要包含被调用方的全部参数!,例如这样调用:

集中初始化和解构函数

集中初始化没什么好说的,就是把所有属性参数什么的在一个函数里一次性初始化,通常是构造函数。而解构函数就是通过Deconstruct方法

public void Deconstruct(
            out int id, out string firstName, 
            out string lastName, out string salary)
        {
           (id, firstName, lastName, salary) = 
                (Id, FirstName, LastName, Salary);
        }

调用的时候:

employee.Deconstruct(out id, out firstName,out lastName, out salary);

C#7.0后支持直接将对象解构为元组,无需方法调用:

(_,firstName,lastName,salary) = employee

静态

静态字段、方法已经在前文说明了,这里简单说一下其特性:

  • 静态字段的作用域为其所属的类。
  • 无论是方法(静态、实例)还是字段(静态、实例),都不能同名,方法重载除外,是说这四种都不能重名!
  • 和普通构造函数一样,静态构造函数赋值优先于静态字段声明!
  • 静态类不包含任何实例成员:包括字段、方法等

有个地方需要注意:最好在声明时进行静态初始化(而不要使用静态构造函数):静态构造函数在首次访问类的任何成员之前执行,无论该成员是静态字段,是其他静态成员,还是实例构造函数。为支持这个设计,编译器添加代码来检查类型的所有静态成员和构造函数,确保首先运行静态构造函数。如果没有静态构造函数,编译器会将所有静态成员初始化为它们的默认值,而且不会添加对静态构造函数的检查。结果是静态字段会在访问前得到初始化,但不一定在调用静态方法或任何实例构造函数之前。有时对静态成员进行初始化的代价比较高,而且访问前确实没必要初始化,所以这个设计能带来一定的性能提升。有鉴于此,请考虑要么以内联方式初始化静态字段(而不要使用静态构造函数),要么在声明时初始化。

局部变量、实例字段和静态字段

局部变量如果使用前没有赋值会报错,静态字段则会使用默认值,实例字段如果在对象生成后也会使用默认值:

静态方法和实例方法

实例方法可以直接调用静态方法,静态方法不能通过this调用实例方法,只能实例化一个对象,再调用实例方法:

public static void fecond()
        {
            Class1 ct = new Class1();
            ct.second();
        }

const和readonly

const就是常量的意思,默认为静态字段,而且只限于字面值类型(int,long,string)。此时再声明static反而会报错。而readly也可以用来声明字段,值得注意的是,只能声明字段,而不能声明局部变量!readonly表明字段值只能从构造函数中修改或者初始化器指定,但其实只读自动属性一用,readonly就没啥用了。

扩展方法和特殊类

包括:扩展方法、嵌套类、分部类及分部方法:其实这一部分,我觉得可以用两个字概述:解耦,做的事情无非就是将耦合度降低。

扩展方法

扩展方法在上文提到过,感觉实则没有任何用处,需要注意的是,即使不在一个程序集的类也可以添加。

public class Program
    {
        public static void Main()
        {
            DirectoryInfo directory = new DirectoryInfo(".\\Source");
            directory.CopyTo(".\\Target",
                SearchOption.TopDirectoryOnly, "*");     
        }
    }
    public static class DirectoryInfoExtension
    {
        public static void CopyTo(
            this DirectoryInfo sourceDirectory, string target,
            SearchOption option, string searchPattern)  //扩展方法的声明,需要this关键字。
        {                   
        }
     }

嵌套类

一般类的 访问修饰符可以定义为默认的internal 或者public,而内嵌类就有比较多的选择,可以是为protected、internal、public以及默认的private。嵌套类可以访问外部类的方法、属性、字段而不管访问修饰符的限制

namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter06.Listing06_46
{
    using System;
    public class Program
    {
        // Define a nested class for processing the command line
        private class CommandLine
        {
            public CommandLine(string[] arguments)
            {
               //
            }
            public string Action;
            public string Id;
            public string FirstName;
            public string LastName;
        }
        public static void Main(string[] args)
        {
            CommandLine commandLine = new CommandLine(args);
        }
    }
}

分部类和分部方法

无非就是把类和方法分别放到不同的文件中去,

namespace AddisonWesley.Michaelis.EssentialCSharp
{
// We dont fully implement our switch block here
#pragma warning disable CS1522
    using System;
    // File: Program.cs
    partial class Program
    {
        static void Main(string[] args)
        {
            CommandLine commandLine = new CommandLine(args);
            switch(commandLine.Action)
            {
                // ...
            }
        }
    }
    // File: Program+CommandLine.cs
    partial class Program
    {
        // Define a nested class for processing the command line
        private class CommandLine
        {
            public CommandLine(string[] args)
            {
                //not implemented
            }
            // ...
            public int Action
            {
                get { throw new NotImplementedException(); }
                set { throw new NotImplementedException(); }
            }
        }
    }
#pragma warning restore CS1522
}

使用关键字partial ,这里就不深入探讨了。

本篇博客从学习到完成历时两天,感觉学到了特别多的干货和东西,最重要的就是从设计的角度去认知为什么要这么设计类,希望能帮助到大家。

相关文章
|
1月前
|
C#
C#学习相关系列之数据类型类的三大特性(二)
C#学习相关系列之数据类型类的三大特性(二)
|
1月前
|
C#
58.c#:directory类
58.c#:directory类
13 0
|
1月前
|
C#
57.c#:directorylnfo类
57.c#:directorylnfo类
13 0
|
1月前
|
监控 C#
55.c#:file类
55.c#:file类
17 1
|
1月前
|
算法 C#
54.c#:random类
54.c#:random类
15 1
|
1月前
|
C#
51.c#:string类的静态方法
51.c#:string类的静态方法
21 1
|
1月前
|
C#
27.c#关键字sealed修饰类
27.c#关键字sealed修饰类
12 0
|
3月前
|
Java C#
C# 面向对象编程解析:优势、类和对象、类成员详解
OOP代表面向对象编程。 过程式编程涉及编写执行数据操作的过程或方法,而面向对象编程涉及创建包含数据和方法的对象。 面向对象编程相对于过程式编程具有几个优势: OOP执行速度更快,更容易执行 OOP为程序提供了清晰的结构 OOP有助于保持C#代码DRY("不要重复自己"),并使代码更易于维护、修改和调试 OOP使得能够创建完全可重用的应用程序,编写更少的代码并减少开发时间 提示:"不要重复自己"(DRY)原则是有关减少代码重复的原则。应该提取出应用程序中常见的代码,并将其放置在单一位置并重复使用,而不是重复编写。
51 0
|
1月前
|
C#
深入C#中的String类
深入C#中的String类
11 0
|
1月前
|
C#
C#学习系列相关之多线程(二)----Thread类介绍
C#学习系列相关之多线程(二)----Thread类介绍