C#入门02:最爱红烧肉-类

封装

C#中类的访问级别分为public,protected,private,Internal和protected Internal.Internal的作用是限制访问该类只能在当前程序集中,就是只能在当前dll库或当前exe程序中,意味着其他的程序集不能从该类派生子类来实现。C#和C++的封装区别还在于,C++使用public:来限定后面的语句都是在同一个作用域内,而C#的限定符只对当前语句有效,需要为每个成员和方法都声明作用域,如果不使用显示声明,就使用默认的作用域。
class默认为Internal作用域,成员和方法默认是Private作用域

    //public表示该类可以在任何地方被派生或调用
    //Internal表示该类只能在本程序集中被派生或调用
    //protected表示只能被子类派生或在子类中被调用
    //默认是Internal作用域,该类只能在本程序集中使用
    public class MyClass
    {
        //成员变量默认都是private,只能在本类使用,派生类或外部都不可以访问
        int age = 30;
        string name = "zhangsan";

        //成员方法默认也是private,不能被外部访问,需要显示添加public才能访问
        public void Print()
        {
            Console.WriteLine("name = {0}, age = {1}", name, age);
        }

        //声明static表示静态方法,和C++类似,可以使用类作用域访问
        //因为是public,所以可以在外部使用MyClass.sPrint()访问
        static public void sPrint()
        {
            Console.WriteLine("This is static function!");
        }
    }
   
    //类的创建都使用new方法,这点和C++有很大区别
    MyClass obj = new MyClass();
    obj.Print();
    
    //访问类的静态public方法
    MyClass.sPrint();

继承

多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。与单一继承相对,单一继承指一个类别只可以继承自一个父类。
C# 不支持多重继承。但是,您可以使用接口来实现多重继承。
C#派生类必须实现和基类一致的构造函数。
比如基类有base(int,int)这样的构造函数,那么在派生类中也必须要实现同样的subclass(int,int)构造函数。

    //接口1
    public interface PaintCost
    {
        int getCost(int area);
    }

    //接口2
    public interface CalArea
    {
        int getArea();
    }

    //基类实现
    class Shape
    {
        //基类构造函数
        protected Shape(int w, int h)
        {
            width = w;
            height = h;
        }

        //基类方法1
        public int GetWidth()
        {
            return width;
        }

        //基类方法2
        public int GetHeight()
        {
            return height;
        }

        //基类成员,声明为protected可以被子类使用
        protected int width;
        protected int height;
    }

    // 派生类
    class Rectangle : Shape, PaintCost, CalArea
    {
        //子类构造函数,调用了基类的构造函数
        //必须实现和基类一致的构造函数,比如基类有base(int,int)这样的构造函数
        //那么在派生类中也必须要实现同样的subclass(int,int)构造函数
        public Rectangle(int w, int h) : base(w, h)
        {
        }

        //实现接口1的方法
        public int getCost(int area)
        {
            return area * (width + height) ;
        }

        //实现接口2的方法
        public int getArea()
        {
            return (width * height);
        }
    }

    //创建派生类的对象
    Rectangle rect = new Rectangle(20, 30);

    //打印方法输出
    Console.WriteLine("Cost={0}, Area={1}", rect.getCost(100), rect.getArea());

    //分别获取基类引用,接口1和接口2的引用
    //这里类似C++中的子类到基类的类型转换:
    //C++: base* b = subclass;
    PaintCost cost = rect;
    CalArea cal = rect;
    Shape sh = rect;

    //从接口1打印
    Console.WriteLine("PaintCost.Cost={0}", cost.getCost(100));

    //从接口2打印
    Console.WriteLine("CalArea.Area={0}", cal.getArea());

    //从基类打印成员
    Console.WriteLine("Shape.width={0}, Shape.height={1}", sh.GetWidth(), sh.GetHeight());

    //也可以使用as关键字来进行类型转换
    Shape sh2 = rect as Shape;

    //基类到派生类的转换:
    //C++:subclass* sub = dynamic_cast<base>(base);
    //if (sub != nullptr) 需要做一下判断是否转换成功
    Rectangle rect2 = sh as Rectangle;
    if (rect2 != null)
    {
        //需要做一下判断,转换失败rect2为空,但不会抛出异常
    }

多态

多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。

静态多态性
C# 提供了两种技术来实现静态多态性。分别为:函数重载和运算符重载。
C#的函数重载和C++类似,运算符重载将在下一节专门说明。

动态多态性
C#动态多态性是通过抽象类和虚方法实现的。
抽象类使用关键字abstract来声明。
虚方法使用关键字virtual来声明。
派生类中使用override来重载基类的抽象方法和虚方法。
1、抽象类
您不能创建一个抽象类的实例。 您不能在一个抽象类外部声明一个抽象方法。 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。

    //抽象类abstract
    abstract class Shape
    {
        //抽象方法abstract
        public abstract int area();
    }

    class Rectangle : Shape
    {
        //实现抽象方法override
        public override int area()
        {
            throw new NotImplementedException();
        }
    }

2、虚方法
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。虚方法是使用关键字 virtual 声明的。虚方法可以在不同的继承类中有不同的实现。对虚方法的调用是在运行时发生的。

    class Shape
    {
        //基类虚方法,有自己的实现
        public virtual int area()
        {
            Console.WriteLine("父类的面积:");
            return 0;
        }
    }

    class Rectangle : Shape
    {
        //派生类重载虚方法,实现自己的订制化功能
        public override int area()
        {
            Console.WriteLine("Rectangle 类的面积:");
            return (width * height);
        }
    }


运算符重载

    class Box
    {
        // 重载 + 运算符来把两个 Box 对象相加
        public static Box operator +(Box b, Box c)
        {
            Box box = new Box();
            box.length = b.length + c.length;
            box.breadth = b.breadth + c.breadth;
            box.height = b.height + c.height;
            return box;
        }
    }

    Box Box1 = new Box();         // 声明 Box1,类型为 Box
    Box Box2 = new Box();         // 声明 Box2,类型为 Box
    Box Box3 = new Box();         // 声明 Box3,类型为 Box

    // ...

    // 把两个对象相加
    Box3 = Box1 + Box2;

    //特别说明:
    //这里Box3可以不用new Box()操作
    //如果有new操作,那么上面的操作其实是赋值操作,将Box1+Box2产生的Box对象拷贝给Box3
    //如果没有new操作,那么Box3实际上是Box1+Box3产生的Box对象的引用类型
    //前者是新的对象产生,会花费更多时间和空间,后者更快,但是共享内存空间,如果该内存空间不会
    //被其他地方使用,那么没有任何问题,如果有被其他使用,就可能引发其他问题。

    //常见的运算符重载
    public static Box operator +(Box b, Box c);
    public static bool operator ==(Box lhs, Box rhs);
    public static bool operator !=(Box lhs, Box rhs);
    public static bool operator <(Box lhs, Box rhs);
    public static bool operator >(Box lhs, Box rhs);
    public static bool operator <=(Box lhs, Box rhs);
    public static bool operator >=(Box lhs, Box rhs);
    public override string ToString();

接口

抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的。
**C#仅支持单一继承,但可以实现多个接口。 **
接口也可以被继承,实现类必须实现继承链上的所有接口方法

    //接口1
    interface IParentInterface
    {
        void ParentInterfaceMethod();
    }

    //接口2继承接口1
    interface IMyInterface : IParentInterface
    {
        void MethodToImplement();
    }

    //实现类继承接口2,因此必须实现接口1和接口2的所有方法
    class InterfaceImplementer : IMyInterface
    {
        //实现接口1的方法
        public void ParentInterfaceMethod()
        {
            Console.WriteLine("ParentInterfaceMethod() called.");
        }

        //实现接口2的方法
        public void MethodToImplement()
        {
            Console.WriteLine("MethodToImplement() called.");
        }
    }

预处理

和C++一样,C#也可以定义一系列预处理指令。可以在项目属性中设置预处理指令: 输入图片说明
勾选DEBUG和TRACE常量会定义这两个预定义指令,在条件编译符号中还可以自行定义需要的预处理指令,用空格分开。
另外还可以在代码文件的using指令前定义预处理指令:

#define GPU
using System;
...
#if GPU
            Console.WriteLine("Use GPU");
#else 
#warning Only Support GPU !
#endif

这里使用#warning在编译时产生一个编译警告(还可以#error产生编译错误)。
另外比较常用的就是代码段管理预处理指令,在IDE中可以折叠被#region包围的代码:

    #region 私有成员
    private int _width = 0;
    private int _height = 0;
    #endregion

    #region 公有方法
    public void Submit()
    {

    }
    public static void Add(MyClass A, MyClass B)
    {

    }
    #endregion

这样一来就可以折叠这段代码,如果没有显示+号,就需要在菜单->编辑->大纲显示,开启代码的大纲显示功能。

异常处理

C#提供了很丰富的异常处理机制,这点和C++很相似,连关键字try,catch,finally都是一模一样。总体上分为两类异常:SystemException和ApplicationException。
下面的代码展示自定义异常类的最常见用法:

    public class MyClass
    {
        public int Work(int a, int b)
        {
            if (b == 0)
            {
                throw new MyException("除数不能为零!");
            }
            return a / b;
        }
            
    }

    //自定义异常处理类,从ApplicationException派生
    public class MyException : ApplicationException
    {
        public MyException(string messag) : base(messag)
        {

        }
    }

    try
    {
        MyClass my = new MyClass();
        int value = my.Work(100, 0);
        Console.WriteLine("value is {0}", value);
    }
    catch (MyException e)
    {
        //捕获自定义异常
        Console.WriteLine("MyException : {0}", e.Message);
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception : {0}", e.Message);
        //如果不想处理,仍然可以继续抛出异常
        throw e;
    }
    finally
    {
        //无论是否抛出异常都会执行这段代码
        Console.WriteLine("程序退出");
    }

文件输入和输出

1、通用文件流FileStream操作,即可写文件也可读取文件

    //打开文件流
    FileStream F = new FileStream("test.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite);

    //写文件数据
    for (int i = 1; i <= 20; i++)
    {
        F.WriteByte((byte)i);
    }

    //读取文件流
    F.Position = 0;
    for (int i = 0; i <= 20; i++)
    {
        Console.Write(F.ReadByte() + " ");
    }

    //关闭文件
    F.Close();

2、简单文本文件的写入StreamWriter和读取StreamReader

    //写文本文件
    string[] names = new string[] { "Zara Ali", "Nuha Ali" };

    //使用using也可以关闭文件
    using (StreamWriter sw = new StreamWriter("names.txt"))
    {
        foreach (string s in names)
        {
            sw.WriteLine(s);
        }
    }

    //读取文本文件
    string line = "";
    using (StreamReader sr = new StreamReader("names.txt"))
    {
        while ((line = sr.ReadLine()) != null)
        {
            Console.WriteLine(line);
        }
    }

3、二进制文件的读BinaryReader和写BinaryWriter

    try
    {
        //从控制台输入数据并写入二进制文件
        BinaryWriter bw = new BinaryWriter(new FileStream("mydata", FileMode.Create));
        Int32 vInt = Convert.ToInt32(Console.ReadLine());
        Double vDoub = Convert.ToDouble(Console.ReadLine());
        String vStr = Console.ReadLine();
        bw.Write(vInt);
        bw.Write(vDoub);
        bw.Write(vStr);
        bw.Close();
    }
    catch (IOException e)
    {
        Console.WriteLine(e.Message);
        return;
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
        return;
    }

    try
    {
        // 读取二进制文件
        BinaryReader br = new BinaryReader(new FileStream("mydata", FileMode.Open));
        Console.WriteLine("Read:\n{0}\n{1}\n{2}", br.ReadInt32(), br.ReadDouble(), br.ReadString());
        br.Close();
    }
    catch (IOException e)
    {
        Console.WriteLine(e.Message);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

4、Windows 文件系统的操作
DirectoryInfo 类
DirectoryInfo 类派生自 FileSystemInfo 类。它提供了各种用于创建、移动、浏览目录和子目录的方法。该类不能被继承。
**FileInfo 类 **
FileInfo 类派生自 FileSystemInfo 类。它提供了用于创建、复制、删除、移动、打开文件的属性和方法,且有助于 FileStream 对象的创建。该类不能被继承。
遍历指定目录下的所有文件并打印文件名称和文件大小:

    //创建一个DirectoryInfo对象
    DirectoryInfo mydir = new DirectoryInfo(@"c:\Windows");

    //获取目录中的文件以及它们的名称和大小
    FileInfo[] f = mydir.GetFiles();
    foreach (FileInfo file in f)
    {
        Console.WriteLine("File Name: {0} Size: {1}", file.Name, file.Length);
    }

猜你喜欢

转载自my.oschina.net/u/3489228/blog/1790333