热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

大家帮忙解释一下,虚函数(Virtual),抽象函数(abstract)和接口的区别。

大家好,小弟正在学习C#,现在学的是蒙蒙呼呼的,觉得:虚函数(Virtual),抽象函数(abstract)和接口他们不都是只提供函数名称,然后再类中去实现代码吗?为什么要有这么多呢?请大家帮忙分
大家好,小弟正在学习C#,现在学的是蒙蒙呼呼的,觉得:虚函数(Virtual),抽象函数(abstract)和接口他们不都是只提供函数名称,然后再类中去实现代码吗?为什么要有这么多呢?
请大家帮忙分析它们之间的不同,最好有个例子,或者是能说明白,这三个东西分别在什么时候用。

31 个解决方案

#1


sf自己坐,我在C#怎么也有五个星?没在这儿回答过问题啊。
唉!惭愧啊!

#2


1.虚函数(Virtual),可以写方法的实现,不一定要被继承。抽象函数(abstract)不能写方法的实现,必须被继承

#3


接口与纯抽象函数类似。它只能包含抽象方法,而不能包含任何实现方法,不能创建接口的实例。

#4


虛函數:由virtual聲明,它允許在派生類中被重寫,要重寫方法,必須先聲名為virtual
public class myclass


{
public virtual int myint()
{
函數体;
}
}
class myclass1:myclass
{
public override int myint()
{
函數体1;
}
}

抽象類、抽象函數:由abstract聲明,在抽象類中可以定義抽象方法,抽象方法基本沒有執行代碼,派生類必須重寫它,提供其執行代碼
public abstract class myclass
{
public abstract int myint();
}
public class myclass1:myclass
{
public override int myint()
{
函數体;
}
}

接口類:由interface聲明,是特殊的抽象類,是方法、屬性、事件和索引符的組合,沒有字段,其成員無執行方式,無構造函數,不允許進行運算符重載,接口和它的成員沒有任何訪問修飾符,它總是公共的,不能聲明為虛擬或靜態,繼承自接口的派生類必須實現接口中的所有方法
interface Imyinterface
{
void myfunction();
string name
{
get;
set;
}
}
class myclass:Imyinterface
{
void myfunction()
{
函數体;
}
string name
{
get
{
return name;
}
set
{
name=value;
}
}
}

#5


學習了

#6


http://www.0531s.com/www/9/2008-01/76841.html
看看有沒有能幫到你的

#7


虛函式其實和抽象函式沒有什麽大的區別,只是虛函式可以像普通函式一樣可以在裏面寫程式碼.
編譯器會在編譯的時候適當的調用虛函式的複寫.
還有 要是你寫一個抽象函式,那麽這個抽象函式所在的類就必須是抽象類,抽象類沒有實體.

接口其實就是一個"樣板",那裏面寫著如果按照我這個"樣板"做自己的類,那麽你那個類裏就必須有什麽什麽函式,什麽什麽屬性等等.
而他自己本身只能有定義,有約定,就是不准有函式体.
接口中定義的各個函式根屬性都不能寫訪問修飾符.
接口本身不能不能實例化.

#8


1.virtual:允许被重写,但不强制要求。声明时提供其自身实现;
2.abstract:强制要求其继承者重写。声明时不提供其自身的实现,抽象类不能被实例化;
3.interface:接口就是协议,其声明的成员(属性,方法,事件和索引器)必须由其继承的类实现。接口不能直接被实例化。

虚方法与抽象方法的区别在于,虚方法提供自身的实现,并且强制要求子类重写;而抽象方法不提供自身的实现,并且强制子类重写。

抽象类与接口很相似,但是思路不一样。接口是公开类的成员,而抽象类则是抽象类成员以要求子类继承并实现。

#9


留个脚印,学习一下

#10


虚方法与抽象方法的区别在于,虚方法提供自身的实现,并且强制要求子类重写;而抽象方法不提供自身的实现,并且强制子类重写。
===============
虚方法 好象不要求子类必须重写吧?

#11


学习 了

#12


虚方法 好象不要求子类必须重写吧?
------------------------------
呵呵,笔误。

#13


学习

#14


virtual   关键字用于修改方法或属性的声明,在这种情况下,方法或属性被称作虚拟成员。虚拟成员的实现可由派生类中的重写成员更改。   
  ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/csref/html/vclrfVirtualPG.htm   
    
  abstract   修饰符用于表示所修饰的类是不完整的,并且它只能用作基类。   
  ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/csspec/html/vclrfcsharpspec_10_1_1_1.htm   
    
  interface   
  一个接口定义一个协定。实现接口的类或结构必须遵守其协定。   
  ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/csref/html/vcrefTheInterfaceType.htm   
  ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpguide/html/cpconInterfaces.htm   
    
  抽象类和接口   
  ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/csspec/html/vclrfcsharpspec_13_4_5.htm

#15


翻翻旧帖子,以前有讨论这个问题的

#16


virtual(C# 参考) 

virtual 关键字用于修饰方法、属性、索引器或事件声明,并且允许在派生类中重写这些对象。例如,此方法可被任何继承它的类重写。


public virtual double Area() 
{
    return x * y;
}


虚拟成员的实现可由派生类中的重写成员更改。有关使用 virtual 关键字的更多信息,请参见使用 Override 和 New 关键字控制版本(C# 编程指南)和了解何时使用 Override 和 New 关键字(C# 编程指南)。


调用虚方法时,将为重写成员检查该对象的运行时类型。将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。

默认情况下,方法是非虚拟的。不能重写非虚方法。

virtual 修饰符不能与 static、abstract 和 override 修饰符一起使用。

除了声明和调用语法不同外,虚拟属性的行为与抽象方法一样。

在静态属性上使用 virtual 修饰符是错误的。

通过包括使用 override 修饰符的属性声明,可在派生类中重写虚拟继承属性。



在该示例中,Dimensions 类包含 x 和 y 两个坐标和 Area() 虚方法。不同的形状类,如 Circle、Cylinder 和 Sphere 继承 Dimensions 类,并为每个图形计算表面积。每个派生类都有各自的 Area() 重写实现。根据与此方法关联的对象,通过调用正确的 Area() 实现,该程序为每个图形计算并显示正确的面积。

在前面的示例中,注意继承的类 Circle、Sphere 和 Cylinder 都使用了初始化基类的构造函数,例如: 



public Cylinder(double r, double h): base(r, h) {}



// cs_virtual_keyword.cs
using System;
class TestClass
{
    public class Dimensions
    {
        public const double PI = Math.PI;
        protected double x, y;
        public Dimensions()
        {
        }
        public Dimensions(double x, double y)
        {
            this.x = x;
            this.y = y;
        }

        public virtual double Area()
        {
            return x * y;
        }
    }

    public class Circle : Dimensions
    {
        public Circle(double r) : base(r, 0)
        {
        }

        public override double Area()
        {
            return PI * x * x;
        }
    }

    class Sphere : Dimensions
    {
        public Sphere(double r) : base(r, 0)
        {
        }

        public override double Area()
        {
            return 4 * PI * x * x;
        }
    }

    class Cylinder : Dimensions
    {
        public Cylinder(double r, double h) : base(r, h)
        {
        }

        public override double Area()
        {
            return 2 * PI * x * x + 2 * PI * x * y;
        }
    }

    static void Main()
    {
        double r = 3.0, h = 5.0;
        Dimensions c = new Circle(r);
        Dimensions s = new Sphere(r);
        Dimensions l = new Cylinder(r, h);
        // Display results:
        Console.WriteLine("Area of Circle   = {0:F2}", c.Area());
        Console.WriteLine("Area of Sphere   = {0:F2}", s.Area());
        Console.WriteLine("Area of Cylinder = {0:F2}", l.Area());
    }
}


输出
  
Area of Circle   = 28.27
Area of Sphere   = 113.10
Area of Cylinder = 150.80
 

#17




abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。在类声明中使用 abstract 修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象类派生的类来实现。

在此例中,类 Square 必须提供 Area 的实现,因为它派生自 ShapesClass:




        abstract class ShapesClass
{
    abstract public int Area();
}
class Square : ShapesClass
{
    int x, y;
    // Not providing an Area method results
    // in a compile-time error.
    public override int Area()
    {
        return x * y;
    }
}


有关抽象类的更多信息,请参见抽象类、密封类及类成员(C# 编程指南)。

抽象类具有以下特性:

抽象类不能实例化。

抽象类可以包含抽象方法和抽象访问器。

不能用 sealed(C# 参考)修饰符修改抽象类,这意味着抽象类不能被继承。

从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实实现。

在方法或属性声明中使用 abstract 修饰符以指示方法或属性不包含实现。

抽象方法具有以下特性:

抽象方法是隐式的虚方法。

只允许在抽象类中使用抽象方法声明。

因为抽象方法声明不提供实际的实现,所以没有方法体;方法声明只是以一个分号结束,并且在签名后没有大括号 ({ })。例如: 


public abstract void MyMethod();


实现由一个重写方法提供,此重写方法是非抽象类的成员。

在抽象方法声明中使用 static 或 virtual 修饰符是错误的。 

除了在声明和调用语法上不同外,抽象属性的行为与抽象方法一样。

在静态属性上使用 abstract 修饰符是错误的。

在派生类中,通过包括使用 override 修饰符的属性声明,可以重写抽象的继承属性。 

抽象类必须为所有接口成员提供实现。 

实现接口的抽象类可以将接口方法映射到抽象方法上。例如:


interface I 
{
    void M();
}
abstract class C: I 
{
    public abstract void M();
}


在本例中,DerivedClass 类是从抽象类 BaseClass 派生的。抽象类包含一个抽象方法 AbstractMethod 和两个抽象属性 X 和 Y。



// abstract_keyword.cs
// Abstract Classes
using System;
abstract class BaseClass   // Abstract class
{
    protected int _x = 100;
    protected int _y = 150;
    public abstract void AbstractMethod();   // Abstract method
    public abstract int X    { get; }
    public abstract int Y    { get; }
}

class DerivedClass : BaseClass
{
    public override void AbstractMethod()
    {
        _x++;
        _y++;
    }

    public override int X   // overriding property
    {
        get
        {
            return _x + 10;
        }
    }

    public override int Y   // overriding property
    {
        get
        {
            return _y + 10;
        }
    }

    static void Main()
    {
        DerivedClass o = new DerivedClass();
        o.AbstractMethod();
        Console.WriteLine("x = {0}, y = {1}", o.X, o.Y);
    }
}


输出
x = 111, y = 161

在上面的示例中,如果试图通过使用下面的语句将抽象类实例化:


BaseClass bc = new BaseClass();   // Error


将出现错误,指出编译器无法创建抽象类“BaseClass”的实例。

#18


看看这个帖子:http://topic.csdn.net/u/20080220/16/fedb74cd-95e1-4ab5-a2bb-934c3e9f017c.html

#19


接口只包含方法、委托或事件的签名。方法的实现是在实现接口的类中完成的,如下面的示例所示:


        interface ISampleInterface
{
    void SampleMethod();
}

class ImplementationClass : ISampleInterface
{
    // Explicit interface member implementation: 
    void ISampleInterface.SampleMethod()
    {
        // Method implementation.
    }

    static void Main()
    {
        // Declare an interface instance.
        ISampleInterface obj = new ImplementationClass();

        // Call the member.
        obj.SampleMethod();
    }
}


接口可以是命名空间或类的成员,并且可以包含下列成员的签名: 

方法 

属性 

索引器 

事件 

一个接口可从一个或多个基接口继承。

当基类型列表包含基类和接口时,基类必须是列表中的第一项。

实现接口的类可以显式实现该接口的成员。显式实现的成员不能通过类实例访问,而只能通过接口实例访问,例如:

有关显式接口实现的更多详细信息和代码示例,请参见显式接口实现(C# 编程指南)。


下面的示例演示了接口实现。在此例中,接口 IPoint 包含属性声明,后者负责设置和获取字段的值。Point 类包含属性实现。



// keyword_interface_2.cs
// Interface implementation
using System;
interface IPoint
{
    // Property signatures:
    int x
    {
        get;
        set;
    }

    int y
    {
        get;
        set;
    }
}

class Point : IPoint
{
    // Fields:
    private int _x;
    private int _y;

    // Constructor:
    public Point(int x, int y)
    {
        _x = x;
        _y = y;
    }

    // Property implementation:
    public int x
    {
        get
        {
            return _x;
        }

        set
        {
            _x = value;
        }
    }

    public int y
    {
        get
        {
            return _y;
        }
        set
        {
            _y = value;
        }
    }
}

class MainClass
{
    static void PrintPoint(IPoint p)
    {
        Console.WriteLine("x={0}, y={1}", p.x, p.y);
    }

    static void Main()
    {
        Point p = new Point(2, 3);
        Console.Write("My Point: ");
        PrintPoint(p);
    }
}


输出
  
My Point: x=2, y=3

 

#20


简单来说虚函数(Virtual)已经包含了也必须包含默认的实现,所以在派生类中可以重新实现也可以不实现这些虚函数。
抽象函数(abstract)没有提供默认实现,所以在派生类中 必须实现这些抽象函数。
接口中的函数类似于抽象函数,也不提供默认实现,实现接口的类也 必须实现这些函数。
但接口可用于多继承,即,类只能从一个类继承,但可同时实现多个接口。

#21


抽象方法必须由派生的子类来实现
而虚方法派生的子类不一定会重写

虚方法在基类中可以有实现,但抽象方法不能。
虚方法可以在子类中不实现,但抽象方法必须在子类中全部实现。

虚方法提供了满足基本需要的代码,一般情况下不需要客户端重新写,如果满足不了,客户端可以覆盖。
抽象方法在抽象类中,通常抽象类提供模板方法的实现,模板方法需要一些接口,但是必须由客户端提供,抽象方法就是定义这些接口。

当觉得一个方法要实现什么功能,并且知道怎么实现功能的时候,用虚方法.
当知道方法要实现的功能,但对怎么实现不清楚的时候,用抽象方法

抽象方法是需要子类去实现的
虚方法,是已经实现了,子类可以去覆盖,也可以不覆盖

#22


mark学习

#23


学习了一下!呵呵,介绍好详细的!

#24


嗯, 有道理

#25


这些都是面向对象的特性了。

#26


恩,就是這樣

#27


说的不错

#28


受教了

#29


学习了,记录了!感谢!

#30


very good!

#31


不错。。果然是人多力量大啊

推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了如何将CIM_DateTime解析为.Net DateTime,并分享了解析过程中可能遇到的问题和解决方法。通过使用DateTime.ParseExact方法和适当的格式字符串,可以成功解析CIM_DateTime字符串。同时还提供了关于WMI和字符串格式的相关信息。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
author-avatar
倒转流年1990
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有