让.Net 值类型具有引用传递的行为
[说明:这里讲的并不是作为函数参数的情形,那样只要用C#的 ref 或者 out 关键字。]
1. C#的unsafe 指针既不优雅,更有不少限制。所以不用他了,那么只能用interface 间接实现了。
using System;
using System.Collections.Generic;
public interface IByRefStruct
{
int Data {
get;
set;
}
}
public struct ByRefStruct : IByRefStruct
{
private int data;
public ByRefStruct(int data) {
this.data = data;
}
public int Data {
get {
return this.data;
}
set {
this.data = value;
}
}
public override string ToString() {
return this.data.ToString();
}
}
internal static class Program
{
private static readonly int N = 3;
private static void Main() {
IList<ByRefStruct> listStruct = new List<ByRefStruct>();
for (int i = 0; i < N; i++) {
listStruct.Add(new ByRefStruct(i + 1));
}
Console.WriteLine("Test struct in List:");
Console.WriteLine("Initial data:", Environment.NewLine);
Print(listStruct);
for (int i = 0; i < listStruct.Count; i++) {
ByRefStruct tmp = listStruct[i];
tmp.Data = listStruct.Count - i;
// This statement is neccesary
listStruct[i] = tmp;
}
Console.WriteLine("{0}Modified data:", Environment.NewLine);
Print(listStruct);
IList<IByRefStruct> listInterface = new List<IByRefStruct>();
for (int i = 0; i < N; i++) {
listInterface.Add(new ByRefStruct(i + 1));
}
Console.WriteLine("{0}{0}Test interface in List:", Environment.NewLine);
Console.WriteLine("Initial data:", Environment.NewLine);
Print(listInterface);
for (int i = 0; i < listStruct.Count; i++) {
IByRefStruct tmp = listInterface[i];
tmp.Data = listInterface.Count - i;
// Now we do not need this statement
// listInterface[i] = tmp;
}
Console.WriteLine("{0}Modified data:", Environment.NewLine);
Print(listInterface);
}
private static void Print(IList<ByRefStruct> list) {
foreach (ByRefStruct byRefStruct in list) {
Console.Write("{0} ", byRefStruct);
}
}
private static void Print(IList<IByRefStruct> list) {
foreach (IByRefStruct byRefStruct in list) {
Console.Write("{0} ", byRefStruct);
}
}
}
2. 使用C++/CLI 的“值类型的强类型装箱实例”,示例如下:
using namespace System;
using namespace System::Collections::Generic;
public value class ByRefStruct
{
private:
int data;
public:
ByRefStruct(int);
virtual String^ ToString() override;
property int Data {
int get();
void set(int);
}
};
ByRefStruct::ByRefStruct(int data) {
this->data = data;
}
String^ ByRefStruct::ToString() {
return this->data.ToString();
}
int ByRefStruct::Data::get() {
return this->data;
}
void ByRefStruct::Data::set(int value) {
this->data = value;
}
const int N = 3;
void Print(IList<Object^>^);
int main() {
IList<Object^>^ list = gcnew List<Object^>();
for (int i = 0; i < N; i++) {
list->Add(gcnew ByRefStruct(i + 1));
}
Console::WriteLine("Initial data:");
Print(list);
for (int i = 0; i < list->Count; i++) {
ByRefStruct^ byRefStruct = (ByRefStruct^)list[i];
byRefStruct->Data = list->Count - i;
// Also do not need this statement
// list[i] = byRefStruct;
}
Console::WriteLine("{0}Modified data:", Environment::NewLine);
Print(list);
return 0;
}
void Print(IList<Object^>^ list) {
for each (Object^ obj in list) {
Console::Write("{0} ", obj);
}
}
在C# 中,我们使用ByRefStruct tmp = listStruct[i],中间发生了一个copy 操作,数据
已不是原来的那个了,而使用interface 的时候,由于interface 是引用类型,所以他在托管
堆中分配内存,IByRefStruct tmp = listInterface[i],取到的是一个托管堆中的对象,并没
有发生copy 操作,所以修改起作用。
而在C++/CLI 中,我们使用ByRefStruct^ temp = (ByRefStruct^)list[i]; 中间发生了
unbox,也没有发生copy操作,而数据还位于托管堆中,但是类型却是ByRefStruct^,这样我
们就可以直接获取ByRefStruct 的Data 成员,这就是所谓的“值类型的强类型装箱实例”。
这在C#中是不能做到的。