.net(2)
11.interface是否可继承interface?abstract class 是否可实现interface
回答1:接口可以继承接口,而且可以继承多个接口,用“,”进行分割,接口不允许直接或间接地从自身继承。和类的继承相似,接口的继承也形成接口之间的层次结构
回答2:抽象类是可以实现接口的,抽象类里可以有抽象方法,也可以有具体的实现方法,也就是说继承了接口后也是可以实现接口里定义的方法
参考文章:
12.private、protected、public、internal
修饰访问符这个算是挺基础的了,不过也不能忘记哦
- public:同一程序集中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或成员。
- private:只有同一类或结构中的代码可以访问该类型或成员。
- protected:只有同一类或结构或者此类的派生类中的代码才可以访问的类型或成员。
- internal:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。
- protected internal:由其声明的程序集或另一个程序集派生的类中任何代码都可访问的类型或成员。 从另一个程序集进行访问必须在类声明中发生,该类声明派生自其中声明受保护的内部元素的类,并且必须通过派生的类类型的实例发生。
参考文章:
1.访问修饰符(C# 编程指南)
13.asp.net中 static的存活周期
全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 当类首次被加载时static定义的变量被分配空间,程序结束后由系统释放.
如果在一个程序里过多的使用static定义的成员,确实很占内存,因为他的生命周期是整个程序,程序运行时无法被gc所回收,直到程序结束,释放内存.
参考文章:
14.try{}里有一个return语句,那么紧跟 在这个try后的finally{}里的代码会不会被执行,是在return前还是后
这题我tm面试的几间都有啊,公司得有多懒
答案:会执行,在return后执行
15.new的作用,where的作用
- new 运算符:用于创建对象和调用构造函数。
- new 修饰符:用于向基类成员隐藏继承成员。
- new 约束:用于在泛型声明中约束可能用作类型参数的参数的类型。
参考文章:
- new(C# 参考)
-
16.this的作用
this 关键字引用类的当前实例,还可用作扩展方法的第一个参数的修饰符。
用途:
- 限定被相似的名称隐藏的成员,例如
public Employee(string name, string alias)
{
// Use this to qualify the fields, name and alias:
this.name = name;
this.alias = alias;
}
- 将对象作为参数传递到其他方法,例如
CalcTax(this);
- 声明索引器,例如:
public int this[int param]
{
get { return array[param]; }
set { array[param] = value; }
}
参考文章:
this(C# 参考)
17.MVC的各个部分用什么技术实现,和三层架构的关系
这个要详细讲的话得要一本书吧,我就说大概。ASP.NET MVC包含了三部分,Model,View和Controller。Controller负责后台逻辑代码,View是纯净的HTML页面,Model是中间数据层。
1.概述
- 当我们讨论系统时,它一般包含用户输入逻辑、业务处理逻辑以及UI显示逻辑等,MVC是一种架构模式,它能够让我们开发出各个模块之间松耦合的应用程序。MVC最主要的目的是“关注点分离”,它能够分离开UI显示、业务逻辑以及用户输入等。根据MVC的规定,一个系统应该被划分为Model、View以及Controller三个部分
- Model:它被当做一个可以处理业务规则、逻辑以及数据的智能体,同时它独立于MVC中其它两个部分(C和V)(不访问Controller和View,译者注)
- Controller:它负责接收用户请求、分配请求给处理者,它可以访问Model和View。
- View:它被当做一个哑巴,专门呈现最终的数据给用户。它可以是一个excel表单,也可以是一个包含许多记录的web页面甚至是一个纯文本。它可以访问Model。
2.MVC与三层架构的区别
①.各自原理
前提当然是要搞懂这两者的联系了
三层架构是最基本的项目分层结果,而MVC则是三层架构的一个变体,MVC是一种好的开发模式。
首先你要明白MVC分别代表的是什么意思.
- M 即Model(模型层),主要负责出来业务逻辑以及数据库的交互
- V 即View(视图层),主要用于显示数据和提交数据
- C 即Controller(控制器),主要是用作捕获请求并控制请求转发
三层:UI 界面层 BLL 业务逻辑层,DAL数据访问层,Model 实体层
MVC中的的M 不是三层中的Model(实体层),他其实包括三层中的 BLL,DAL,Model,这是非常要注意的,这也是他们之间的区别的关键所在
三层是基于业务逻辑来分的,而mvc是基于页面来分的
MVC是 Model-View-Controller,严格说这三个加起来以后才是三层架构中的WEB层,也就是说,MVC把三层架构中的WEB层再度进行了分化,分成了控制器、视图、实体三个部分,控制器完成页面逻辑,通过实体来与界面层完成通话;而C层直接与三层中的BLL进行对话
②.区别之处
- 在三层中也提到了Model,但是三层架构中Model的概念与MVC中Model的概念是不一样的,“三层”中典型的Model层是已实体类构成的,而MVC里,则是由业务逻辑与访问数据组成的。
- 首先,MVC和三层架构,是不一样的。
三层架构中,DAL(数据访问层)、BLL(业务逻辑层)、WEB层各司其职,意在职责分离。
MVC是 Model-View-Controller,严格说这三个加起来以后才是三层架构中的WEB层,也就是说,MVC把三层架构中的WEB层再度进行了分化,分成了控制器、视图、实体三个部分,控制器完成页面逻辑,通过实体来与界面层完成通话;而C层直接与三层中的BLL进行对话。所以, .net的三层结构中,并没有action这个概念。 - MVC模式是一种复合设计模式,一种解决方案;三层是种软件架构,通过接口实现编程
- 三层模式是体系结构模式,MVC是设计模式
- 三层模式又可归于部署模式,MVC可归于表示模式
参考文章:
18.MVC框架中可以实现AOP效果的接口是?
关于AOP,它面向的是一个切面,能够把公共的功能抽出来,独立开发,然后将公共部分实现,在开发人员不知情的情况下,添加进去。而在MVC前台框架中,MVC中Controller中的Filter可以将公共的代码抽离出来。
实现AOP效果的接口(只列举常用)
Asp.Net MVC提供了以下几种默认的Filter:
Filter Type | 实现接口 | 执行时间 | Default Implementation |
---|---|---|---|
Authorization filter | IAuthorizationFilter | 在所有Filter和Action执行之前执行 | AuthorizeAttribute |
Action filter | IActionFilter | 分别在Action执行之前和之后执行。 | ActionFilterAttribute |
Result filter | IResultFilter | 分别在Action Result执行之后和之前 | ResultFilterAttribute |
Exception filter | IExceptionFilter | 只有在filter,或者 action method, 或者 action result 抛出一个异常时候执行 | HandleErrorAttribute |
参考文章:
19.Entity Framework是什么
1.Entity Framework简述
微软官方提供的ORM工具,ORM让开发人员节省数据库访问的代码时间,将更多的时间放到业务逻辑层代码上。EF提供变更跟踪、唯一性约束、惰性加载、查询事物等。开发人员使用Linq语言,对数据库操作如同操作Object对象一样省事。
EF有三种使用场景,
- 从数据库生成Class
- 由实体类生成数据库表结构,
- 通过数据库可视化设计器设计数据库,同时生成实体类。
ORM 是什么?
ORM 是将数据存储从域对象自动映射到关系型数据库的工具。ORM主要包括3个部分:域对象、关系数据库对象、映射关系。ORM使类提供自动化CRUD,使开发人员从数据库API和SQL中解放出来。
EntityFramework 架构
- EDM (实体数据模型):EDM包括三个模型,概念模型、 映射和存储模型。
- 概念模型 ︰ 概念模型包含模型类和它们之间的关系。独立于数据库表的设计。
- 存储模型 ︰ 存储模型是数据库设计模型,包括表、 视图、 存储的过程和他们的关系和键。
- 映射 ︰ 映射包含有关如何将概念模型映射到存储模型的信息。
- LINQ to Entities ︰ LINQ to Entities 是一种用于编写针对对象模型的查询的查询语言。它返回在概念模型中定义的实体。
- Entity SQL: Entity SQL 是另一种炉类似于L2E的言语,但相给L2E要复杂的多,所以开发人员不得不单独学习它。
- Object Services(对象服务):是数据库的访问入口,负责数据具体化,从客户端实体数据到数据库记录以及从数据库记录和实体数据的转换。
- Entity Client Data Provider:主要职责是将L2E或Entity Sql转换成数据库可以识别的Sql查询语句,它使用Ado.net通信向数据库发送数据可获取数据。
- ADO.Net Data Provider:使用标准的Ado.net与数据库通信
DBContext
DbContext是EntityFramework很重要的部分,连接域模型与数据库的桥梁,是与数据库通信的主要类。
DbContext主要负责以下活动:
- EntitySet::DbContext包含了所有映射到表的entities
- Querying:将Linq-To-Entities转译为Sql并发送到数据库
- Change Tracking:从数据库获取entities后保留并跟踪实体数据变化
- Persisting Data:根据entity状态执行Insert、update、delete命令
- Caching:DbContext的默认第一级缓存,在上下文中的生命周期中存储entity
- Manage Relationship:DbContext在DbFirst模式中使用CSDL、MSL、SSDL管理对象关系,Code first中使用fluent api 管理关系
- Object Materialization:DbContext将物理表转成entity实例对象
参考文章:
20.entityframework的开发模式
简述
有三种方式:Code First、DBFirst、Model First
- CodeFirst 领域设计时先定义实体类,用实体类生成数据库
- DbFirst 从数据库生成实体类
- Model First 使用Visual Studio实体设计器,设计ER,同时生成Entity类和DB
查询方式
- LINQ to Entities:
//Querying with LINQ to Entities
using (var context = newSchoolDBEntities())
{
var L2EQuery = context.Students.where(s => s.StudentName == "Bill");
var student = L2EQuery.FirstOrDefault<Student>();
}
LINQ Query syntax:
using (var context = new SchoolDBEntities())
{
var L2EQuery = from st in context.Students
where st.StudentName == "Bill"select st;
var student = L2EQuery.FirstOrDefault<Student>();
}
- Entity SQL
//Querying with Object Services and Entity SQL
string sqlString = "SELECT VALUE st FROM SchoolDBEntities.Students " +
"AS st WHERE st.StudentName == 'Bill'";
var objctx = (ctx as IObjectContextAdapter).ObjectContext;
ObjectQuery<Student> student = objctx.CreateQuery<Student>(sqlString);
Student newStudent = student.First<Student>();
//使用EntityDataReader
using (var con = newEntityConnection("name=SchoolDBEntities"))
{
con.Open();
EntityCommand cmd = con.CreateCommand();
cmd.CommandText = "SELECT VALUE st FROM SchoolDBEntities.Students as st where st.StudentName='Bill'";
Dictionary<int, string> dict = newDictionary<int, string>();
using (EntityDataReader rdr = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection))
{
while (rdr.Read())
{
int a = rdr.GetInt32(0);
var b = rdr.GetString(1);
dict.Add(a, b);
}
}
}
- Native SQL
using (var ctx = newSchoolDBEntities())
{
var studentName = ctx.Students.SqlQuery("Select studentid, studentname, standardId from Student where studentname='Bill'").FirstOrDefault<Student>();
}
1.请从中选择引用类型或值类型
说起题,我们不由得会想到几个名词:栈和堆,值类型和引用类型,装箱和拆箱。
这些概念可是C#的入门基础呀,咱们还是要搞懂的,详细可以查看参考文章。
这里要提下一个知识点:C#新手都有一个误区:值类型的值都是保存在栈里,实际应该是:值类型的值是在它声明的位置存储的。即局部变量(参数)的值会在栈里,而当值类型作为引用类型的类型成员的话,会跟随对象,即存储在托管堆里。
顺便提供几张经典图片便于读者理解:
最后在这些概念里,我觉得比较重要的精华提要:
- 对于栈和堆的概念,我们声明值类型和引用类型的时候,为什么会分配两种内存而不是一种呢?只要我们仔细想一下,基本类型他们并不复杂,很多时候只包含简单的值,比如 i=1,而对象类型则相对复杂,它们则会引用其他对象或基本类型。简单来说,对象类型需要动态内存而基本类型则需要静态内存。若需要分配动态内存,那么就分配到堆上;反之在栈上。
- 对于值类型和引用类型的概念,值类型,它们的内存值都分配在栈上,当我们把一个int值分配给另外一个int值时,需要创建一个完全不同的拷贝。换句话说,你可以改变其中任何一个而不会影响另外一个。这种数据类型被称为值类型;引用类型,当我们创建一个对象,并把一个对象赋给另外一个对象时,它们的指针指向相同的内存(如下图,当我们把obj赋给obj1时,它们指向相同的内存)。换句话说,我们改变其中一个,会影响到另外一个。
参考文章:
2.判断一个string变量是否为空
最普通的用法当然就是:
- s.Length == 0
- s == string.Empty
- s == ""
效率上:s.Length == 0 > s == string.Empty > s == ""
不过 net 2.0后,可用String.IsNullOrEmpty(s) 来进行判断
参考文章:
String.IsNullOrWhiteSpace和String.IsNullOrEmpty的区别
3.Lock的作用
关于lock的介绍就到这里,有下面几点需要注意的地方
- lock的是引用类型的对象,string类型除外。
- lock推荐的做法是使用静态的、只读的、私有的对象。
- 保证lock的对象在外部无法修改才有意义,如果lock的对象在外部改变了,对其他线程就会畅通无阻,失去了lock的意义。
还有摘自本篇评论的精彩回复:
this 表示的就是当前实例,当你再new一个的时候,锁定的就不再是同一个对象了。 不能锁定值类型的原因是,当这个值类型传递到另一个线程的时候,会创建一个副本,锁定的也不再是同一个对象了。 锁定字符串带来的问题是,字符串在CLR中会暂存在 内存中,如果有两个变量被分配了相同的字符串内容,那么这两个引用会指向同一块内存,实际锁定也就是同一个对象,这就会导致整个应用程序的阻塞。所以锁定字符串是非常危险的行为。
参考文章:
1.[C#基础]说说lock到底锁谁?](http://www.cnblogs.com/wolf-sun/p/4209521.html)
4.abstract class 和 interface 有什么区别
这篇已经有提过了:请参阅:最近找工作面的面试题目汇总(一)
5.关键字params是什么
参考文章:
为了将方法声明为可以接受可变数量参数的方法,我们可以使用params关键字来声明数组
要求:
- 在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。
- 该参数必须标识一个一维数组,但类型不限,对该参数传递null或者0个数目的数组的引用都是合法的
参考文章:
6.怎么获取用户请求的Url地址
- 获取 完整url:
string url=Request.Url.ToString();
参考文章:
写出三元运算符实例
这个我就不说了。 ?:
7.说明Stack和Queue的区别
1.Queue的定义
Queue是先进先出(first in first-out)数据结构,表示放进queue的第一个数据,会是第一个拿出来使用
示例:
//建立Queue的对象
var myqueue = new Queue<string>();
//新增数据进入queue
myqueue.Enqueue("第1个");
myqueue.Enqueue("第2个");
myqueue.Enqueue("第3个");
//查看queue的所有资料
foreach (string queue in myqueue)
{
Console.WriteLine(queue);
}
//使用Peek()方法查看queue里的第一条数据
Console.WriteLine("");
Console.WriteLine("使用peek方法的輸出值:" + myqueue.Peek());
//使用Dequeue()方法从queue中拿出值
//记得是先进先出的获取方法
Console.WriteLine("第一个被我拿走了:" + myqueue.Dequeue());
//查看剩下的值
Console.WriteLine("查看myqueue剩下的值");
foreach (string queue in myqueue)
{
Console.WriteLine(queue.ToString());
}
//查看某位置的值
Console.WriteLine("查看特定位置的值");
Console.WriteLine(myqueue.ElementAt(1));
运行结果
第1个
第2个
第3个
使用peek方法的輸出值:第1个
第一个被我拿走了:第1个
查看myqueue剩下的值
第2个
第3个
查看特定位置的值
第3个
第3个
第2个
第1个
Peek看到的数据:第3个
被拿走了:第3个
查看剩下的数据
第2个
第1个
2.Stack的定义
Stack为后进先出(first-in last-out)的数据结构,表示第一个进去的数据,反而是最后一个出来。
示例代码
//建立Stack对象
var mystack = new Stack<string>();
//插入数据
mystack.Push("第1个");
mystack.Push("第2个");
mystack.Push("第3个");
//查看数据
foreach(string stack in mystack)
{
Console.WriteLine(stack.ToString());
}
//Stack与Queue一样有Peek方法查看第一条数据
Console.WriteLine("");
Console.WriteLine("Peek看到的数据:"+mystack.Peek());
//使用Pop方法取出数据
Console.WriteLine("被拿走了:"+mystack.Pop());
Console.WriteLine("查看剩下的数据");
foreach (string stack in mystack)
{
Console.WriteLine(stack.ToString());
}
运行结果
第3个
第2个
第1个
Peek看到的数据:第3个
被拿走了:第3个
查看剩下的数据
第2个
第1个
参考文章:
8.Overload和Override的区别
1.Overload的定义
重载应该叫overload,重载某个方法是在同一个类或父子关系类中发生的!重载(overload)是提供了一种机制, 相同函数名通过不同的返回值类型以及参数来表来区分的机制
overload: 同一类中或父子关系类中皆可.
public string ToString(){return "a";}
public string ToString(int id){return id.ToString();}
2.Override的定义
重写叫override,重写是在子类中重写父类中的方法。重写(override)是用于重写基类的虚方法,这样在派生类中提供一个新的方法
override: 父类:public virtual string ToString(){return "a";}
子类:public override string ToString(){return "b";}
3.两者间的区别
很本质的区别就是看函数特征:覆写(Override)的两个函数的函数特征相同,重载(Overload)的两个函数的函数名虽然相同,但函数特征不同。
- override 是在继承的时候,如果你写的函数与要继承的函数函数特征相同,那么,加上这个关键字,在使用这个子类的这个函数的时候就看不见父类(或超类)的函数了,它被覆盖掉了。
比如:Derived继承了Base,Base里面有void A(int a) ,那么如果你Derived里面觉得A写得不好或不适合这个类,你想重新再写一遍A里的代码,那么就写override void A(int a)这样,原来的那个函数就被你新写的这个覆盖掉了。
overload 是重载,就是说函数名相同,函数特征不同,系统会根据你提供的参数来调相应的函数。比如:void A(int a)和void A(int a,int b) ,如果你用的是A(1)那么调的是第一个,如果是A(1,1)那么调的是第二个。
参考文章:
## 9. using的意义
简述:
提供能确保正确使用 IDisposable 对象的方便语法。它的用途就是清理非托管资源,不受GC控制的资源。Using结束后会隐式的调用Disposable方法
- 有许多其他类别的非托管资源和封装这些资源的类库类型,所有这些类型都必须实现IDisposable接口,而using语句确保即使在调用对象上的方法时发生异常Dispose方法也会被调用。
- 实际上,将对象放入 try 块中并在finally块中调用Dispose一样可以达到同样的效果,而这功能就是编译器转换using语句的方式。
以下两段代码的作用是一致的
using (Font font1 = new Font("Arial", 10.0f))
{
byte charset = font1.GdiCharSet;
}
{
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}
参考文章:
- using 语句(C# 参考)
-
10.List是什么
因为ArrayList存在不安全类型与装箱拆箱的缺点,所以出现了泛型的概念。List类是ArrayList类的泛型等效类,它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。
优点
- 类型安全
- 性能增强
- 代码服用
1.抽象类的介绍,抽象类里的虚函数和抽象函数
参考文档
- 抽象类、密封类及类成员(C# 编程指南)
-
简介
不能初始化的类被叫做抽象类,它们只提供部分实现,但是另一个类可以继承它并且能创建它们
的实例。抽象类能够被用于类,方法,属性,索引器和事件,使用abstract 在一个类声明中表示该类倾向要作为其它类的基类,成员被标示成abstract,或被包含进一个抽象类,必须被其派生类实现。 - 一个抽象类可以包含抽象和非抽象方法,当一个类继承于抽象类,那么这个派生类必须实现所有的,一个抽象方法是一个没有方法体的方法。
- 通过声明派生类也为抽象,我们可以避免所有或特定的虚方法的实现,这就是抽象类的部分实现。
- 在C#中,一个抽象类能够继承另一个非抽象类,另外,继承了基类的方法,添加新的抽象和非抽象方法是可行的。
- 一个抽象类也能从一个接口来实现,这种情况,我们必须为所有的方法提供方法体,这些方法是来自接口。
- 我们不能把关键字abstract 和 sealed 一起用在C#中,因为一个密封类不能够被抽象。
-
一个抽象类必须为所有的接口成员提供实现
一个用于实现接口的抽象类可能把接口方法安排到抽象方法上。例如
interface I
{
void M();
}
abstract class C: I
{
public abstract void M();
}
抽象类特征
- 抽象类不能被实例化;
- 抽象类可以包含抽象方法和访问器;
- 不能把抽象类用密封(sealed)来修饰,那就意味着类不能被继承,这违反抽象类被继承的原则;
- 一个派生于一个抽象类的非抽象类必须包括所有的继承来的抽象方法和访问器的实现;
- 在方法和属性中使用abstract 关键字意味着包含它们的实现
抽象方法特征
- 抽象方法是隐式的虚方法;
- 抽象方法的声明只能在抽象类中;
- 因为抽象方法声明只提供一个无实现的方式,没有方法体;
- 方法体的实现被覆写方法提供,覆写方法是一个非抽象类的成员;
- 抽象属性的行为和抽象方法相像,除了不同的声明形式。
- 在一个静态属性中使用abstract 是一个错误。
*一个抽象属性能够通过派生类使用 override 实现.
2.虚函数和抽象方法
参考文档
- C#之虚函数
-
虚方法(virtual)和抽象方法(abstract)的区别
虚方法与非虚方法的最大不同是,虚方法的实现可以由派生类所取代,这种取代是通过方法的重写实现的。
虚方法的特点
- 虚方法前不允许有static,abstract,或override修饰符;
- 虚方法不能是私有的,因此不能使用private修饰符;
- 我们知道一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,
其中那个申明时定义的类叫申明类,那个执行时实例化的类叫实例类。
如:A a =new B(); 其中A是申明类,B是实例类。
- 当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数;
- 如果不是虚函数,那么它就直接执行该函数。而如果是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是开始检查对象的实例类。
- 在这个实例类里,他会检查这个实例类的定义中是否有实现该虚函数或者重新实现该虚函数(通过override关键字)的方法,如果有,它就不会再找了,而是马上执行该实例类中实现的虚函数的方法。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚函数的父类为止,然后执行该父类里重载后的函数。
虚方法(virtual)和抽象方法(abstract)的区别
- 虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化。如:
//抽象方法
public abstract class Animal
{
public abstract void Sleep();
public abstract void Eat();
}
//虚方法
public class Animal
{
public virtual void Sleep(){}
public virtual void Eat(){}
}
- 抽象方法只能在抽象类中声明,虚方法不是。其实如果类包含抽象方法,那么该类也是抽象的,也必须声明为抽象的。如:
public class Animal
{
public abstract void Sleep();
public abstract void Eat();
}
编译器会报错:
Main.cs(10): 'VSTest.Animal.Sleep()' is abstract but it is contained in nonabstract class 'VSTest.Animal'
Main.cs(11): 'VSTest.Animal.Eat()' is abstract but it is contained in nonabstract class 'VSTest.Animal'
- 抽象方法必须在派生类中重写,这一点跟接口类似,虚方法不必。
public abstract class Animal
{
public abstract void Sleep();
public abstract void Eat();
}
public class Cat : Animal
{
public override void Sleep()
{
Console.WriteLine( "Cat is sleeping" );
}
// we need implement Animal.Eat() here
}
编译器会报错:Main.cs(14): 'VSTest.Cat' does not implement inherited abstract member 'VSTest.Animal.Eat()'
因为我们没有实现抽象类中所有抽象方法。
3.静态类和静态类成员
参考文档
(一).C#静态方法与非静态方法比较一、C#静态成员
- 静态成员属于类所有,非静态成员属于类的实例所有。
- 每创建一个类的实例,都会在内存中为非静态成员新分配一块存储;非静态成员属于类所有,为各个类的实例所公用,无论类创建了多少实例;类的静态成员在内存中只占同一块区域。
(二).C#静态方法与非静态方法比较二、C#静态方法
- C#静态方法属于类所有,类实例化前即可使用。
- 非静态方法可以访问类中的任何成员,静态方法只能访问类中的静态成员。
- 因为静态方法在类实例化前就可以使用,而类中的非静态变量必须在实例化之后才能分配内存,这样,C#静态方法调用时无法判断非静态变量使用的内存地址。所以无法使用。而静态变量的地址对类来说是固定的,故可以使用。
(三).C#静态方法与非静态方法比较三、C#静态方法是一种特殊的成员方法
它不属于类的某一个具体的实例,而是属于类本身。所以对静态方法不需要首先创建一个类的实例,而是采用类名.静态方法的格式 。
-
static方法是类中的一个成员方法,属于整个类,即不用创建任何对象也可以直接调用!
static内部只能出现static变量和其他static方法!而且static方法中还不能使用this....等关键字..因为它是属于整个类!
- 静态方法效率上要比实例化高,静态方法的缺点是不自动进行销毁,而实例化的则可以做销毁。
- 静态方法和静态变量创建后始终使用同一块内存,而使用实例的方式会创建多个内存.
-
C#中的方法有两种:实例方法,静态方法.
(四).C#静态方法与非静态方法比较四、C#静态方法中获取类的名称
静态方法中用:
string className =
System.Reflection.MethodBase.
GetCurrentMethod().ReflectedType.FullName;
非静态方法中还可以用:
string className = this.GetType().FullName;
4.静态构造函数
定义:静态构造函数用于初始化任何 静态 数据,或用于执行仅需执行一次的特定操作。 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数。
静态构造函数具有以下特点:
- 静态构造函数既没有访问修饰符,也没有参数。
- 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类。
- 无法直接调用静态构造函数。
- 在程序中,用户无法控制何时执行静态构造函数。
- 静态构造函数的典型用途是:当类使用日志文件时,将使用这种构造函数向日志文件中写入项。
- 静态构造函数在为非托管代码创建包装类时也很有用,此时该构造函数可以调用 LoadLibrary 方法。
- 如果静态构造函数引发异常,运行时将不会再次调用该构造函数,并且在程序运行所在的应用程序域的生存期内,类型将保持未初始化。
5.接口和抽象类
参考文档
①.抽象类和接口的区别:
- 类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类.而接口只是一个行为的规范或规定,微软的自定义接口总是后带able字段,证明其是表述一类类“我能做。。。”。抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中;
- 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法;
- 一个类一次可以实现若干个接口,但是只能扩展一个父类;
- 接口可以用于支持回调,而继承并不具备这个特点;
- 抽象类不能被密封;
- 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的;
- (接口)与非抽象类类似,抽象类也必须为在该类的基类列表中列出的接口的所有成员提供它自己的实现。但是,允许抽象类将接口方法映射到抽象方法上;
- 抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可变的,而把可变的座位子类去实现;
- 好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染;
-
尽量避免使用继承来实现组建功能,而是使用黑箱复用,即对象组合。因为继承的层次增多,造成最直接的后果就是当你调用这个类群中某一类,就必须把他们全部加载到栈中!后果可想而知。(结合堆栈原理理解)。同时,有心的朋友可以留意到微软在构建一个类时,很多时候用到了对象组合的方法。比如 asp.net中,Page类,有Server Request等属性,但其实他们都是某个类的对象。使用Page类的这个对象来调用另外的类的方法和属性,这个是非常基本的一个设计原则;
-
如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法。
②.抽象类和接口的使用
- 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单的方法来控制组件版本;
- 如果创建的功能将在大范围的全异对象间使用,则使用接口。如果要设计小而简练的功能块,则使用接口;
- 如果要设计大的功能单元,则使用抽象类。如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类;
- 抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。
以下是我在网上看到的几个形象比喻。
1.飞机会飞,鸟会飞,他们都继承了同一个接口“飞”;但是F22属于飞机抽象类,鸽子属于鸟抽象类;
2. 就像铁门木门都是门(抽象类),你想要个门我给不了(不能实例化),但我可以给你个具体的铁门或木门(多态);而且只能是门,你不能说它是窗(单继承),一个门可以有锁(接口)也可以有门铃(多实现)。门(抽象类)定义了你是什么,接口(锁)规定了你能做什么(一个接口最好只能做一件事,你不能要求锁也能发出声音吧(接口污染))。
6.mvc和webform
- 图解ASP.NET MVC与WebForm的区别
- 深入比较ASP.NET Webform和ASP.NET MVC两种开发方式的优缺点
- mvc与三层结构终极区别
- MVC:请求过程
- asp.net MVC处理机制
7.mvc的filter
8.项目中前端用了啥框架,怎么实现,以及如何自定义jquery插件
学习闭包:
(2).javascript的闭包
(3).图解闭包