class是引用类型,struct是值类型
引用类型在堆上,值类型是内联的。
值类型有全部的值的内容,而引用类型只有一个地址。
值类型总是有一个值,而引用类型指针可以为空。
- 为了优化减少体积
- 需要传递给c++程序
- 当作参数传递
除了以上,尽量用class。
struct选择基准:
●设计上
1. 倾向于“载体”,“容器”的概念
(比如其他对象的副本,部分副本,数据的集合...)
2. 无状态性(或简单状态)
(状态相当于一个处于对象生命周期内的东西,存储了状态,即说明对象一般需维持一个较长生命周期)
3. 不具有复杂功能,逻辑。甚至于没有功能。
(仅包含get/set, ctor/dtor, isvalid...基本功能)
●限制,需求等因素
1,内容短
2,限制:
不需要设为null,不需要做方便的初始化(比如泛型内 out参数)
不需要使用引用修改的便捷性(修改一个,所有引用可见)
不能有继承性
3,频繁构建,销毁
4,c/c++兼容性
看过一些C++的struct和class选择文章,道理差不多,更多的从设计根本上去剖析,还是很不错的,有些思想值得多参考。
也有些属于语言的层面的坑,需要注意,比如非引用类型在C#中的种种限制,比如C++的struct和class具备几乎完全相同性质
虽说C#/C++语言特性不同,但是在设计上,从问题本质出发的思考上,结论应该是是具备较大相似性的。
1、比如如下定义:
class Student_C
{
public Student_C(string n, int s) { name = n; score = s; }
public string name;
public int score;
}
struct Student_S
{
public Student_S(string n, int s) { name = n; score = s; }
public string name;
public int score;
}
2、引用类型和值类型相比,在创建对象、赋值等基本操作时,含义完全不同。
class Program
{
static void Main(string[] args)
{
Student_C s1 = new Student_C("小明", 80);
Student_C s2 = s1;
// 以上代码在堆上创建了一个"小明"对象,变量s1和s2都指向这个对象
Student_S ss1 = new Student_S("小红", 70);
Student_S ss2 = ss1;
// 以上代码在栈上创建了 两个 临时对象
// ss1和ss2是两个独立的学生对象,只是内容相同
}
}
当我们认识到,即便对于最基本的创建赋值操作,class和struct都是不同的,就会逐步想清楚它们的区别了。
总的来说,C#的struct是为了加速局部对象的处理,包括创建、运算和销毁。所以只有小且频繁的对象才用struct。
例如,Unity中表示坐标的Vector2、Vector3,表示旋转的Quaternion,适合用struct。
它们有两个特点:数据量相当小,只有8~16个字节;计算坐标时会大量创建和使用临时对象。
在这种情况下,使用struct是最合适的。能够减少GC,赋值操作也方便。
除此以外,大多数情况使用class都是更不错的选择。
最后直接回答题主的问题。
1、有一个创建视图的接口,由于参数很多,因此对参数进行封装,封装的参数应该使用class还是struct?
根据题主的字面意思。由于参数数量不确定(未来还可能添加)、字段类型不统一,不满足struct的优化条件,建议使用class。
2、生成excel配置文件对应的class,配置表中的一行数据应该对应一个class还是struct?
从配置文件中读取的数据,一般都是要长期使用的,不存在频繁创建销毁问题;且每一行的数据量估计在十几字节到几百字节不等。所以很可能不满足struct的优化条件,仍然建议使用class。
C# 中 Struct 和 Class 的区别总结
公众号:技术译站,专注IT 技术。一个技术译民的『技术译站』
关注他
6 人赞同了该文章
翻译自 Manju lata Yadav 2019年6月2日 的博文 《Difference Between Struct And Class In C#》,补充了一些内容和示例。
结构体(struct
)是类(class
)的轻量级版本。结构体是值类型,可用于创建行为类似于内置类型的对象。
比较
结构体和类共享许多特性,但与类相比有以下局限性。
- 结构体不能有默认构造函数(无参构造函数)或析构函数,构造函数中必须给所有字段赋值。
public struct Coords
{
public double x;
public double y;
public Coords() //错误,不允许无参构造函数
{
this.x = 3;
this.y = 4;
}
public Coords(double x) //错误,构造函数中必须给所有字段赋值
{
this.x = x;
}
public Coords(double x) //这个是正确的
{
this.x = x;
this.y = 4;
}
public Coords(double x, double y) //这个是正确的
{
this.x = x;
this.y = y;
}
}
- 结构体是值类型,在赋值时进行复制。
- 结构体是值类型,而类是引用类型。
- 结构体可以在不使用
new
操作符的情况下实例化。 例如:
public struct Coords
{
public double x;
public double y;
}
static void Main()
{
Coords p;
p.x = 3;
p.y = 4;
Console.WriteLine($"({p.x}, {p.y})"); // 输出: (3, 4)
}
- 结构体不能继承于另一个结构体或者类,类也不能继承结构体。所有结构体都直接继承于抽象类
System.ValueType
,System.ValueType
又继承于System.Object
。 - 结构体不能是基类,因此,结构体不能是
abstract
的,且总是隐式密封的(sealed
)。 - 不允许对结构体使用抽象(
abstract
)和密封(sealed
)修饰符,也不允许对结构体成员使用protected
或protected internal
修饰符。 - 结构体中的函数成员不能是抽象的(
abstract
)或虚的(virtual
),重写(override
)修饰符只允许重写从System.ValueType
继承的方法。 - 结构体中不允许实例属性或字段包含初始值设定项。但是,结构体允许静态属性或字段包含初始值设定项。 例如:
public struct Coords
{
public double x = 4; //错误, 结构体中初始化器不允许实例字段设定初始值
public static double y = 5; // 正确
public static double z { get; set; } = 6; // 正确
}
- 结构体可以实现接口。
- 结构体可以用作
nullable type
(即:Nullable<T>
中的T
),对其赋值null
值,参考【Nullable<T> Struct
】
什么时候使用结构体或类?
要回答这个问题,我们应该很好地理解它们的差异。
这样,结构体(struct
)只能在确定以下情形时使用:
- 它在逻辑上表示单个值,比如基本类型(
int
,double
,等等)。 - 它是不可变的(immutable)。
- 它不会频繁地装箱和拆箱。
在所有其他情形,应该将类型定义为类(class
)。
结构体示例:
struct Location
{
public int x, y;
public Location(int x, int y)
{
this.x = x;
this.y = y;
}
}
static void Main()
{
Location a = new Location(20, 20);
Location b = a;
a.x = 100;
Console.WriteLine(b.x);
}
输出将是 20。“b” 的值是 “a” 的副本,因此 “b” 不受 “a.x” 更改的影响。但是在类中,输出将是 100,因为变量 “a” 和 “b” 引用同一个对象。
以下为译者补充
结构体实例与类实例
结构体实例的内存在栈(stack
)上进行分配,所占用的内存随声明它的类型或方法一起回收。 这就是在赋值时要复制结构体的一个原因。 相比之下,类实例的内存在堆(heap
)上进行分配,当对类实例的所有引用都超出范围时,为该类实例分配的内存将由公共语言运行时自动回收(垃圾回收)。
结构体实例的值相等性
两个结构体实例的比较是基于值的比较,而类实例的比较则是对其引用的比较。
若要确定两个结构体实例中的实例字段是否具有相同的值,可使用 ValueType.Equals
方法。 由于所有结构体都隐式继承于 System.ValueType
,因此可以直接在其对象上调用该方法,如以下示例所示:
public struct Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
static void Main()
{
Person p1 = new Person("技术译站", 100);
Person p2;
p2.Name = "技术译站";
p2.Age = 100;
if (p2.Equals(p1))
Console.WriteLine("p2 和 p1 有相同的值。");
Console.ReadKey();
}
// 输出: p2 和 p1 有相同的值。
System.ValueType
的 Equals
是使用反射实现的,因为它必须能够确定任何结构体中有哪些字段。 在创建自己的结构体时,重写 Equals
方法可以提供特定于你的类型的高效求等算法。
“基于值的相等”这一点和 C# 9.0 中新增的记录(record
) 类型具有相似之处,想了解 C# 9.0 可以查看:欢迎来到 C# 9.0。