专注于技术经验交流

水至清则无鱼、宁静而致远!

技术、经验、学习共同打造网络新生活!
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C#基础概念二十五问(6-16)[转贴]

Posted on 2007-04-28 10:19  小鱼儿  阅读(287)  评论(0编辑  收藏  举报
6.sealed 修饰符是干什么的?

答:

sealed 修饰符表示密封

用于类时,表示该类不能再被继承,不能和 abstract 同时使用,因为这两个修饰符在含义上互相排斥

用于方法和属性时,表示该方法或属性不能再被继承,必须和 override 关键字一起使用,因为使用 sealed 修饰符的方法或属性肯定是基类中相应的虚成员

通常用于实现第三方类库时不想被客户端继承,或用于没有必要再继承的类以防止滥用继承造成层次结构体系混乱

恰当的利用 sealed 修饰符也可以提高一定的运行效率,因为不用考虑继承类会重写该成员

示例:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Example06
{
    class Program
    {
        class A
        {
            public virtual void F()
            {
                Console.WriteLine("A.F");
            }
            public virtual void G()
            {
                Console.WriteLine("A.G");
            }
        }
        class B : A
        {
            public sealed override void F()
            {
                Console.WriteLine("B.F");
            }
            public override void G()
            {
                Console.WriteLine("B.G");
            }
        }
        class C : B
        {
            public override void G()
            {
                Console.WriteLine("C.G");
            }
        }
        static void Main(string[] args)
        {
            new A().F();
            new A().G();
            new B().F();
            new B().G();
            new C().F();
            new C().G();
 
            Console.ReadLine();
        }
    }
}

结果:
类 B 在继承类 A 时可以重写两个虚函数,如图所示:

由于类 B 中对 F 方法进行了密封, 类 C 在继承类 B 时只能重写一个函数,如图所示:

控制台输出结果,类 C 的方法 F 只能是输出 类B 中对该方法的实现:

A.F
A.G
B.F
B.G
B.F
C.G


7.override 和 overload 的区别?

答:

override 表示重写,用于继承类对基类中虚成员的实现

override 表示重载,用于同一个类中同名方法不同参数(包括类型不同或个数不同)的实现

示例:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Example07
{
    class Program
    {
        class BaseClass
        {
            public virtual void F()
            {
                Console.WriteLine("BaseClass.F");
            }
        }
        class DeriveClass : BaseClass
        {
            public override void F()
            {
                base.F();
                Console.WriteLine("DeriveClass.F");
            }
            public void Add(int Left, int Right)
            {
                Console.WriteLine("Add for Int: {0}", Left + Right);
            }
            public void Add(double Left, double Right)
            {
                Console.WriteLine("Add for int: {0}", Left + Right);
            }
        }
        static void Main(string[] args)
        {
            DeriveClass tmpObj = new DeriveClass();
            tmpObj.F();
            tmpObj.Add(1, 2);
            tmpObj.Add(1.1, 2.2);
 
            Console.ReadLine();
        }
    }
}

结果:
BaseClass.F
DeriveClass.F
Add for Int: 3
Add for int: 3.3


8.什么是索引指示器?

答:

实现索引指示器(indexer)的类可以象数组那样使用其实例后的对象

示例:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Example08
{
    public class Point
    {
        private double x, y;
        public Point(double X, double Y)
        {
            x = X;
            y = Y;
        }
        //重写ToString方法方便输出
        public override string ToString()
        {
            return String.Format("X: {0} , Y: {1}", x, y);
        }
    }
    public class Points
    {
        Point[] points;
        public Points(Point[] Points)
        {
            points = Points;
        }
        public int PointNumber
        {
            get 
            { 
                return points.Length; 
            }
        }    
        //实现索引访问器
        public Point this[int Index]
        {
            get
            {
                return points[Index];
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Point[] tmpPoints = new Point[10];
            for (int i = 0; i < tmpPoints.Length; i++)
            {
                tmpPoints[i] = new Point(i, Math.Sin(i));
            }
 
            Points tmpObj = new Points(tmpPoints);
            for (int i = 0; i < tmpObj.PointNumber; i++)
            {
                Console.WriteLine(tmpObj[i]);
            }
 
            Console.ReadLine();
        }
    }
}

结果:
X: 0 , Y: 0
X: 1 , Y: 0.841470984807897
X: 2 , Y: 0.909297426825682
X: 3 , Y: 0.141120008059867
X: 4 , Y: -0.756802495307928
X: 5 , Y: -0.958924274663138
X: 6 , Y: -0.279415498198926
X: 7 , Y: 0.656986598718789
X: 8 , Y: 0.989358246623382
X: 9 , Y: 0.412118485241757

9.new 修饰符是起什么作用?

答:

new 修饰符与 new 操作符是两个概念

new 修饰符用于声明类或类的成员,表示隐藏了基类中同名的成员。而new 操作符用于实例化一个类型

new 修饰符只能用于继承类,一般用于弥补基类设计的不足

new 修饰符和 override 修饰符不可同时用在一个成员上,因为这两个修饰符在含义上互相排斥

示例:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Example09
{
    class BaseClass
    {
        //基类设计者声明了一个PI的公共变量,方便进行运算
        public static double PI = 3.1415;
    }
    class DervieClass : BaseClass
    {
        //继承类发现该变量的值不能满足运算精度,于是可以通过new修饰符显示隐藏基类中的声明
        public new static double PI = 3.1415926;
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(BaseClass.PI);
            Console.WriteLine(DervieClass.PI);
 
            Console.ReadLine();
        }
    }
}

结果:
3.1415
3.1415926


10.this 关键字的含义?

答:

this 是一个保留字,仅限于构造函数和方法成员中使用

在类的构造函数中出现表示对正在构造的对象本身的引用,在类的方法中出现表示对调用该方法的对象的引用,在结构的构造上函数中出现表示对正在构造的结构的引用,在结构的方法中出现表示对调用该方法的结果的引用

this 保留字不能用于静态成员的实现里,因为这时对象或结构并未实例化

在 C# 系统中,this 实际上是一个常量,所以不能使用 this++ 这样的运算

this 保留字一般用于限定同名的隐藏成员、将对象本身做为参数、声明索引访问器、判断传入参数的对象是否为本身

示例:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Example10
{
    class Class1
    {
        private double c;
        private string value;
 
        public double C
        {
            get
            {
                return c;
            }
        }
        public Class1(double c)
        {
            //限定同名的隐藏成员
            this.c = c;
        }
        public Class1(Class1 value)
        {
            //用对象本身实例化自己没有意义
            if (this != value)
            {
                c = value.C;
            }
        }
        public override string ToString()
        {
            //将对象本身做为参数
            return string.Format("{0} Celsius = {1} Fahrenheit", c, UnitTransClass.C2F(this));
        }
 
        //由于好奇,在这做了一个效率测试,想看看到底哪种方式访问成员变量更快,结论:区别不大。。。
        public string Test1()
        {
            long vTickCount = Environment.TickCount;
            for (int i = 0; i < 10000000; i++)
                this.value = i.ToString();
            return string.Format("Have this.: {0} MSEL", Environment.TickCount - vTickCount);
        }
        public string Test2()
        {
            long vTickCount = Environment.TickCount;
            for (int i = 0; i < 10000000; i++)
                value = i.ToString();
            return string.Format("Don't have this.: {0} MSEL", Environment.TickCount - vTickCount);
        }
    }
    class UnitTransClass
    {
        public static double C2F(Class1 value)
        {
            //摄氏到华氏的转换公式
            return 1.8 * value.C + 32;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Class1 tmpObj = new Class1(37.5);
 
            Console.WriteLine(tmpObj);
 
            Console.WriteLine(tmpObj.Test1());
            Console.WriteLine(tmpObj.Test2());
 
            Console.ReadLine();
        }
    }
}

结果:
37.5 Celsius = 99.5 Fahrenheit
Have this.: 4375 MSEL
Don't have this.: 4406 MSEL

11.可以使用抽象函数重写基类中的虚函数吗?

答:

可以,但需使用 new 修饰符显式声明,表示隐藏了基类中该函数的实现

示例:

    class BaseClass
    {
        public virtual void F()
        {
            Console.WriteLine("BaseClass.F");
        }
    }
    abstract class  DeriveClass : BaseClass
    {
        public new abstract void F();
    }


12.密封类可以有虚函数吗?

答:

可以,基类中的虚函数将隐式的转化为非虚函数,但密封类本身不能再增加新的虚函数

示例:

    class BaseClass
    {
        public virtual void F()
        {
            Console.WriteLine("BaseClass.F");
        }
    }
    sealed class DeriveClass : BaseClass
    {
        //基类中的虚函数F被隐式的转化为非虚函数
 
        //密封类中不能再声明新的虚函数G
        //public virtual void G()
        //{
        //    Console.WriteLine("DeriveClass.G");
        //}
    }


13.如果基类中的虚属性只有一个属性访问器,那么继承类重写该属性后可以有几个属性访问器?如果基类中有 get 和 set 两个呢?

答:

如果基类中的虚属性只有一个属性访问器,那么继承类重写该属性后也应只有一个。如果基类中有 get 和 set 两个属性访问器,那么继承类中可以只有一个也可以同时有两个属性访问器


14.abstract 可以和 virtual 一起使用吗?可以和 override 一起使用吗?

答:

abstract 修饰符不可以和 static、virtual 和 override 修饰符一起使用


15.接口可以包含哪些成员?

答:

接口可以包含属性、方法、索引指示器和事件,但不能包含常量、域、操作符、构造函数和析构函数,而且也不能包含任何静态成员

 

16.类和结构的区别?

答:
类:

类是引用类型在堆上分配,类的实例进行赋值只是复制了引用,都指向同一段实际对象分配的内存

类有构造和析构函数

类可以继承和被继承

结构:

结构是值类型在栈上分配(虽然栈的访问速度比较堆要快,但栈的资源有限放),结构的赋值将分配产生一个新的对象。

结构没有构造函数,但可以添加。结构没有析构函数

结构不可以继承自另一个结构或被继承,但和类一样可以继承自接口

 

示例:

根据以上比较,我们可以得出一些轻量级的对象最好使用结构,但数据量大或有复杂处理逻辑对象最好使用类。

如:Geoemtry(GIS 里的一个概论,在 OGC 标准里有定义) 最好使用类,而 Geometry 中点的成员最好使用结构

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Example16
{
    interface IPoint
    {
        double X
        {
            get;
            set;
        }
        double Y
        {
            get;
            set;
        }
        double Z
        {
            get;
            set;
        }
    }
    //结构也可以从接口继承
    struct Point: IPoint
    {
        private double x, y, z;
        //结构也可以增加构造函数
        public Point(double X, double Y, double Z)
        {
            this.x = X;
            this.y = Y;
            this.z = Z;
        }
        public double X
        {
            get { return x; }
            set { x = value; }
        }
        public double Y
        {
            get { return x; }
            set { x = value; }
        }
        public double Z
        {
            get { return x; }
            set { x = value; }
        }
    }
    //在此简化了点状Geometry的设计,实际产品中还包含Project(坐标变换)等复杂操作
    class PointGeometry
    {
        private Point value;
        
        public PointGeometry(double X, double Y, double Z)
        {
            value = new Point(X, Y, Z);
        }
        public PointGeometry(Point value)
        {
            //结构的赋值将分配新的内存
            this.value = value;
        }
        public double X
        {
            get { return value.X; }
            set { this.value.X = value; }
        }
        public double Y
        {
            get { return value.Y; }
            set { this.value.Y = value; }
        }
        public double Z
       {
            get { return value.Z; }
            set { this.value.Z = value; }
        }
        public static PointGeometry operator +(PointGeometry Left, PointGeometry Rigth)
        {
            return new PointGeometry(Left.X + Rigth.X, Left.Y + Rigth.Y, Left.Z + Rigth.Z);
        }
        public override string ToString()
        {
            return string.Format("X: {0}, Y: {1}, Z: {2}", value.X, value.Y, value.Z);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Point tmpPoint = new Point(1, 2, 3);
 
            PointGeometry tmpPG1 = new PointGeometry(tmpPoint);
            PointGeometry tmpPG2 = new PointGeometry(tmpPoint);
            tmpPG2.X = 4;
            tmpPG2.Y = 5;
            tmpPG2.Z = 6;
 
            //由于结构是值类型,tmpPG1 和 tmpPG2 的坐标并不一样
            Console.WriteLine(tmpPG1);
            Console.WriteLine(tmpPG2);
 
            //由于类是引用类型,对tmpPG1坐标修改后影响到了tmpPG3
            PointGeometry tmpPG3 = tmpPG1;
            tmpPG1.X = 7;
            tmpPG1.Y = 8;
            tmpPG1.Z = 9;
            Console.WriteLine(tmpPG1);
            Console.WriteLine(tmpPG3);
 
            Console.ReadLine();
        }
    }
}

结果:
X: 1, Y: 2, Z: 3
X: 4, Y: 5, Z: 6
X: 7, Y: 8, Z: 9
X: 7, Y: 8, Z: 9

New Document