在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
C#/.Net/数据库笔试资料 using System; using System.Collections.Generic; using System.Text;
namespace Example01 { class Program { class Class1 { public static String staticStr = "Class"; public String notstaticStr = "Obj"; } static void Main(string[] args) { //静态变量通过类进行访问,该类所有实例的同一静态变量都是同一个值 Console.WriteLine("Class1's staticStr: {0}", Class1.staticStr);
Class1 tmpObj1 = new Class1(); tmpObj1.notstaticStr = "tmpObj1"; Class1 tmpObj2 = new Class1(); tmpObj2.notstaticStr = "tmpObj2";
//非静态变量通过对象进行访问,不同对象的同一非静态变量可以有不同的值 Console.WriteLine("tmpObj1's notstaticStr: {0}", tmpObj1.notstaticStr); Console.WriteLine("tmpObj2's notstaticStr: {0}", tmpObj2.notstaticStr);
Console.ReadLine(); } } } 结果: 2.const 和 static readonly 区别? 答:const 用 const 修饰符声明的成员叫常量,是在编译期初始化并嵌入到客户端程序 static readonly 用 static readonly 修饰符声明的成员依然是变量,只不过具有和常量类似的使用方法:通过类进行访问、初始化后不可以修改。但与常量不同的是这种变量是在运行期初始化 示例: 测试类: using System; using System.Collections.Generic; using System.Text;
namespace Example02Lib { public class Class1 { public const String strConst = "Const"; public static readonly String strStaticReadonly = "StaticReadonly"; //public const String strConst = "Const Changed"; //public static readonly String strStaticReadonly = "StaticReadonly Changed"; } }
客户端代码:
using System; using System.Collections.Generic; using System.Text; using Example02Lib;
namespace Example02 { class Program { static void Main(string[] args) { //修改Example02中Class1的strConst初始值后,只编译Example02Lib项目 //然后到资源管理器里把新编译的Example02Lib.dll拷贝Example02.exe所在的目录, 执行Example02.exe //切不可在IDE里直接调试运行因为这会重新编译整个解决方案!!
//可以看到strConst的输出没有改变,而strStaticReadonly的输出已经改变 //表明Const变量是在编译期初始化并嵌入到客户端程序,而StaticReadonly是在运行时初始化的 Console.WriteLine("strConst : {0}", Class1.strConst); Console.WriteLine("strStaticReadonly : {0}", Class1.strStaticReadonly);
Console.ReadLine(); } } } 结果: 修改后的示例: 测试类: using System; using System.Collections.Generic; using System.Text;
namespace Example02Lib { public class Class1 { //public const String strConst = "Const"; //public static readonly String strStaticReadonly = "StaticReadonly"; public const String strConst = "Const Changed"; public static readonly String strStaticReadonly = "StaticReadonly Changed"; } } 结果 strConst : Const 3.extern 是什么意思? 示例: using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace Example03 { class Program { //注意DllImport是一个Attribute Property,在System.Runtime.InteropServices命名空间中定义 //extern与DllImport一起使用时必须再加上一个static修饰符 [DllImport("User32.dll")] public static extern int MessageBox(int Handle, string Message,
string Caption, int Type);
static int Main() { string myString; Console.Write("Enter your message: "); myString = Console.ReadLine(); return MessageBox(0, myString, "My Message Box", 0); } } } 4.abstract 是什么意思? 答:abstract 修饰符可以用于类、方法、属性、事件和索引指示器(indexer),表示其为抽象成员.abstract 不可以和 static 、virtual 一起使用声明为 abstract 成员可以不包括实现代码,但只要类中还有未实现的抽象成员(即抽象类),那么它的对象就不能被实例化,通常用于强制继承类必须实现某一成员 示例: using System; using System.Collections.Generic; using System.Text;
namespace Example04 { #region 基类,抽象类 public abstract class BaseClass { //抽象属性,同时具有get和set访问器表示继承类必须将该属性实现为可读写 public abstract String Attribute { get; set; }
//抽象方法,传入一个字符串参数无返回值 public abstract void Function(String value);
//抽象事件,类型为系统预定义的代理(delegate):EventHandler public abstract event EventHandler Event;
//抽象索引指示器,只具有get访问器表示继承类必须将该索引指示器实现为只读 public abstract Char this[int Index] { get; } } #endregion
#region 继承类 public class DeriveClass : BaseClass { private String attribute;
public override String Attribute { get { return attribute; } set { attribute = value; } } public override void Function(String value) { attribute = value; if (Event != null) { Event(this, new EventArgs()); } } public override event EventHandler Event; public override Char this[int Index] { get { return attribute[Index]; } } } #endregion
class Program { static void OnFunction(object sender, EventArgs e) { for (int i = 0; i < ((DeriveClass)sender).Attribute.Length; i++) { Console.WriteLine(((DeriveClass)sender)[i]); } } static void Main(string[] args) { DeriveClass tmpObj = new DeriveClass();
tmpObj.Attribute = "1234567"; Console.WriteLine(tmpObj.Attribute);
//将静态函数OnFunction与tmpObj对象的Event事件进行关联 tmpObj.Event += new EventHandler(OnFunction);
tmpObj.Function("7654321");
Console.ReadLine(); } } } 结果: 5.internal 修饰符起什么作用? 答:internal 修饰符可以用于类型或成员,使用该修饰符声明的类型或成员只能在同一程集内访问接口的成员不能使用 internal 修饰符值得注意的是,如果为 internal 成员加上了 protected 修饰符,这时的访问级别为 internal 或 protected。只是看字面意思容易弄错,许多人认为 internal protected 应该是“只有同一个程序集中的子类可以访问”,但其实它表示“同一个程序集中的所有类,以及所有程序集中的子类都可以访问” 示例 Example05Lib 项目的 Class1 using System; using System.Collections.Generic; using System.Text;
namespace Example05Lib { public class Class1 { internal String strInternal = null; public String strPublic; internal protected String strInternalProtected = null; } } 结果
Example05 项目里的 Class3 类无法访问到 Class1 的 strInternal 成员,因为它们不在同一个程序集里。但却可以访问到 strInternalProtected 成员,因为 Class3 是 Class1 的继承类
Example05 项目的 Program 类既无法访问到 Class1 的 strInternal 成员,也无法访问到 strInternalProtected 成员,因为它们既不在同一个程序集里也不存在继承关系
答: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 中对 F 方法进行了密封, 类 C 在继承类 B 时只能重写一个函数,如图所示:
控制台输出结果,类 C 的方法 F 只能是输出 类B 中对该方法的实现: A.F
答:override 表示重写,用于继承类对基类中虚成员的实现 overload 表示重载,用于同一个类中同名方法不同参数(包括类型不同或个数不同)的实现 示例: 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(); } } } 结果: 8.什么是索引指示器? 答:实现索引指示器(indexer)的类可以象数组那样使用其实例后的对象,但与数组不同的是索引指示器的参数类型不仅限于int.. 简单来说,其本质就是一个含参数属性 示例: 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]; } } }
//感谢watson hua(http://huazhihao.cnblogs.com/)的指点 //索引指示器的实质是含参属性,参数并不只限于int class WeatherOfWeek { public string this[int Index] { get { //注意case段使用return直接返回所以不需要break switch (Index) { case 0: { return "Today is cloudy!"; } case 5: { return "Today is thundershower!"; } default: { return "Today is fine!"; } } } } public string this[string Day] { get { string TodayWeather = null; //switch的标准写法 switch (Day) { case "Sunday": { TodayWeather = "Today is cloudy!"; break; } case "Friday": { TodayWeather = "Today is thundershower!"; break; } default: { TodayWeather = "Today is fine!"; break; } } return TodayWeather; } } } 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]); }
string[] Week = new string[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Staurday"}; WeatherOfWeek tmpWeatherOfWeek = new WeatherOfWeek(); for (int i = 0; i < 6; i++) { Console.WriteLine(tmpWeatherOfWeek[i]); } foreach (string tmpDay in Week) { Console.WriteLine(tmpWeatherOfWeek[tmpDay]); }
Console.ReadLine(); } } } 结果: 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(); } } } 结果: 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(); } } } 结果: 11.可以使用抽象函数重写基类中的虚函数吗? 答:可以 需使用 new 修饰符显式声明,表示隐藏了基类中该函数的实现 或增加 override 修饰符,表示抽象重写了基类中该函数的实现 示例: class BaseClass { public virtual void F() { Console.WriteLine("BaseClass.F"); } } abstract class DeriveClass1 : BaseClass { public abstract new void F(); }
//感谢watson hua(http://huazhihao.cnblogs.com/)的指点 //是他提醒了我还可以用这种方法抽象重写基类的虚方法 abstract class DeriveClass2 : BaseClass { public abstract override 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.什么是属性访问器? 答:属性访问器(Property Accessor),包括 get 访问器和 set 访问器分别用于字段的读写操作 答:abstract 修饰符不可以和 static、virtual 修饰符一起使用 abstract 修饰符可以和 override 一起使用,参见第11点 示例: using System; using System.Collections.Generic; using System.Text;
namespace Example14 { class BaseClass { public virtual void F() { Console.WriteLine("BaseClass.F"); } } abstract class DeriveClass1 : BaseClass { //在这里, abstract是可以和override一起使用的 public abstract override void F(); } class Program { static void Main(string[] args) { } } } 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(); } } } 结果: 17.接口的多继承会带来哪些问题? 答:C# 中的接口与类不同,可以使用多继承,即一个子接口可以有多个父接口。但如果两个父成员具有同名的成员,就产生了二义性(这也正是 C# 中类取消了多继承的原因之一),这时在实现时最好使用显式的声明 示例: 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(); } } } 结果: 18.抽象类和接口的区别? 答:抽象类(abstract class)可以包含功能定义和实现,接口(interface)只能包含功能定义 19.别名指示符是什么? 答:通过别名指示符我们可以为某个类型起一个别名 示例: Class1.cs: using System; using System.Collections.Generic; using System.Text;
namespace com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01 { class Class1 { public override string ToString() { return "com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01's Class1"; } } } Class2.cs: using System; using System.Collections.Generic; using System.Text;
namespace com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02 { class Class1 { public override string ToString() { return "com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02's Class1"; } } } 主单元(Program.cs): using System; using System.Collections.Generic; using System.Text;
//使用别名指示符解决同名类型的冲突 //在所有命名空间最外层定义,作用域为整个单元文件 using Lib01Class1 = com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01.Class1; using Lib02Class2 = com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02.Class1;
namespace Example19 { namespace Test1 { //Test1Class1在Test1命名空间内定义,作用域仅在Test1之内 using Test1Class1 = com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01.Class1;
class Class1 { //Lib01Class1和Lib02Class2在这可以正常使用 Lib01Class1 tmpObj1 = new Lib01Class1(); Lib02Class2 tmpObj2 = new Lib02Class2(); //TestClass1在这可以正常使用 Test1Class1 tmpObj3 = new Test1Class1(); } } namespace Test2 { using Test1Class2 = com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01.Class1;
class Program { static void Main(string[] args) { //Lib01Class1和Lib02Class2在这可以正常使用 Lib01Class1 tmpObj1 = new Lib01Class1(); Lib02Class2 tmpObj2 = new Lib02Class2();
//注意这里,TestClass1在这不可以正常使用。 //因为,在Test2命名空间内不能使用Test1命名空间定义的别名 //Test1Class1 tmpObj3 = new Test1Class1();
//TestClass2在这可以正常使用 Test1Class2 tmpObj3 = new Test1Class2();
Console.WriteLine(tmpObj1); Console.WriteLine(tmpObj2); Console.WriteLine(tmpObj3);
Console.ReadLine(); } } } }
20.如何手工释放资源? 答: .NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工作。但在以下两种情况需要我们手工进行资源释放:一、由于它无法对非托管资源进行释放,所以我们必须自己提供方法来释放对象内分配的非托管资源,比如你在对象的实现代码中使用了一个COM对象;二、你的类在运行是会产生大量实例(象 GIS 中的Geometry),必须自己手工释放这些资源以提高程序的运行效率 最理想的办法是通过实现一个接口显式的提供给客户调用端手工释放对象,System 命名空间内有一个 IDisposable 接口,拿来做这事非常合适,省得我们自己再声明一个接口了 示例: 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();
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论