在这里讨论了SO之后,我已经多次读过可变结构是"邪恶"的评论(就像这个问题的答案一样).
C#中可变性和结构的实际问题是什么?
免责声明:我知道有两个关于const-correctness有用的问题,但是,没有人讨论过如何在C++中使用const-correctness 而不是其他编程语言.此外,我对这些问题的答案不满意.
我现在使用了一些编程语言,在C++中让我烦恼的一件事就是const-correctness的概念.在Java,C#,Python,Ruby,Visual Basic等中没有这样的概念,这似乎对C++非常具体.
在你推荐我使用C++ FAQ Lite之前,我已经阅读了它,但这并不能说服我.完全有效,可靠的程序一直用Python编写,没有const关键字或等价物.在Java和C#中,对象可以声明为final(或const),但是没有const成员函数或const函数参数.如果函数不需要修改对象,则它可以采用仅提供对象的读访问权的接口.该技术同样可以在C++中使用.在我工作的两个真实的C++系统上,几乎没有使用const,一切都运行正常.因此,对于让const污染代码库的有用性,我还远没有卖掉.
我想知道在C++中它是什么让const成为必要,而不是其他编程语言.
到目前为止,我只看到了必须使用const的一种情况:
#include <iostream>
struct Vector2 {
int X;
int Y;
};
void display(/* const */ Vector2& vect) {
std::cout << vect.X << " " << vect.Y << std::endl;
}
int main() {
display(Vector2());
}
Run Code Online (Sandbox Code Playgroud)
Visual Studio接受使用const注释掉的编译,但是使用警告C4239时,使用非标准扩展.所以,如果你想要传递临时代码,避免副本和保持标准兼容的语法简洁,你必须通过const引用,不管它.不过,这更像是一个怪癖,而不是一个根本原因.
否则,实际上不存在必须使用const的情况,除非与使用const的其他代码连接.在我看来,康斯特似乎不是一个自以为是的瘟疫,它蔓延到它接触到的一切:
const在C++中工作的原因是因为你可以把它丢弃.如果你不能把它扔掉,那么你的世界就会糟透了.如果声明一个采用const Bla的方法,则可以将它传递给非const Bla.但如果是相反的方式你不能.如果声明一个采用非const Bla的方法,则不能将它传递给const Bla.所以现在你被卡住了.所以你逐渐需要一个不是const的所有东西的const版本,你最终得到了一个阴影世界.在C++中,你可以使用它,因为与C++中的任何东西一样,无论你是否想要这个检查,它都是纯粹可选的.如果你不喜欢它,你可以打破常量.
Anders Hejlsberg(C#架构师),CLR设计选择
我处在一个经典的设计困境中.我正在编写一个C#数据结构来包含一个值和测量单位元组(例如7.0毫米),我想知道我是否应该使用引用类型或值类型.
结构的好处应该是减少堆操作,从而使我在表达式中获得更好的性能并减少对垃圾收集器的压力.对于像这样的简单类型,这通常是我的选择,但在这个具体案例中存在缺点.
元组是相当通用的分析结果框架的一部分,其中结果在WPF应用程序中以不同方式呈现,具体取决于结果值的类型.WPF及其所有数据模板,值转换和模板选择器都可以很好地处理这种弱类型.这意味着如果我的元组表示为结构,则该值将经历大量装箱/拆箱.实际上,在表达式中使用元组对于在拳击场景中的使用来说将是次要的.为了避免所有的拳击,我考虑将我的类型声明为一个类.关于结构的另一个担心是在WPF中可能存在双向绑定的缺陷,因为在代码中的某处而不是引用副本更容易得到元组的副本.
我也有一些方便的运算符重载.我可以使用重载的比较运算符比较毫米和毫米毫米没有问题.但是我不喜欢重载==和!=的想法,如果我的元组是一个类,因为约定是==和!=是引用类型的ReferenceEquals(与System.String不同,这是另一个经典的讨论).如果==和!=重载,有人会写if(myValue == null)并在myValue有一天结果为null时得到一个讨厌的运行时异常.
另一个方面是在C#中没有明确的方法(与例如C++不同)在代码使用中区分引用和值类型,但语义是非常不同的.我担心我的元组的用户(如果声明了struct)假定该类型是一个类,因为大多数自定义数据结构都是并且假设是引用语义.这是另一个争论,为什么人们应该更喜欢类,因为那是用户所期望的,而且没有"." /" - >"告诉他们分开.一般来说,我几乎总是会使用一个类,除非我的探查器告诉我使用一个结构,只是因为类语义最有可能是其他程序员所期望的,而C#只有模糊的提示,无论它是一个还是另一个.
所以我的问题是:
在决定是否应该重视或参考时,我还应该考虑哪些其他因素?
可以== /!=在类中重载在任何情况下都是合理的吗?
程序员假设的东西.大多数人可能会认为称为"点"的东西是值类型.如果您阅读一些带有"UnitValue"的代码,您会假设什么?
根据我的使用说明你会选择什么?
tl; wr:这是一个讨论.
我知道这个"问题"更多的是讨论,因此我将其标记为社区维基.但是,根据" 如何询问"页面,它可能属于此处,因为它具体与编程相关,经过一个小时的研究后无法在网络上讨论,具体而言,与大多数C#程序员和主题相关.而且,这个问题本身就是为了获得答案,不管我的偏见,我都会保持开放的态度:C#真的会从总体结构中受益吗?尽管有这一前言,但我知道这是关闭的,但如果有权限和意图关闭的用户将我重定向到Web上的适当讨论点,我将不胜感激.
结构是C#中的灵活但有争议的类型.它们提供堆栈分配的值类型组织范例,但不提供其他值类型的不变性.
有人说结构应该代表值,而值不会改变(例如int i = 5;,5是不可变的),而有些人认为它们是带有子字段的OOP布局.
上结构不变性(辩论1,2,3),为此,当前的解决方案似乎具有程序员执行不变性,也没有解决.
例如,当结构作为引用(本页底部)访问并限制赋值时,C#编译器将检测可能的数据丢失.此外,由于结构构造函数,属性和函数能够执行任何操作,在返回控件之前使用限制(对于构造函数)分配所有字段,结构不能声明为常量,如果它们仅限于数据表示.
聚合类(维基百科)是严格的数据结构,功能有限,注定会因缺乏灵活性而在对应方面提供语法糖.在C++中,它们"没有用户声明的构造函数,没有私有或受保护的非静态数据成员,没有基类,也没有虚函数".尽管核心概念保持不变,但C#中此类类别的理论细节仍可供讨论.
由于聚合结构是严格的带有标记访问器的数据持有者,因此它们的不变性(在可能的C#环境中)将被保险.除非?指定了null运算符(),否则聚合也无法为空,无其他纯值类型.出于这个原因,许多非法的结构操作将成为可能,以及一些语法糖.
从C++语法中我们可以想象出以下几点:(记住,这是一个社区维基,欢迎并鼓励改进)
aggregate Size
{
int Width;
int Height;
}
aggregate Vector
{
// Default values for constructor.
double X = 0, Y = 0, Z = 0;
}
aggregate Color
{
byte R, G, B, …Run Code Online (Sandbox Code Playgroud) 我对struct类型有所了解.但我无法理解:它是为了什么?我什么时候用它?类,简单的值类型和枚举 - 这就是我所需要的.
有什么建议?
UPD:请!不要告诉我结构在堆栈中(我知道这个:).什么结构用于?
我需要类似的东西List<String, Int32, Int32>.List一次只支持一种类型,而Dictionary一次只支持两种类型.是否有一种干净的方式来做上面的事情(一个多维通用列表/集合)?
我有一个包含一些基本类型的类.(3x float,2x int).
现在我需要一个可以容纳这个类的数百万个实例的集合.我不需要派生类型.所有元素都完全来自这个单独的类.更多元素的数量是固定的.在极少数情况下,我计划复制整个列表/数组并修改副本.最初的列表/数组应该是不可变的,因此我不需要与其他线程同步.
现在的问题是:
我读到C#中的List也在内部实现为Array.
如果它是C++,我知道数组将保存完整的对象.但我不确定C#如何处理这个问题.C#数组是仅保存对类实例的引用还是保存完整的数据结构?
考虑Foo结构如下:
struct Foo
{
public float X;
public float Y;
public Foo(float x, float y)
{
this.X = x;
this.Y = y;
}
public void Change(float x)
{
this.X = x;
}
}
Run Code Online (Sandbox Code Playgroud)
我理解修改构造函数中的字段,这对我和我理解结构作为值,类似数字的不可变类型是完全合乎逻辑的.
然而,因为可以T"做:
Foo bar = new Foo(1, 2);
bar.X = 5;
Run Code Online (Sandbox Code Playgroud)
为什么可以使用:
Foo bar = new Foo(1, 2);
bar.Change(5);
Run Code Online (Sandbox Code Playgroud)
编辑:如果结构是可变的,那么为什么它们不能在列表中修改或从属性返回?
我在这里阅读了几个关于结构的线程(一个关于可变结构)并且我一直在阅读结构应该如何没有标识.
结构中缺少身份究竟是什么?我猜它会像一个数字,例如5,没有上下文(5什么?).客户是期待服务的人,因此有一个身份.我在想什么?
我知道技术差异以及结构如何是线程安全的(只要它们不能被变异,但我仍然可以编写变异状态的方法),每次将它们传递给方法时都会有新副本等等......
我知道结构是值类型,但后来我不明白为什么这样有效:
编辑:我的意思是,为什么this.Size.Height不起作用呢?
struct A
{
int height;
public int Height
{
get
{
return height;
}
set
{
height = value;
}
}
}
//... class Main
{
A a = A();
a.Height = 5; //works. Why? I thought it should say "cannot modify as it is not variable". I think the properties should return copy of this struct...?
}
Run Code Online (Sandbox Code Playgroud)
第二个问题 - 我已经阅读过我不需要使用带有结构的"新"但如果没有它我就无法使用它.
我有一个带有一个字段的结构,它失去了它的价值.我可以将字段声明为静态并解决问题.我也可以将struct更改为class(不改变任何其他内容),这也解决了问题.我只是想知道为什么会这样?
嘿,我有这种类型的东西
eph_t *a;
Run Code Online (Sandbox Code Playgroud)
你可以看到类型是eph_t.它是C中的数组,但我不知道数组的大小,也不知道数组的最终元素是什么.有没有办法,我可以浏览整个数组,因为我想将数组中每个元素的值赋给某些东西.
我可以考虑哪些选择?如果您无法理解问题中的某些内容,只需发表评论,以便我可以通知您.
我希望从今天开始将日期设置为16年前的1月1日.我用过代码
DateTime dtm = new DateTime(DateTime.Today.Year, 1, 1);
dtm.AddYears(-16);
dtpDOB.Value = dtm;// assign value to date time picker
Run Code Online (Sandbox Code Playgroud)
但它显示日期值为2014年1月1日,为什么这不会将年份部分设置为16年?
谢谢
c# ×11
struct ×6
arrays ×2
list ×2
.net ×1
aggregate ×1
boxing ×1
c ×1
c++ ×1
class ×1
class-design ×1
collections ×1
const ×1
datetime ×1
field ×1
generic-list ×1
immutability ×1
mutable ×1
properties ×1