内存的分配:
相关阅读
Value Type
),一类是引用类型(Reference Type
)。System.ValueType
类型(注意System.ValueType
本身是一个类类型)System.ValueType
和所有的引用类型都继承自 System.Object
基类。ValueType
。System.ValueType
; 引用类型继承自System.Object
ref
关键字将结构体包装成引用类型进行传递,节省空间及时间。ref
关键字传递的是引用类型的指针,而非引用类型地址。(当声明参数带有ref
关键字时,引用类型传递的是引用类型的指针,相反如果没有ref关键字,参数传递的是新的指向引用内容的指针(引用))C#为了类型安全,默认并不支持指针。但是也并不是说C#不支持指针,我们可以使用unsafe关键词,开启不安全代码(unsafe code)开发模式。在不安全模式下,我们可以直接操作内存,这样就可以使用指针了。在不安全模式下,CLR并不检测unsafe代码的安全,而是直接执行代码。unsafe代码的安全需要开发人员自行检测。
相关阅读 :C# 不安全代码
装箱是将值类型转换为 object
类型或由此值类型实现的任何接口类型的过程。
对值类型进行装箱时,会将该值包装到System.Object
内部,再将后者存储在托管堆上。
取消装箱将从对象中提取值类型。
装箱是隐式的(也可以进行显式装箱);取消装箱是显式的。
//隐式装箱
int i = 123;
object o = i;
//显式装箱
int i = 123;
object o = (object)i; // explicit boxing
//显示取消装箱
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
相对于简单的赋值而言,装箱和取消装箱过程需要进行大量的计算。 对值类型进行装箱时,必须分配并构造一个新对象。 取消装箱所需的强制转换也需要进行大量的计算,只是程度较轻。
如何在 C# 中使用装箱。
// String.Concat example.
// String.Concat has many versions. Rest the mouse pointer on
// Concat in the following statement to verify that the version
// that is used here takes three object arguments. Both 42 and
// true must be boxed.
Console.WriteLine(String.Concat("Answer", 42, true));
// List example.
// Create a list of objects to hold a heterogeneous collection
// of elements.
List<object> mixedList = new List<object>();
// Add a string element to the list.
mixedList.Add("First Group:");
// Add some integers to the list.
for (int j = 1; j < 5; j++)
{
// Rest the mouse pointer over j to verify that you are adding
// an int to a list of objects. Each element j is boxed when
// you add j to mixedList.
mixedList.Add(j);
}
// Add another string and more integers.
mixedList.Add("Second Group:");
for (int j = 5; j < 10; j++)
{
mixedList.Add(j);
}
// Display the elements in the list. Declare the loop variable by
// using var, so that the compiler assigns its type.
foreach (var item in mixedList)
{
// Rest the mouse pointer over item to verify that the elements
// of mixedList are objects.
Console.WriteLine(item);
}
// The following loop sums the squares of the first group of boxed
// integers in mixedList. The list elements are objects, and cannot
// be multiplied or added to the sum until they are unboxed. The
// unboxing must be done explicitly.
var sum = 0;
for (var j = 1; j < 5; j++)
{
// The following statement causes a compiler error: Operator
// '*' cannot be applied to operands of type 'object' and
// 'object'.
//sum += mixedList[j] * mixedList[j]);
// After the list elements are unboxed, the computation does
// not cause a compiler error.
sum += (int)mixedList[j] * (int)mixedList[j];
}
// The sum displayed is 30, the sum of 1 + 4 + 9 + 16.
Console.WriteLine("Sum: " + sum);
// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30
图片来源
Dictionary
中的TKey
, 应该重写什么方法?参考答案:
GetHashCode
和 Equals
扩展:
0. hashCode方法及equals方法的规范
1. MSDN: Object.GetHashCode( )
哈希代码旨在高效插入和基于哈希表的集合中查找。 哈希代码不是永久的值。 出于此原因︰
- 不要序列化哈希代码值或将它们存储在数据库中。
- 不使用的哈希代码作为键从键控集合中检索对象。
- 不要跨应用程序域或进程发送的哈希代码。 在某些情况下,可能会基于每个进程或每个应用程序域计算哈希代码。(说明哈希代码可能不同)
- 不要判断哈希代码是否相等,来两个对象是否相等。 (不相等的对象可以具有相同的哈希代码。) 若要测试相等性,调用ReferenceEquals或Equals方法。
2. MSDN: Object.Equals (Object)
using System;
public class Example
{
public static void Main()
{
byte value1 = 12;
int value2 = 12;
object object1 = value1;
object object2 = value2;
Console.WriteLine("{0} ({1}) = {2} ({3}): {4}",
object1, object1.GetType().Name,
object2, object2.GetType().Name,
object1.Equals(object2));
}
}
// The example displays the following output:
// 12 (Byte) = 12 (Int32): False
using System;
// Define a value type that does not override Equals.
public struct Person
{
private string personName;
public Person(string name)
{
this.personName = name;
}
public override string ToString()
{
return this.personName;
}
}
public struct Example
{
public static void Main()
{
Person person1 = new Person("John");
Person person2 = new Person("John");
Console.WriteLine("Calling Equals:");
Console.WriteLine(person1.Equals(person2));
Console.WriteLine("\nCasting to an Object and calling Equals:");
Console.WriteLine(((object) person1).Equals((object) person2));
}
}
// The example displays the following output:
// Calling Equals:
// True
//
// Casting to an Object and calling Equals:
// True
C#中的抽象类(abstract class)和接口(interface)的比较:
使用params
关键字, 可以指定长度可变的方法参数, 当没有传入参数时, params
列表的长度为0
params
关键字之后不允许有任何其它参数, 且方法声明中只允许有一个params
关键字
示例:
public class MyClass
{
public static void UseParams(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
public static void UseParams2(params object[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
static void Main()
{
// 可以传递一个以逗号分隔的指定类型的列表
UseParams(1, 2, 3, 4);
UseParams2(1, 'a', "test");
// 接受0个或多个参数
// 以下方法只打印一个空行
UseParams2();
// 可以传递一个数组, 只要类型相同
int[] myIntArray = { 5, 6, 7, 8, 9 };
UseParams(myIntArray);
object[] myObjArray = { 2, 'b', "test", "again" };
UseParams2(myObjArray);
// 下面的方法会造成编译器错误, 因为object类型数组无法转化为integer类型数组
//UseParams(myObjArray);
// 下面的方法不会造成错误, 但整个整数类型数组将变成参数数组的第一个元素
UseParams2(myIntArray);
}
}
/*
Output:
1 2 3 4
1 a test
5 6 7 8 9
2 b test again
System.Int32[]
*/
参考:
清理干净。不要保持资源一直开启!确定的关闭所有已打开的连接,尽可能的清理所有非托管对象。当使用非托管资源时一个原则是:尽可能晚的初始化对象并且尽快释放掉资源。
不要过度的使用引用。合理的利用引用对象。记住,如果我们的对象还存活,我们应该将对象设置为null。我在设置空值时的一个技巧是使用NullObject模式来避免空引用带来的异常。当GC开始回收时越少的引用对象存在,越有利于性能。
简单的用终结器(终结器隐式调用对象基类上的 Finalize
)。对GC来说终结器十分消耗资源,我们只有在十分确定的方式下使用终结器。如果我们能用IDisposeable
代替终结器(在垃圾回收器释放对象前显式释放资源),它将十分有效率,因为我们的GC一次就能回收掉资源,而不是两次。
将对象和其子对象放在一起。便于GC拷贝大数据而不是数据碎片,当我们声明一个对象时,尽可能将内部所有对象声明的近一些。
相关阅读: 译文—C#堆VS栈(Part Four)