C# 25个必须知道的基础概念2

11.可使用抽象函数重写基类中的虚函数吗?
答:
能够,但需使用 new 修饰符显式声明,表示隐藏了基类中该函数的实现
示例:
Code

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


12.密封类能够有虚函数吗?
答:
能够,基类中的虚函数将隐式的转化为非虚函数,但密封类自己不能再增长新的虚函数
示例:
Code

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 中点的成员最好使用结构
Code

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


17.接口的多继承会带来哪些问题?
答:
C# 中的接口与类不一样,可使用多继承,即一个子接口能够有多个父接口。但若是两个父成员具备同名的成员,就产生了二义性(这也正是 C# 中类取消了多继承的缘由之一),这时在实现时最好使用显式的声明
示例:
Code

using System;
using System.Collections.Generic;
using System.Text;
namespace Example17
{
   
class Program
    {
        
//一个完整的接口声明示例

interface IExample
        {
            
//属性

string P
            {
               
get;
               
set;
            }
            
//方法

string F(int Value);
            
//事件

event EventHandler E;
            
//索引指示器

string
this[int Index]
            {
               
get;
               
set;
            }
        }
        
interface IA
        {
            
int Count { get; set;}
        }
        
interface IB
        {
            
int Count();
        }
        
//IC接口从IA和IB多重继承

interface IC : IA, IB
        {
        }
        
class C : IC
        {
            
private
int count =
100;
            
//显式声明实现IA接口中的Count属性

int IA.Count
            {
               
get { return
100; }
               
set { count = value; }
            }
            
//显式声明实现IB接口中的Count方法

int IB.Count()
            {
               
return count * count;
            }
        }
        
static
void Main(string[] args)
        {
            C tmpObj
=
new C();
            
//调用时也要显式转换
            Console.WriteLine("Count property: {0}", ((IA)tmpObj).Count);
            Console.WriteLine(
"Count function: {0}", ((IB)tmpObj).Count());
            Console.ReadLine();
        }
    }
}


结果:
Count property:
100
Count function:
10000


18.抽象类和接口的区别?
答:
抽象类(abstract class)能够包含功能定义和实现,接口(interface)只能包含功能定义
抽象类是从一系列相关对象中抽象出来的概念, 所以反映的是事物的内部共性;接口是为了知足外部调用而定义的一个功能约定, 所以反映的是事物的外部特性
分析对象,提炼内部共性造成抽象类,用以表示对象本质,即“是什么”
为外部提供调用或功能须要扩充时优先使用接口
19.别名指示符是什么?
答:
经过别名指示符咱们能够为某个类型起一个别名
主要用于解决两个命名空间内有同名类型的冲突或避免使用冗余的命名空间
别名指示符只在一个单元文件内起做用
示例:
Code

Class1.cs:
using System;
using System.Collections.Generic;
using System.Text;
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01
{
   
class Class1
    {
        
public
override
string ToString()
        {
            
return
"com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01's Class1";
        }
    }
}


Class2.cs




using System;
using System.Collections.Generic;
using System.Text;
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02
{
   
class Class1
    {
        
public
override
string ToString()
        {
            
return
"com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's Class1";
        }
    }
}


主单元(Program.cs):
using System;
using System.Collections.Generic;
using System.Text;
//使用别名指示符解决同名类型(51aspx)的冲突

using Lib01Class1 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01.Class1;
using Lib02Class2 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02.Class1;
namespace Example19
{
   
class Program
    {
        
static
void Main(string[] args)
        {
            Lib01Class1 tmpObj1
=
new Lib01Class1();
            Lib02Class2 tmpObj2
=
new Lib02Class2();
            Console.WriteLine(tmpObj1);
            Console.WriteLine(tmpObj2);
            Console.ReadLine();
        }
    }
}


结果:
com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01
's Class1
com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's Class1


20.如何释放非托管资源?
答:
.NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工做,但它没法对非托管资源进行释放,这时咱们必须本身提供方法来释放对象内分配的非托管资源,好比你在对象的实现代码中使用了一个COM对象
最简单的办法,能够经过实现protected void Finalize()(析构函数会在编译时变成这个东东)来释放非托管资源,由于GC在释放对象时会检查该对象是否实现了 Finalize() 方法,若是是则调用它。但,听说这样会下降效率。。。
有一种更好的,那就是经过实现一个接口显式的提供给客户调用端手工释放对象的方法,而不是傻傻的等着GC来释放咱们的对象(况且效率又那么低)
System 命名空间内有一个 IDisposable 接口,拿来作这事很是合适,就免得咱们本身再声明一个接口了
另外补充一句,这种实现并不必定要使用了非托管资源后才用,若是你设计的类会在运行时有大些的实例(象 GIS 中的Geometry),为了优化程序性能,你也能够经过实现该接口让客户调用端在确认不须要这些对象时手工释放它们
示例:

Code

using System;
using System.Collections.Generic;
using System.Text;
namespace Example20
{
   
class Program
    {
        
class Class1 : IDisposable
        {
            
//析构函数,编译后变成 protected void Finalize(),GC会在回收对象前会调用调用该方法

~Class1()
            {
                Dispose(
false);
            }
            
//经过实现该接口,客户能够显式地释放对象,而不须要等待GC来释放资源,听说那样会下降效率

void IDisposable.Dispose()
            {
                Dispose(
true);
            }
            
//将释放非托管资源设计成一个虚函数,提供在继承类中释放基类的资源的能力

protected
virtual
void ReleaseUnmanageResources()
            {
               
//Do something
            }
            
//私有函数用以释放非托管资源

private
void Dispose(bool disposing)
            {
                ReleaseUnmanageResources();
               
//为true时表示是客户显式调用了释放函数,需通知GC不要再调用对象的Finalize方法
               
//为false时确定是GC调用了对象的Finalize方法,因此没有必要再告诉GC你不要调用个人Finalize方法啦

if (disposing)
                {
                    GC.SuppressFinalize(
this);
                }
            }
        }
        
static
void Main(string[] args)
        {
            
//tmpObj1没有手工释放资源,就等着GC来慢慢的释放它吧
            Class1 tmpObj1 =
new Class1();
            
//tmpObj2调用了Dispose方法,传说比等着GC来释放它效率要调一些
            
//我的认为是由于要逐个对象的查看其元数据,以确认是否实现了Dispose方法吧
            
//固然最重要的是咱们能够本身肯定释放的时间以节省内存,优化程序运行效率
            Class1 tmpObj2 =
new Class1();
            ((IDisposable)tmpObj2).Dispose();
        }
    }
}


21.P/Invoke是什么?
答:
在受控代码与非受控代码进行交互时会产生一个事务(transition) ,这一般发生在使用平台调用服务(Platform Invocation Services),即P/Invoke
如调用系统的 API 或与 COM 对象打交道,经过 System.Runtime.InteropServices 命名空间
虽然使用 Interop 很是方便,但据估计每次调用事务都要执行 10 到 40 条指令,算起来开销也很多,因此咱们要尽可能少调用事务
若是非用不可,建议本着一次调用执行多个动做,而不是屡次调用每次只执行少许动做的原则
22.StringBuilder 和 String 的区别?
答:
String 虽然是一个引用类型,但在赋值操做时会产生一个新的对象,而 StringBuilder 则不会
因此在大量字符串拼接或频繁对某一字符串进行操做时最好使用 StringBuilder,不要使用 String
示例:
Code

using System;
using System.Collections.Generic;
using System.Text;
namespace Example22
{
   
class Program
    {
        
static
void Main(string[] args)
        {
            
const
int cycle =
100000;
            
long vTickCount = Environment.TickCount;
            String str
=
null;
            
for (int i =
0; i < cycle; i++)
                str
+= i.ToString();
            Console.WriteLine(
&amp;quot;String: {0} MSEL&amp;quot;, Environment.TickCount - vTickCount);
            vTickCount
= Environment.TickCount;
            
//看到这个变量名我就生气,奇怪为何你们都使它呢? :)
            StringBuilder sb =
new StringBuilder();
            
for (int i =
0; i < cycle; i++)
                sb.Append(i);
            Console.WriteLine(
&amp;quot;StringBuilder: {0} MSEL&amp;quot;, Environment.TickCount - vTickCount);
            Console.ReadLine();
        }
    }
}


结果:
String:
102047 MSEL
StringBuilder:
46 MSEL


23.explicit 和 implicit 的含义?
答:
explicit 和 implicit 属于转换运算符,如用这二者可让咱们自定义的类型支持相互交换
explicti 表示显式转换,如从 A -> B 必须进行强制类型转换(B = (B)A)
implicit 表示隐式转换,如从 B -> A 只需直接赋值(A = B)
隐式转换可让咱们的代码看上去更漂亮、更简洁易懂,因此最好多使用 implicit 运算符。不过!若是对象自己在转换时会损失一些信息(如精度),那么咱们只能使用 explicit 运算符,以便在编译期就能警告客户调用端
示例:
Code

using System;
using System.Collections.Generic;
using System.Text;
namespace Example23
{
   
class Program
    {
        
//本例灵感来源于大话西游经典台词“神仙?妖怪?”--主要是我实在想不出什么好例子了

class Immortal
        {
            
public
string name;
            
public Immortal(string Name)
            {
                name
= Name;
            }
            
public
static
implicit
operator Monster(Immortal value)
            {
               
return
new Monster(value.name +
&amp;quot;:神仙变妖怪?偷偷下凡便可。。。&amp;quot;);
            }
        }
        
class Monster
        {
            
public
string name;
            
public Monster(string Name)
            {
                name
= Name;
            }
            
public
static
explicit
operator Immortal(Monster value)
            {
               
return
new Immortal(value.name +
&amp;quot;:妖怪想当神仙?再去修炼五百年!&amp;quot;);
            }
        }
        
static
void Main(string[] args)
        {
            Immortal tmpImmortal
=
new Immortal(&amp;quot;紫霞仙子&amp;quot;);
            
//隐式转换
            Monster tmpObj1 = tmpImmortal;
            Console.WriteLine(tmpObj1.name);
            Monster tmpMonster
=
new Monster(&amp;quot;孙悟空&amp;quot;);
            
//显式转换
            Immortal tmpObj2 = (Immortal)tmpMonster;
            Console.WriteLine(tmpObj2.name);
            Console.ReadLine();
        }
    }
}


结果:
紫霞仙子:神仙变妖怪?偷偷下凡便可。。。
孙悟空:妖怪想当神仙?再去修炼五百年!


24.params 有什么用?
答:
params 关键字在方法成员的参数列表中使用,为该方法提供了参数个数可变的能力
它在只能出现一次而且不能在其后再有参数定义,以前能够
示例:
Code

using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
   
class App
    {
        
//第一个参数必须是整型,但后面的参数个数是可变的。
        
//并且因为定的是object数组,全部的数据类型均可以作为参数传入

public
static
void UseParams(int id, params
object[] list)
        {
            Console.WriteLine(id);
            
for (int i =
0; i < list.Length; i++)
            {
                Console.WriteLine(list);
            }
        }
        
static
void Main()
        {
            
//可变参数部分传入了三个参数,都是字符串类型
            UseParams(1, &amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;);
            
//可变参数部分传入了四个参数,分别为字符串、整数、浮点数和双精度浮点数数组
            UseParams(2, &amp;quot;d&amp;quot;, 100, 33.33, new
double[] { 1.1, 2.2 });
            Console.ReadLine();
        }
    }
}


结果:
1
a
b
c
2
d
100
33.33
System.Double[]


25.什么是反射?
答:
反射,Reflection,经过它咱们能够在运行时得到各类信息,如程序集、模块、类型、字段、属性、方法和事件
经过对类型动态实例化后,还能够对其执行操做
通常用于插件式框架程序和设计模式的实现,固然反射是一种手段能够充分发挥其能量来完成你想作的任何事情(前面好象见过一位高人用反射调用一个官方类库中未说明的函数。。。)
示例:
Code

using System;
using System.Collections.Generic;
using System.Text;
namespace Example25Lib
{
   
public
class Class1
    {
        
private
string name;
        
private
int age;
        
//若是显式的声明了无参数构造函数,客户端只须要用程序集的CreateInstance便可实例化该类
        
//在此特地不实现,以便在客户调用端体现构造函数的反射实现
        
//public Class1()
        
//{
        
//}

public Class1(string Name, int Age)
        {
            name
= Name;
            age
= Age;
        }
        
public
void ChangeName(string NewName)
        {
            name
= NewName;
        }
        
public
void ChangeAge(int NewAge)
        {
            age
= NewAge;
        }
        
public
override
string ToString()
        {
            
return
string.Format(&amp;quot;Name: {0}, Age: {1}&amp;quot;, name, age);
        }
    }
}


反射实例化对象并调用其方法,属性和事件的反射调用略去
Code

using System;
using System.Collections.Generic;
using System.Text;
//注意添加该反射的命名空间
using System.Reflection;
namespace Example25
{
   
class Program
    {
        
static
void Main(string[] args)
        {
            
//加载程序集
            Assembly tmpAss = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory +
&amp;quot;Example25Lib.dll&amp;quot;);
            
//遍历程序集内全部的类型,并实例化
            Type[] tmpTypes = tmpAss.GetTypes();
            
foreach (Type tmpType in tmpTypes)
            {
               
//获取第一个类型的构造函数信息
                ConstructorInfo[] tmpConsInfos = tmpType.GetConstructors();
               
foreach (ConstructorInfo tmpConsInfo in tmpConsInfos)
                {
                    
//为构造函数生成调用的参数集合
                    ParameterInfo[] tmpParamInfos = tmpConsInfo.GetParameters();
                    
object[] tmpParams =
new
object[tmpParamInfos.Length];
                    
for (int i =
0; i < tmpParamInfos.Length; i++)
                    {
                        tmpParams
= tmpAss.CreateInstance(tmpParamInfos.ParameterType.FullName);
                        
if (tmpParamInfos.ParameterType.FullName ==
&amp;quot;System.String&amp;quot;)
                        {
                            tmpParams
=
&amp;quot;Clark&amp;quot;;
                        }
                    }
                    
//实例化对象

object tmpObj = tmpConsInfo.Invoke(tmpParams);
                    Console.WriteLine(tmpObj);
                    
//获取全部方法并执行

foreach (MethodInfo tmpMethod in tmpType.GetMethods())
                    {
                        
//为方法的调用建立参数集合
                        tmpParamInfos = tmpMethod.GetParameters();
                        tmpParams
=
new
object[tmpParamInfos.Length];
                        
for (int i =
0; i < tmpParamInfos.Length; i++)
                        {
                            tmpParams
= tmpAss.CreateInstance(tmpParamInfos.ParameterType.FullName);
                           
if (tmpParamInfos.ParameterType.FullName ==
&amp;quot;System.String&amp;quot;)
                            {
                                tmpParams
=
&amp;quot;Clark Zheng&amp;quot;;
                            }
                           
if (tmpParamInfos.ParameterType.FullName ==
&amp;quot;System.Int32&amp;quot;)
                            {
                                tmpParams
=
27;
                            }
                        }
                        tmpMethod.Invoke(tmpObj, tmpParams);
                    }
                    
//调用完方法后再次打印对象,比较结果
                    Console.WriteLine(tmpObj);
                }
            }
            Console.ReadLine();
        }
    }
}


结果:
Name: Clark, Age:
0
Name: Clark Zheng, Age:
27设计模式

相关文章
相关标签/搜索