Interview Summary-Advent
Technology Questions(C#):
1. new用法总结
using System; namespace ConsoleApplication1 { public class BaseA { public int x = 1; public void Invoke() { Console.WriteLine(x.ToString()); } public int TrueValue { get { return x; } set { x = value; } } } public class DerivedB : BaseA { new public int x = 2; new public void Invoke() { Console.WriteLine(x.ToString()); } new public int TrueValue { get { return x; } set { x = value; } } } class Test { static void Main(string[] args) { DerivedB b = new DerivedB(); b.Invoke();//调用DerivedB的Invoke方法,输出:2 Console.WriteLine(b.x.ToString());//输出DerivedB的成员x值:2 BaseA a = b; a.Invoke();//调用BaseA的Invoke方法,输出:1 a.TrueValue = 3;//调用BaseA的属性TrueValue,修改BaseA的成员x的值 Console.WriteLine(a.x.ToString());//输出BaseA的成员x的值:3 Console.WriteLine(b.TrueValue.ToString());//输出DerivedB的成员x的值,仍然是:1 //可见,要想访问被隐藏的基类的成员变量、属性或方法,办法就是将子类造型为父类,然 //后通过基类访问被隐藏的成员变量、属性或方法。 } } }
3)new 约束:用于在泛型声明中约束可能用作类型参数的参数的类型。
new约束指定泛型类声明中的任何类型参数都必须具有公共的无参数构造函数.
using System; using System.Collections.Generic; namespace ConsoleApplication2 { public class Employee { private string name; private int id; public Employee() { name = "Temp"; id = 0; } public Employee(string s, int i) { name = s; id = i; } public string Name { get { return name; } set { name = value; } } public int ID { get { return id; } set { id = value; } } } class ItemFactory<T> where T : new() { public T GetNewItem() { return new T(); } } public class Test { public static void Main() { ItemFactory<Employee> EmployeeFactory = new ItemFactory<Employee>(); ////此处编译器会检查Employee是否具有公有的无参构造函数。 //若没有则会有The Employee must have a public parameterless constructor 错误。 Console.WriteLine("{0}'ID is {1}.", EmployeeFactory.GetNewItem().Name, EmployeeFactory.GetNewItem().ID); } } }
2. Using用法总结
三种用法如下:
using关键字可以用作指令、别名、语句。
1)作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。
2)作为别名,以便更易于将标识符限定到命名空间或类型。
eg:using MyAlias = MyCompany.Proj.Nested;
3)作为C# using语句,用于定义一个范围,在此范围的末尾将释放对象。
3. 值类型和引用类型(装箱和拆箱)
C#的所有值类型均隐式派生自System.ValueType:
-
- 结构体:struct(直接派生于System.ValueType);
- 数值类型:
- 整型:sbyte(System.SByte的别名),short(System.Int16),int(System.Int32),long(System.Int64),byte(System.Byte),ushort(System.UInt16),uint(System.UInt32),ulong(System.UInt64),char(System.Char);
- 浮点型:float(System.Single),double(System.Double);
- 用于财务计算的高精度decimal型:decimal(System.Decimal)。
- bool型:bool(System.Boolean的别名);
- 用户定义的结构体(派生于System.ValueType)。
- 枚举:enum(派生于System.Enum);
- 可空类型(派生于System.Nullable<T>泛型结构体,T?实际上是System.Nullable<T>的别名)。
- 结构体:struct(直接派生于System.ValueType);
C#有以下一些引用类型:
-
- 数组(派生于System.Array)
- 用户用定义的以下类型:
- 类:class(派生于System.Object);
- 接口:interface(接口不是一个“东西”,所以不存在派生于何处的问题。Anders在《C# Programming Language》中说,接口只是表示一种约定[contract]);
- 委托:delegate(派生于System.Delegate)。
- object(System.Object的别名);
- 字符串:string(System.String的别名)。
可以看出:
-
- 引用类型与值类型相同的是,结构体也可以实现接口;
- 引用类型可以派生出新的类型,而值类型不能;
- 引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型);
- 引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值。
总结:
1、C#中,变量是值还是引用仅取决于其数据类型。
2、C#的值类型包括:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型。
3、C#的引用类型包括:数组,用户定义的类、接口、委托,object,字符串。
4、数组的元素,不管是引用类型还是值类型,都存储在托管堆上。
5、引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。为了方便,本文简称引用类型部署在托管推上。
6、值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。
7、值类型在内存管理方面具有更好的效率,并且不支持多态,适合用作存储数据的载体;引用类型支持多态,适合用于定义应用程序的行为。
8、应该尽可能地将值类型实现为具有常量性和原子性的类型。
9、应该尽可能地确保0为值类型的有效状态。
10、应该尽可能地减少装箱和拆箱。
4. 继承,多态,重载实现原理
Will Update Soon.
5. 重载、重写、隐藏的区别
C#重载:同一个作用域内发生(比如一个类里面),定义一系列同名方法,但是方法的参数列表不同。这样才能通过传递不同的参数来决定到底调用哪一个。而返回值类型不同是不能构成重载的。
C#重写:继承时发生,在子类中重新定义父类中的方法,子类中的方法和父类的方法是一样的
例如:基类方法声明为virtual(虚方法),派生类中使用override申明此方法的重写.
C#隐藏:基类方法不做申明(默认为非虚方法),在派生类中使用new声明此方法的隐藏。
C#重载时,根据参数选择调用的方法;
C#重写时,访问父类子类皆调用子类的重写方法;
C#隐藏时,访问父类则调用父类的方法,子类子类的方法。
eg1:C#隐藏(new)示例
C#隐藏(new)示例: 1.using System; 2. class A 3. { 4. public void F() 5. { 6. Console.WriteLine("A.F"); 7. } 8. } 9. class B: A 10. { 11. new public void F() 12. { 13. Console.WriteLine("B.F"); 14. } 15. } 16. class Test 17. { 18. static void Main(string[] args) 19. { 20. B b = new B(); 21. b.F(); 22. A a = b; 23. a.F(); 24. } 25. } 输出为 B.F A.F
eg2:C#重写virtual(虚方法)示例
1.using System; 2. class A 3. { 4. public virtual void F() 5. { 6. Console.WriteLine("A.F"); 7. } 8. } 9. class B: A 10. { 11. public override void F() 12. { 13. Console.WriteLine("B.F"); 14. } 15. } 16. class Test 17. { 18. static void Main() 19. { 20. B b = new B(); 21. b.F(); 22. A a = b; 23. a.F(); 24. } 25. } 输出为 B.F B.F
补充:重写override一般用于接口实现和继承类的方法改写,要注意:
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
6. 将一个数组按基数和偶数前后分开
思路:在数组开头和结尾各设置一指针,然后开始对数据比较,如果为偶数,直接与最后一个基数进行位置交换,改变指针位置,重新进行比较。
public static void ArraySort(int[] array) { int i = 0; int j = array.Length - 1; int temp; while (i <= j) { if (array[i] % 2 == 0) { temp = array[i]; array[i] = array[j]; array[j] = temp; j--; } else { i++; } } }
Project Questions:
Will Update Soon
English Questions: