new 运算符
一:运算符
用于创建对象和调用构造函数。例如:
Class1 obj = new Class1();
还可用于创建匿名类型的实例:
var query = from cust in customers select new {Name = cust.Name, Address = cust.PrimaryAddress};
new 运算符还用于调用值类型的默认构造函数。例如:
int i = new int();
在上一个语句中,i 初始化为 0,它是 int 类型的默认值。该语句的效果等同于:
int i = 0;
有关默认值的完整列表,请参见默认值表(C# 参考)。
请记住,为结构声明默认的构造函数是错误的,因为每一个值类型都隐式具有一个公共的默认构造函数。可以在结构类型上声明参数化构造函数以设置其初始值,但是,只有在需要默认值之外的值时才必须这样做。
值类型对象(例如结构)是在堆栈上创建的,而引用类型对象(例如类)是在堆上创建的。两种类型的对象都是自动销毁的,但是,基于值类型的对象是在超出范围时销毁,而基于引用类型的对象则是在对该对象的最后一个引用被移除之后在某个不确定的时间销毁。对于占用固定资源(例如大量内存、文件句柄或网络连接)的引用类型,有时需要使用确定性终止以确保对象被尽快销毁。有关更多信息,请参见 using 语句(C# 参考)。
不能重载 new 运算符。
如果 new 运算符分配内存失败,将引发异常 OutOfMemoryException。
在下面的示例中,通过使用 new 运算符创建并初始化一个 struct 对象和一个类对象,然后为它们赋值。显示了默认值和所赋的值。
struct SampleStruct
{
public int x;
public int y;
public SampleStruct(int x, int y)
{
this.x = x;
this.y = y;
}
}
class SampleClass
{
public string name;
public int id;
public SampleClass() {}
public SampleClass(int id, string name)
{
this.id = id;
this.name = name;
}
}
class ProgramClass
{
static void Main()
{
// Create objects using default constructors:
SampleStruct Location1 = new SampleStruct();
SampleClass Employee1 = new SampleClass();
// Display values:
Console.WriteLine("Default values:");
Console.WriteLine(" Struct members: {0}, {1}",
Location1.x, Location1.y);
Console.WriteLine(" Class members: {0}, {1}",
Employee1.name, Employee1.id);
// Create objects using parameterized constructors:
SampleStruct Location2 = new SampleStruct(10, 20);
SampleClass Employee2 = new SampleClass(1234, "Cristina Potra");
// Display values:
Console.WriteLine("Assigned values:");
Console.WriteLine(" Struct members: {0}, {1}",
Location2.x, Location2.y);
Console.WriteLine(" Class members: {0}, {1}",
Employee2.name, Employee2.id);
}
}
/*
Output:
Default values:
Struct members: 0, 0
Class members: , 0
Assigned values:
Struct members: 10, 20
Class members: Cristina Potra, 1234
*/
注意,在示例中字符串的默认值为 null,因此未显示它。
2.new 修饰符
在用作声明修饰符时,new 关键字可以显式隐藏从基类继承的成员。隐藏继承的成员时,该成员的派生版本将替换基类版本。虽然可以不使用 new 修饰符来隐藏成员,但将收到编译器警告。如果使用 new 来显式隐藏成员,将禁止此警告。
若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并使用 new 修饰符对其进行修饰。例如:
public class BaseC { public int x; public void Invoke() { } } public class DerivedC : BaseC { new public void Invoke() { } }
在此示例中,使用 BaseC.Invoke 隐藏了 DerivedC.Invoke。字段 x 不受影响,因为未使用类似名称将其隐藏。
通过继承隐藏名称采用下列形式之一:
通常,在类或结构中引入的常数、字段、属性或类型会隐藏与其共享名称的所有基类成员。有三种特殊情况。例如,如果将名称为 N 的新字段声明为不可调用的类型,并且基类型将 N 声明为一种方法,则新字段在调用语法中不会隐藏基声明。请参阅 C# 语言规范获取详细信息(请参阅“表达式”一节中“成员查找”部分)。
类或结构中引入的方法会隐藏基类中共享该名称的属性、字段和类型。它还会隐藏具有相同签名的所有基类方法。
类或结构中引入的索引器会隐藏具有相同签名的所有基类索引器。
对同一成员同时使用 new 和 override 是错误的做法,因为这两个修饰符的含义互斥。 new 修饰符会用同样的名称创建一个新成员并使原始成员变为隐藏。 override 修饰符会扩展继承成员的实现。
在不隐藏继承成员的声明中使用 new 修饰符将会生成警告。
在此示例中,基类 BaseC 和派生类 DerivedC 使用相同的字段名 x,从而隐藏了继承字段的值。此示例演示 new 修饰符的用法。另外还演示了如何使用完全限定名访问基类的隐藏成员。
public class BaseC { public static int x = 55; public static int y = 22; } public class DerivedC : BaseC { // Hide field 'x'. new public static int x = 100; static void Main() { // Display the new value of x: Console.WriteLine(x); // Display the hidden value of x: Console.WriteLine(BaseC.x); // Display the unhidden member y: Console.WriteLine(y); } } /* Output: 100 55 22 */
在此示例中,嵌套类隐藏了基类中同名的类。此示例演示如何使用 new 修饰符来消除警告消息,以及如何使用完全限定名来访问隐藏的类成员。
public class BaseC { public class NestedC { public int x = 200; public int y; } } public class DerivedC : BaseC { // Nested type hiding the base type members. new public class NestedC { public int x = 100; public int y; public int z; } static void Main() { // Creating an object from the overlapping class: NestedC c1 = new NestedC(); // Creating an object from the hidden class: BaseC.NestedC c2 = new BaseC.NestedC(); Console.WriteLine(c1.x); Console.WriteLine(c2.x); } } /* Output: 100 200 */
如果移除 new 修饰符,程序仍将编译和运行,但你会收到以下警告:
当泛型类创建类型的新实例,请将 new 约束应用于类型参数,如下面的示例所示:
class ItemFactory<T> where T : new() { public T GetNewItem() { return new T(); } }
当与其他约束一起使用时,new() 约束必须最后指定:
public class ItemFactory2<T> where T : IComparable, new() { }
在C#的泛型定义中有new()约束关键字。这个用途是限制泛型参数,必须有一个公共的无参的构造函数。简单点说就是你传递一个类代替T的时候,这个类必须有一个构造函数,且必须是公共的无参的。
还有一点需要说明的是,当有多个约束时候,new()关键字必须放到坐后面。