C# 笔记

悠扬的幻想天空 - 博客

July 12, 2019 技术 • 作者:悠扬

VS 快捷键

注释: 先CTRL+K,然后CTRL+C

取消注释: 先CTRL+K,然后CTRL+U

格式化: 先CTRL+K,然后CTRL+F

转换

Convert.ToInt32(null)会回传 0,並不会产生Exception。而int.Parse(null)则会产生 Exception。

访问修饰符

public 关键字是类型和类型成员的访问修饰符。公共访问是允许的最高访问级别,对访问公共成员没有限制。 意味着在其后声明的所有成员对所有的人都可以取。

private 关键字是一个成员访问修饰符。私有访问是允许的最低访问级别。私有成员只有在声明它们的类和结构体中才是可访问的。 只能在类内部调用,任何实例无法调用private调用。

internal 关键字是类型和类型成员的访问修饰符。只有在同一程序集的文件中,内部类型或成员才是可访问的。仅为同项目(这里的项目是只单独的项目,而不是整个解决方案)调用。

protected 是一个成员访问修饰符。自己及自己的子类可以调用,受保护成员在它的类中可访问并且可由派生类访问。

protected internal 内部保护访问。该类型或成员可由对其进行声明的程序集或另一程序集中的派生类中的任何代码访问。只限于本项目或是子类访问,其他不能访问。

private protected 只有在其声明程序集内,通过相同类中的代码或派生自该类的类型,才能访问类型或成员。

partial

部分类型(partial)定义允许将类、结构或接口的定义拆分成多个文件。

关键字partial是一个上下文关键字,只有和 class、struct、interface 放在一起时才有关键字的含义。因此partial的引入不会影响现有代码中名称为partial的变量。

局部类型的各个部分一般是分开放在几个不同的.cs文件中,但C#编译器允许我们将他们放在同一文件中。

In File1.cs:

namespace PC
{
    partial class A
    {
        int num = 0;
        void MethodA() { }
        partial void MethodC();
    }
}

In File2.cs:

namespace PC
{
    partial class A
    {
        void MethodB() { }
        partial void MethodC() { }
    }
}

引用传参

public void swap(ref int x, ref int y)

输出传参

return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。

继承

当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类

C# 不支持多重继承。但是,您可以使用接口来实现多重继承。

创建子类对象调用子类的构造函数时,会首先调用父类的无参构造函数。

using System;

namespace ConsoleApp1
{
    class Program
    {
        class father
        {
            public father()
            {
                Console.WriteLine("father");
            }
        }
        class son : father
        {
            public son()
            {
                Console.WriteLine("son");
            }
        }
        static void Main(string[] args)
        {
            son s = new son();
            Console.ReadKey();
        }
    }
}

输出:

father
son

多态性

重写

用关键字 virtual 修饰的方法,叫虚方法。可以在子类中用override 声明同名的方法,这叫“重写”。相应的没有用virtual修饰的方法,我们叫它实方法。
重写会改变父类方法的功能。
看下面演示代码:

#region 重写

public class C1
{
    public virtual string GetName()
    {
        return "叔祥";
    }
}

public class C2 : C1
{
    public override string GetName()
    {
        return "xiangshu";
    }
}

 C1 c1 = new C1();
 Console.WriteLine(c1.GetName());//输出“祥叔”

 C2 c2 = new C2();
 Console.WriteLine(c2.GetName());//输出“xiangshu”
 //重点看这里
 C1 c3 = new C2();
 Console.WriteLine(c3.GetName());//输出“xiangshu” 
 
#endregion

覆盖

在子类中用 new 关键字修饰 定义的与父类中同名的方法,叫覆盖。

覆盖不会改变父类方法的功能。

public class C1
{
    public string GetName()
    {
        return "祥叔";
    }
}

public class C2 : C1
{
    public new string GetName()
    {
        return "xiangshu";
    }
}

C1 c1 = new C1();
Console.WriteLine(c1.GetName());//输出“祥叔”

C2 c2 = new C2();
Console.WriteLine(c2.GetName());//输出“xiangshu”

//重点看这里,和上面的重写作比较
C1 c3 = new C2();
Console.WriteLine(c3.GetName());//输出“祥叔” 

基类访问(访问被new隐藏的基类成员)

基类访问表达式由关键字base后面跟一个点和成员的名称组成。例如:

Console.WriteLine("{0}",base.Field1);

上面一行代码中的 base.Fields1 就属于基类访问。

new 和 override 的差异

子类方法什么都不加

只在子类方法加override

virtual可以被子类重写,而abstract必须被子类重写,

如果类成员被abstract修饰,则该类前必须添加abstract,因为只有抽象类才可以有抽象方法。

无法创建abstract类的实例,只能被继承无法实例化。

abstract、virtual、override和new是在类别的继承关係中常用的四个修饰方法的关键字,在此略作总结。

  1. 常用的中文名称:
  • abstract => 抽象方法。
  • virtual => 虚拟方法。
  • override => 覆盖基础类别方法。
  • new => 隐藏基础类别方法。
  • override 和 new 有时都叫覆写基础类别方法。
  1. 适用场合:
  • abstract 和 virtual 用在基础类别(父类别)中
  • override 和 new 用在派(衍)生类别(子类别)中。
  1. 具体概念:
  • abstract 抽象方法,是空的方法,没有方法实体,派(衍)生类必须以 override 实现此方法。
  • virtual 虚拟方法,若希望或预料到基础类别的这个方法在将来的派(衍)生类别中会被覆写(override 或 new),则此方法必须被声明为 virtual。
  • override 覆写继承自基础类别的virtural方法,可以理解为拆掉老房子,在原址上建新房子,老房子再也找不到了(基础类别方法永远调用不到了)。
  • new 隐藏继承自基础类别的virtual方法,老房子还留着,在旁边盖个新房子,想住新房子的住新房子(作为衍生类别对象调用),想住老房子住老房子(作为基础类别对象调用)。
  • 当派(衍)生类别中出现与基础类别同名的方法,而此方法前面未加 override 或 new 修饰符时,编译器会报警告,但不报错,真正执行时等同于加了new。
  1. abstract 和 virtual 的区别:
  • abstract 方法还没实现,连累着基础类别也不能被实例化,除了作为一种规则或符号外没啥用;virtual 则比较好,派(衍)生类别想覆写就覆写,不想覆写就吃老子的。
  • 而且继承再好也是少用为妙,继承层次越少越好,派(衍)生类别新扩展的功能越少越好,virtual 深合此意。
  1. override 和 new 的区别:
  • 当派(衍)生类别对象作为基类类型使用时,override 的执行派(衍)生类别方法,new 的执行基础类别方法。
  • 如果作为派(衍)生类别类型调用,则都是执行 override 或 new 之后的。

委托

委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。类似于 C 和 C++ 中的函数指针,但不同的是,委托是面向对象的、类型安全的和可靠的。

委托用于将方法作为参数传递给其他方法。 事件处理程序就是通过委托调用的方法。 你可以创建一个自定义方法,当发生特定事件时,某个类(如 Windows 控件)就可以调用你的方法。 下面的示例演示了一个委托声明:

public delegate int PerformCalculation(int x, int y);

方法和委托必须具有相同的返回类型。

  • C# 2.0 版引入了匿名方法的概念,可以将代码块作为参数(而不是单独定义的方法)进行传递。 C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。 匿名方法和 Lambda 表达式(在某些上下文中)都可编译为委托类型。 这些功能现在统称为匿名函数。 有关 lambda 表达式的详细信息,请参阅匿名函数

预处理指令

折叠(IDE支持)

利用 #region,可以指定在使用 Visual Studio Code 编辑器的大纲功能时可展开或折叠的代码块。 在较长的代码文件中,能够折叠或隐藏一个或多个区域会十分便利,这样,可将精力集中于当前处理的文件部分。 下面的示例演示如何定义区域:

#region MyClass definition  
public class MyClass   
{  
    static void Main()   
    {  
    }  
}  
#endregion  

特征(Attribute)

using System.ServiceModel;
namespace Artech.WcfServices.Contracts
{
    [ServiceContract(Name="CalculatorService", Namespace="http://www.artech.com/")] 
    //服务协定定义
    public interface ICalculator
    {
           [OperationContract]//要公开的服务方法 
           double Add(double x, double y);

           [OperationContract]
           double Subtract(double x, double y);

        [OperationContract]
        double Multiply(double x, double y);

        [OperationContract]
        double Divide(double x, double y);        
    } 
}

[ServiceContract] 这个特性告诉编译器,该类型(指IInterface1)是一个服务契约,

[OperationContract] 这个特性告诉编译器,该成员(指Function1)是一个操作契约,这样在编程的时候,用反射机制可以判断出,哪些类型标记过服务契约,哪些成员标记过操作契约,在WCF中会找到这些做服务。

特性是一个继承自System.Attribute类的类,其实特性和注释(即“/ ... /”)类似,是用于描述程序集、类型、成员的“备注信息”,和注释不同的是:注释是给“人”看的,而特性是给“编译器”看的,


添加新评论