C# 访问修饰符和声明修饰符

一、访问修饰符的基本说明

1.public: 公有的,是类和类成员的访问修饰符,访问不受限制

2.private: 私有的,是一个成员访问修饰符,不能修饰类,只有在声明它的类和结构内部可以访问

3.internal: 内部的,是类和类成员的访问修饰符,同一个程序集中的所有类都可以访问,可访问性低于public

4.protected: 受保护的,是一个成员访问修饰符,只能在它的类和派生类中访问

5.protected internal: 访问限于当前程序集或派生类

6.private protected: 访问限于当前程序集中该类的派生类或该类内部

privateprotectedprotected internalprivate protected不能修饰类

什么是程序集呢?

简而言之,一个程序集就是一个项目,一个命名空间,一个dll文件

注意点:

如果在成员声明中未指定访问修饰符,则使用默认的访问修饰符

类/枚举/接口/结构体默认访问修饰符是internal,仅限该程序集内部访问

默认的成员可访问性 该成员允许声明的可访问性
class private public
protected
internal
private
protected internal
struct private public
internal
private
interface public
enum public

Example:

namespace TestModifier
{
    public class Class1
    {
        //访问不受限制
        public string namePublic = null;
        //当前程序集
        internal string nameInternal = null;
        //只能在类内部访问
        private string namePrivate = null;
        //只能在该类及其派生类内部使用
        protected string nameProtected = null;
        //当前程序集及派生类
        protected internal string nameProtectedInternal = null;
        //C# 7.2 该类及其派生类内部使用
        private protected string namePrivateProtected = null;
    }

    class Class2 : Class1
    {
        private string name1;
        private string name2;
        private string name3;
        private string name4;
        private string name5;
        public void Method1()
        {
            name1 = new Class1().namePublic;
            name2 = new Class1().nameInternal;
            name3 = new Class1().nameProtectedInternal;
            //派生类
            name4 = base.nameProtected;
            //派生类
            name5 = base.namePrivateProtected;
        }
    }
}

二、声明修饰符

1.partical: 在整个同一程序集中定义分部类和分部方法。在处理大型项目或自动生成的代码(如 Windows 窗体设计器提供的代码)时,在多个文件间拆分类、结构或接口类型可能会非常有用。分部类型可以包含分部方法。

分部类:

  • 处理大型项目时,使一个类分布于多个独立文件中可以让多位程序员同时对该类进行处理

  • 当使用自动生成的源文件时,你可以添加代码而不需要重新创建源文件。 Visual Studio 在创建Windows 窗
    体、Web 服务包装器代码等时会使用这种方法。 你可以创建使用这些类的代码,这样就不需要修改由
    Visual Studio生成的文件

  • 若要拆分类定义,请使用 partial 关键字修饰符

class Program
{
    /// 
    /// 声明修饰符
    /// partical: 分部类和分部方法
    /// 
    /// 
    static void Main(string[] args)
    {
        A a = new A();
        a.GetParticalMethod("xiaomi");
    }

    /// 
    /// 分部方法:
    /// 分部类型各部分中的签名必须匹配
    /// 方法必须返回void
    /// 不允许使用访问修饰符.分部方法是隐式专用的,仅限内部使用
    /// 
    partial class A
    {
        //定义分部方法
        partial void MethodA(string s);

        /// 
        /// 运行分部方法
        /// 
        public void GetParticalMethod(string s)
        {
            MethodA(s);
        }
    }

    partial class A
    {
        partial void MethodA(string s)
        {
            Console.WriteLine("Something happened: {0}", s);
        }
    }
}

2.static: 声明属于类型本身而不是属于特定对象的成员

3.abstract: 指示被修改的实现已丢失或不完整。abstract修饰符可用于方法属性索引事件

  • 抽象类不能实例化,其抽象成员必须由派生自抽象类的非抽象类实现
  • 派生自抽象类的非抽象类,必须包含全部已继承的抽象方法和访问器的实际实现
  • 抽象类中既可以有抽象方法也可以有普通方法,但抽象方法必须放在抽象类中
  • 抽象类能够继承普通类和接口。继承的普通类中有虚方法时,可以重载为普通方法,也可以重载为抽象方法;当继承接口时,可以实现该接口为普通方法或者抽象方法
  • 实现接口的抽象类有可能将接口方法映射到抽象方法上
  • 抽象类可能包含抽象方法和访问器
  • 无法使用 sealead 修饰符来修改抽象类,因为两个修饰符的含义相反。sealead 修饰符阻止类被继承,而 abstract 修饰符要求类被继承
  • 在方法或属性声明中使用 abstract 修饰符,以指示该方法或属性不包含实现
  • 抽象方法是隐式的虚拟方法
  • 只有抽象类中才允许抽象方法声明
  • 由于抽象方法声明不提供实际的实现,因此没有方法主体;方法声明仅以分号结尾,且签名后没有大括号 ({ })
namespace AbstractModifier
{
    class Program
    {
        static void Main(string[] args)
        {
            AbstractClassTest test = new B();
            //调用继承基类的方法
            test.M();
            //调用虚拟方法重载的抽象方法
            test.N();
            //调用接口抽象方法实现
            test.Test();

            Console.Read();
        }
    }

    public class A
    {
        public void M()
        {
            Console.WriteLine("测试");
        }

        public virtual void N()
        {
            Console.WriteLine("方法名为N");
        }
    }

    public interface I
    {
        void Test();
    }
    /// 
    /// 抽象类继承类A,实现接口I
    /// 
    public abstract class AbstractClassTest : A, I
    {
        /// 
        /// 重载的普通方法
        /// 
        //public override void N()
        //{
        //    base.N();
        //}
        /// 
        /// 接口方法的实现的普通方法
        /// 
        //public void Test()
        //{
        //    throw new NotImplementedException();
        //}

        /// 
        /// 重载的抽象方法
        /// 
        public abstract override void N();
        /// 
        /// 接口方法的实现的抽象方法
        /// 
        public abstract void Test();
    }

    public class B : AbstractClassTest
    {
        public override void N()
        {
            Console.WriteLine("抽象重载方法的实现");
        }

        public override void Test()
        {
            Console.WriteLine("接口方法的实现的抽象方法的实现");
        }
    }
}


namespace AbstractModifier
{
    /// 
    /// 抽象类
    /// 
    class Program
    {
        static void Main(string[] args)
        {
            var o = new DerivedClass();
            o.AbstractMethod();
            Console.WriteLine($"x = {o.X}, y = {o.Y}");

            Console.Read();
        }
    }

    public abstract class BaseClass
    {
        protected int _x = 10;
        protected int _y = 10;
        public abstract void AbstractMethod();
        public abstract int X { get; }
        public abstract int Y { get; }
    }

    public class DerivedClass : BaseClass
    {
        public override int X => _x + 10;

        public override int Y => _y + 10;

        public override void AbstractMethod()
        {
            base._x++;
            base._y++;
        }
    }
}

输出结果:

x = 21, y = 21

4、sealed:

  • 密封类禁止继承,普通类中的方法或属性也可用 sealed 修饰,但普通类中的方法或属性必须与 override 关键字结合使用,那么该普通类中的方法或属性在其基类中必须与virtual关键字结合使用

  • 可以对替代基类中的虚方法或属性的方法或属性使用 sealed 修饰符。 这使你可以允许类派生自你的类并防止它们替代特定虚方法或属性。

public class A
{
    protected virtual int m { get; set; }
    protected virtual void Test1()
    {
        Console.WriteLine("这是虚方法1");
    }

    protected virtual void Test2()
    {
        Console.WriteLine("这是虚方法2");
    }
}

public class B : A
{
    protected override sealed int m { get => base.m; set => base.m = value; }
    protected override sealed void Test1()
    {
        base.Test1();
    }

    protected override void Test2()
    {
        base.Test2();
    }
}

public sealed class C : B
{
    /// 
    /// 这里只能重载Test2,其父类的Test1方法重载为密封方法了
    /// 
    protected override void Test2()
    {
        base.Test2();
    }
}

5.virtual: 修改方法、属性、索引器或事件声明,并使它们可以在派生类中被重写

  • 虚拟成员的实现可由派生类中的替代成员更改
  • 调用虚拟方法时,将为替代的成员检查该对象的运行时类型。将调用大部分派生类中的该替代成员,如果没有派生类替代该成员,则它可能是原始成员

6.override: 扩展或修改继承的抽象或虚拟实现的方法、属性、索引器或事件需要 override 修饰符

  • override 方法提供从基类继承的方法的新实现。通过 override 声明重写的方法称为重写基方法
  • override 方法必须具有与重写基方法相同的签名。 从 C# 9.0 开始,override 方法支持协变返回类型。具体而言,override 方法的返回类型可从相应基方法的返回类型派生。在 C# 8.0 和更早版本中,
    override 方法和重写基方法的返回类型必须相同
  • 不能重写非虚方法或静态方法。 重写基方法必须是 virtual 、 abstract 或 override
  • override 声明不能更改 virtual 方法的可访问性。override 方法和 virtual 方法必须具有相同级别访问修饰符。不能使用 new 、 static 或 virtual 修饰符修改 override 方法
  • 重写属性声明必须指定与继承的属性完全相同的访问修饰符、类型和名称。重写属性必须为 virtual 、 abstract 或 override。从 C# 9.0 开始,只读重写属性支持协变返回类型。
class Program
{
    static void Main(string[] args)
    {
        Shape square = new Square(2);
        Console.WriteLine($"Area of Square is {square.GetArea()}");

        Console.Read();
    }
}
    public abstract class Shape
{
    public abstract int GetArea();
}

/// 
/// 正方形
/// 
public class Square : Shape
{
    private int side;
    /// 
    /// 设置正方形边长
    /// 
    /// 正方形边长
    public Square(int n) => side = n;
    /// 
    /// 返回正方形边长
    /// 
    /// 
    public override int GetArea() => side * side;
}

7.new: 做修饰符该关键字可以显式隐藏从基类继承的成员。隐藏继承的成员时,该成员的派生版本将替换基类版本,作运算符,用于创建对象和调用构造函数

虽然可以不使用 new 修饰符来隐藏成员,但将收到编译器警告。如果使用 new 来显式隐藏成员,将禁止此警告

如果用父类新建子类,如: Base dclass= new Derived();dclass.Method()是调用了基类的method()方法,而不是子类的

override 可以覆盖基类的方法,让基类的方法以子类的内容实现;而 new 不用来覆盖基类的方法,而是全新定义一个子类的方法,这个方法只属于子类,与基类的方法无关,只是名字上相同而已

public class BaseC
{
    public int x;
    public void Invoke() { }
}
public class DerivedC : BaseC
{
    public new void Invoke() { }
}

8.extern: 声明在外部实现的方法。extern 修饰符的常见用法是在使用 Interop 服务调入非托管代码时与 DllImport 特性一起使用。在这种情况下,还必须将方法声明为 static

你可能感兴趣的:(C#,c#,后端)