在 C# 中,struct 是表示数据结构的值类型数据类型。 它可以包含参数化构造函数、静态构造函数、常量、字段、方法、属性、索引器、运算符、事件和嵌套类型。
struct 可用于保存不需要继承的小数据值,例如 坐标点、键值对和复杂的数据结构。
使用 struct 关键字声明结构。 默认访问修饰符是internal
以下示例声明了一个结构 Coordinate。
struct Coordinate
{
public int x;
public int y;
}
可以使用或不使用 new 运算符创建结构对象,与原始类型变量相同。
var point = new Coordinate();
Console.WriteLine(point.x); //输出: 0
Console.WriteLine(point.y); //输出: 0
上面,使用 new 关键字创建了 Coordinate 结构的对象。 它调用结构的默认无参数构造函数,该构造函数将所有成员初始化为其指定数据类型的默认值。
如果在不使用 new 关键字的情况下声明 struct 类型的变量,则它不会调用任何构造函数,因此所有成员都保持未分配状态。 因此,您必须在访问它们之前为每个成员赋值,否则会产生编译时错误。
struct Coordinate
{
public int x;
public int y;
}
Coordinate point;
Console.Write(point.x); // 编译错误
point.x = 10;
point.y = 20;
Console.Write(point.x); //输出: 10
Console.Write(point.y); //输出: 20
结构不能包含无参数构造函数。 它只能包含参数化构造函数或静态构造函数。
struct Coordinate
{
public int x;
public int y;
public Coordinate(int x, int y)
{
this.x = x;
this.y = y;
}
}
Coordinate point = new Coordinate(10, 20);
Console.WriteLine(point.x); //输出: 10
Console.WriteLine(point.y); //输出: 20
您必须在参数化构造函数中包含结构体的所有成员,并为它赋值; 否则,如果任何成员未配赋值的话,C# 编译器将给出编译时错误。
结构可以包含属性、自动实现的属性、方法等,与类相同。
struct Coordinate
{
public int x { get; set; }
public int y { get; set; }
public void SetOrigin()
{
this.x = 0;
this.y = 0;
}
}
Coordinate point = Coordinate();
point.SetOrigin();
Console.WriteLine(point.x); //输出: 0
Console.WriteLine(point.y); //输出: 0
以下的结构体包含了静态方法
struct Coordinate
{
public int x;
public int y;
public Coordinate(int x, int y)
{
this.x = x;
this.y = y;
}
public static Coordinate GetOrigin()
{
return new Coordinate();
}
}
Coordinate point = Coordinate.GetOrigin();
Console.WriteLine(point.x); //输出: 0
Console.WriteLine(point.y); //输出: 0
为了更高的性能 从 C# 7.2 开始,可以在结构类型的声明中使用 ref 修饰符。 ref 结构类型的实例只能堆栈上分配,并且不能转义到托管堆。 为了确保这一点,编译器将 ref 结构类型的使用限制如下:
如下的示例,在struct没有加入ref之前是不会出错的 Coordinate也是会被分配到堆中
public ref struct Coordinate
{
public int x;
public int y;
}
public class Graph
{
public Coordinate Coordinate { get; set; } //编译时报错 字段或自动实现的属性不能是类型“Coordinate”,除非它是 ref 结构的实例成员。
}
基于这个新的类库里多了 Span
有时候当结构体创建之后,就不允许修改了。我们可以给这个struct加上readonly
public readonly struct Person
{
public string Name { get; } //只能是get
public string Surname { get; }
public int Age { get; }
public Person(string name, string surname, int age)
{
Name = name;
Surname = surname;
Age = age;
}
}
Person s = new Person("asd", "qwe", 15); //只能用这个初始化的方式了
8.0之后可以对给成员添加readonly
public struct Coordinate
{
public Coordinate(int x, int y)
{
X = x;
Y = y;
}
public readonly int X;
public int Y;
}
public class Program
{
public static void Main(string[] args)
{
var coordinate = new Coordinate(5, 8);
coordinate.X = 100; //编译会出错
}
}
结构体可以包含事件用来通知订阅者有关某些操作。 以下结构包含一个事件。
struct Coordinate
{
private int _x, _y;
public int x
{
get{
return _x;
}
set{
_x = value;
CoordinatesChanged(_x);
}
}
public int y
{
get{
return _y;
}
set{
_y = value;
CoordinatesChanged(_y);
}
}
public event Action<int> CoordinatesChanged;
}
上述结构包含 CoordinatesChanged 事件,当 x 或 y 坐标发生变化时会引发该事件。 下面的示例演示了 CoordinatesChanged 事件的处理。
class Program
{
static void Main(string[] args)
{
Coordinate point = new Coordinate();
point.CoordinatesChanged += StructEventHandler;
point.x = 10;
point.y = 20;
}
static void StructEventHandler(int point)
{
Console.WriteLine("Coordinate changed to {0}", point);
}
}
struct 是值类型,因此它比类对象更快。 只要您只想存储数据,比较小体量的数据,注生性能,就使用 struct 。 通常,struct适用于游戏编程, 量化交易。
这个东西返回的是一个新的Struct.
void Main()
{
var students = new Student[]{ new Student{ Name = "abc" }};
var x = students.FirstOrDefault(x => x.Name == "abc");
x.Name ="asdfasdf";
students.Dump(); // students 对象还是abc。 FirstOrDefault返回是一个新的struct.
var t2 = new Student{Name = "abc"};
t2.Name = "abc222";
t2.Dump(); //t2的名字变了。
}
public struct Student
{
public string Name {get;set;}
}
借助 Marshal可以方便把结构体数据映射到byte[]中。跟一些C++写的程序进行数据交换的时候很有用。
public static class StructHelper
{
static byte[] StructToBytes<T>(T structObj) where T : struct
{
int size = Marshal.SizeOf(structObj);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(structObj, buffer, false);
byte[] bytes = new byte[size];
Marshal.Copy(buffer, bytes, 0, size);
return bytes;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
static object BytesToStruct<T>(byte[] bytes, Type strcutType)
{
int size = Marshal.SizeOf(strcutType);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, 0, buffer, size);
return Marshal.PtrToStructure(buffer, strcutType);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
}