在 C# 中,派生类可以包含与基类方法同名的方法。
基类方法必须定义为 virtual。
如果派生类中的方法前面没有 new 或 override 关键字,则编译器将发出警告,该方法将如同存在
new
关键字一样执行操作。如果派生类中的方法前面带有
new
关键字,则该方法被定义为独立于基类中的方法。如果派生类中的方法前面带有
override
关键字,则派生类的对象将调用该方法,而不是调用基类方法。可以从派生类中使用
base
关键字调用基类方法。override
、virtual
和new
关键字还可以用于属性、索引器和事件中。
替代和方法选择
当在类中对方法进行命名时,如果有多个方法与调用兼容(例如,存在两种同名的方法,并且其参数与传递的参数兼容),则 C# 编译器将选择最佳方法进行调用。 以下方法将是兼容的:
public class Derived : Base
{
public override void DoWork(int param) { }
public void DoWork(double param) { }
}
在 Derived
的一个实例中调用 DoWork
时,C# 编译器将首先尝试使该调用与最初在 Derived
上声明的 DoWork
版本兼容。 替代方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。 仅当 C# 编译器无法将方法调用与 Derived
上的原始方法匹配时,才尝试将该调用与具有相同名称和兼容参数的替代方法匹配。 例如:
int val = 5;
Derived d = new Derived();
d.DoWork(val); // Calls DoWork(double).
由于变量 val
可以隐式转换为 double 类型,因此 C# 编译器将调用 DoWork(double)
,而不是 DoWork(int)
。 有两种方法可以避免此情况。 首先,避免将新方法声明为与虚方法相同的名称。 其次,可以通过将 Derived
的实例强制转换为 Base
来使 C# 编译器搜索基类方法列表,从而使其调用虚方法。 由于是虚方法,因此将调用 Derived
上的 DoWork(int)
的实现。 例如:
((Base)d).DoWork(val); // Calls DoWork(int) on Derived.
当定义类型为基类,实例化(初始化)为派生类时,使用该对象调用派生类的方法会产生错误。
当在该基类中定义一个方法,并在派生类中使用new关键字定义一个同名方法时,使用该对象调用该同名方法时,调用的是基类中的同名方法。但使用override关键字重写基类中的方法时,同样的操作调用的是派生类中的方法()。
using System;
using System.Reflection;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("\nTestCars1");
System.Console.WriteLine("----------");
Car car1 = new Car();
car1.DescribeCar();
System.Console.WriteLine("----------");
// Notice the output from this test case. The new modifier is
// used in the definition of ShowDetails in the ConvertibleCar
// class.
ConvertibleCar car2 = new ConvertibleCar();
car2.DescribeCar();
System.Console.WriteLine("----------");
Minivan car3 = new Minivan();
car3.DescribeCar();
System.Console.WriteLine("----------");
System.Console.ReadKey(true);
}
}
class Car
{
public void DescribeCar()
{
System.Console.WriteLine("Four wheels and an engine.");
ShowDetails();
}
public virtual void ShowDetails()
{
System.Console.WriteLine("Standard transportation.");
}
}
// Define the derived classes.
// Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails
// hides the base class method.
class ConvertibleCar : Car
{
public new void ShowDetails()
{
System.Console.WriteLine("A roof that opens up.");
}
}
// Class Minivan uses the override modifier to specify that ShowDetails
// extends the base class method.
class Minivan : Car
{
public override void ShowDetails()
{
System.Console.WriteLine("Carries seven people.");
}
}
}
当抽象类从基类继承虚方法时,抽象类可以使用抽象方法重写该虚方法。
// compile with: /target:library
public class D
{
public virtual void DoWork(int i)
{
// Original implementation.
}
}
public abstract class E : D
{
public abstract override void DoWork(int i);
}
public class F : E
{
public override void DoWork(int i)
{
// New implementation.
}
}
如果将 virtual
方法声明为 abstract
,则该方法对于从抽象类继承的所有类而言仍然是虚方法。 继承抽象方法的类无法访问方法的原始实现,因此在上一示例中,类 F 上的 DoWork
无法调用类 D 上的 DoWork
。通过这种方式,抽象类可强制派生类向虚拟方法提供新的方法实现。
抽象属性的使用示例
using System;
using System.Reflection;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
Shape[] shapes =
{
new Square(5, "Square #1"),
new Circle(3, "Circle #1"),
new Rectangle( 4, 5, "Rectangle #1")
};
System.Console.WriteLine("Shapes Collection");
foreach (Shape s in shapes)
{
System.Console.WriteLine(s);
}
System.Console.ReadKey(true);
}
}
public abstract class Shape
{
private string name;
public Shape(string s)
{
// calling the set accessor of the Id property.
Id = s;
}
public string Id
{
get
{
return name;
}
set
{
name = value;
}
}
// Area is a read-only property - only a get accessor is needed:
public abstract double Area
{
get;
}
public override string ToString()
{
return Id + " Area = " + string.Format("{0:F2}", Area);
}
}
public class Square : Shape
{
private int side;
public Square(int side, string id)
: base(id)
{
this.side = side;
}
public override double Area
{
get
{
// Given the side, return the area of a square:
return side * side;
}
}
}
public class Circle : Shape
{
private int radius;
public Circle(int radius, string id)
: base(id)
{
this.radius = radius;
}
public override double Area
{
get
{
// Given the radius, return the area of a circle:
return radius * radius * System.Math.PI;
}
}
}
public class Rectangle : Shape
{
private int width;
private int height;
public Rectangle(int width, int height, string id)
: base(id)
{
this.width = width;
this.height = height;
}
public override double Area
{
get
{
// Given the width and height, return the area of a rectangle:
return width * height;
}
}
}
}
使用new修饰派生类属性示例:
using System;
using System.Reflection;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
BaseClass b1 = new BaseClass();
DerivedClass d1 = new DerivedClass();
BaseClass d1_1 = new DerivedClass();
//DerivedClass d1_2 = new DerivedClass();
//d1_2 = (DerivedClass)d1_1;
b1.Name = "Mary";
d1.Name = "John";
((DerivedClass)d1_1).Name = "peter";//此处不使用强制装换时,d1_1的输出结果和d1的结果一样。
b1.Id = "Mary123";
d1.Id = "John123"; // The BaseClass.Id property is called.
((DerivedClass)d1_1).Id = "wsafsdasdfasdf";
System.Console.WriteLine("Base: {0}, {1}", b1.Name, b1.Id);
System.Console.WriteLine("Derived: {0}, {1}", d1.Name, d1.Id);
System.Console.WriteLine("Base: {0}, {1}", d1_1.Name, d1_1.Id);
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey(true);
}
}
public class BaseClass
{
private string name = "Name-BaseClass";
private string id = "ID-BaseClass";
public string Name
{
get { return name; }
set { }
}
public string Id
{
get { return id; }
set { }
}
}
public class DerivedClass : BaseClass
{
private string name = "Name-DerivedClass";
private string id = "ID-DerivedClass";
new public string Name
{
get
{
return name;
}
// Using "protected" would make the set accessor not accessible.
set
{
name = value;
}
}
// Using private on the following property hides it in the Main Class.
// Any assignment to the property will use Id in BaseClass.
new private string Id
{
get
{
return id;
}
set
{
id = value;
}
}
}
}
当派生类使用new创建和基类同名的属性,并且声明基类类型而实例化派生类类型时,使用实例化的对象调用该属性时
始终是调用基类的属性,要使该对象调用派生类的属性,必须在调用属性的时候对该对象进行强制转换成派生类。结论:
在以上情况下,调用何种属性或方法取决于声明时变量的类型与具体实例化的类型无关(new和override的区别)。(new和override用在属性和方法上的结论一样)
引用返回值:
对于方法能以引用返回值的形式返回的表达式,存在一些限制。 这些方法包括:
返回值的生存期必须长于方法执行时间。 换言之,它不能是返回自身的方法中的本地变量。 它可以是实例或类的静态字段,也可是传递给方法的参数。 尝试返回局部变量将生成编译器错误 CS8168:“无法按引用返回局部 "obj",因为它不是 ref 局部变量”。
返回值不得为null
使用格式:通过向方法返回类型添前加 ref 关键字来定义 ref 返回值,此外,方法正文中每个 return 语句所返回对象的名称前面须有 ref 关键字
静态构造函数初始化静态成员。 静态构造函数是无参数构造函数
使用对象初始值设定项:
public class Program
{
public static void Main()
{
// Declare a StudentName by using the constructor that has two parameters.
StudentName student1 = new StudentName("Craig", "Playstead");
// Make the same declaration by using an object initializer and sending
// arguments for the first and last names. The default constructor is
// invoked in processing this declaration, not the constructor that has
// two parameters.
StudentName student2 = new StudentName
{
FirstName = "Craig",
LastName = "Playstead",
};
// Declare a StudentName by using an object initializer and sending
// an argument for only the ID property. No corresponding constructor is
// necessary. Only the default constructor is used to process object
// initializers.
StudentName student3 = new StudentName
{
ID = 183
};
// Declare a StudentName by using an object initializer and sending
// arguments for all three properties. No corresponding constructor is
// defined in the class.
StudentName student4 = new StudentName
{
FirstName = "Craig",
LastName = "Playstead",
ID = 116
};
System.Console.WriteLine(student1.ToString());
System.Console.WriteLine(student2.ToString());
System.Console.WriteLine(student3.ToString());
System.Console.WriteLine(student4.ToString());
}
// Output:
// Craig 0
// Craig 0
// 183
// Craig 116
}
public class StudentName
{
// The default constructor has no parameters. The default constructor
// is invoked in the processing of object initializers.
// You can test this by changing the access modifier from public to
// private. The declarations in Main that use object initializers will
// fail.
public StudentName() { }
// The following constructor has parameters for two of the three
// properties.
public StudentName(string first, string last)
{
FirstName = first;
LastName = last;
}
// Properties.
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
public override string ToString()
{
return FirstName + " " + ID;
}
}
总结:使用对象初始值设定项一般是调用类的默认的无参构造方法如下语句
StudentName student2 = new StudentName
{
FirstName = "Craig",
LastName = "Playstead",
};
StudentName student2 = new StudentName()
{
FirstName = "Craig",
LastName = "Playstead",
};
以上两段代码的效果一样,只是可以省略括号。
也可以调用其他构造方法后再进行值设定。代码如下:
StudentName student2 = new StudentName("afsdf","asdfasf")
{
FirstName = "Craig",
LastName = "Playstead",
}
集合初始值设定项:
class StudentName
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
}
class CollInit
{
Dictionary<int, StudentName> students = new Dictionary<int, StudentName>()
{
{ 111, new StudentName {FirstName="Sachin", LastName="Karnik", ID=211}},
{ 112, new StudentName {FirstName="Dina", LastName="Salimzianova", ID=317}},
{ 113, new StudentName {FirstName="Andy", LastName="Ruth", ID=198}}
};
}
泛型类:
非泛型类(即,具体类)可继承自封闭式构造基类,但不可继承自开放式构造类或类型参数,因为运行时客户端代码无法提供实例化基类所需的类型参数。
//No error
class Node1 : BaseNodeGeneric<int> { }
//Generates an error
//class Node2 : BaseNodeGeneric<T> {}
//Generates an error
//class Node3 : T {}
泛型方法:
static void Main(string[] args)
{
int a = 1, b = 2;
String s = "asdffa",s2="asdfasdfgggg";
Program p = new Program();
p.swap(a,b);
p.swap<double>(a, b);//a,b自动转型为doublel
p.swap<String>(s, s2);
System.Console.ReadKey();
}
void swap<T>( T a, T b) {
T a1 = a;
T b1 = b;
System.Console.WriteLine(typeof(T));
System.Console.WriteLine(a1);
System.Console.WriteLine(b1);
}
输出:
System.Int32
1
2
System.Double
1
2
System.String
asdffa
asdfasdfgggg
c#异步编程:
“async”“await”关键字:
“ async ”修饰符只能用于返回值为Task类型或Void的方法。它不能用于主程序的切入点。await只在调用方法时修饰。
所有的方法之前不能使用await关键字,使用“await”关键字方法必须返回 “可等待”类型。以下属于“可等待”类型:
1. Task
2. Task<T>
3. 自定义“可等待”类型
http://www.cnblogs.com/nxhdw/p/6253863.html
static void Main(string[] args)
{
System.Console.WriteLine("main start");
Program.SayAync();
// Thread.Sleep(1000);
System.Console.WriteLine("SayAsync方法执行完成");
System.Console.ReadKey(); }
public static async void SayAync() {
System.Console.WriteLine("async start,before await");
await Task.Run(() =>
{
System.Console.WriteLine("before sleep");
Thread.Sleep(1000);
System.Console.WriteLine("void类型异步方法执行");
});
输出:
main start
async start,before awa
before sleep
SayAsync方法执行完成
void类型异步方法执行