Aci*_*dic 33 c# struct pass-by-value
我最近试图为一个Vector2字段创建一个属性,只是意识到它不能按预期工作.
public Vector2 Position { get; set; }
Run Code Online (Sandbox Code Playgroud)
这阻止我改变其成员的价值(X&Y)
查看有关此内容的信息,我读到为Vector2struct 创建属性只返回原始对象的副本而不是引用.
作为Java开发人员,这让我很困惑.
C#中的对象何时按值传递,何时通过引用传递?
是否所有struct对象都按值传递?
Kon*_*lph 74
重要的是要意识到C#中的所有内容都是按值传递的,除非您指定ref或out在签名中.
使值类型(以及因此structs)与引用类型不同的是,直接访问值类型,而通过引用访问引用类型.如果将引用类型传递给方法,则它的引用(而不是值本身)将按值传递.
为了说明,假设我们有一个类 PointClass和一个结构 PointStruct,类似地定义(省略不相关的细节):
struct PointStruct { public int x, y; }
class PointClass { public int x, y; }
Run Code Online (Sandbox Code Playgroud)
我们有一个方法SomeMethod,按值采用这两种类型:
static void ExampleMethod(PointClass apc, PointStruct aps) { … }
Run Code Online (Sandbox Code Playgroud)
如果我们现在创建两个对象并调用该方法:
var pc = new PointClass(1, 1);
var ps = new PointStruct(1, 1);
ExampleMethod(pc, ps);
Run Code Online (Sandbox Code Playgroud)
......我们可以使用下图来形象化:
既然pc是引用,它本身不包含值; 相反,它在内存中的其他地方引用(未命名)值.这由虚线边框和箭头可视化.
但是:两个pc和ps,实际变量被复制调用方法时.
如果ExampleMethod在内部重新分配参数变量会发生什么?让我们检查:
static void ExampleMethod(PointClass apc, PointStruct aps); {
apc = new PointClass(2, 2);
aps = new PointStruct(2, 2);
}
Run Code Online (Sandbox Code Playgroud)
输出pc和ps调用该方法后:
pc: {x: 1, y: 1}
ps: {x: 1, y: 1}
Run Code Online (Sandbox Code Playgroud)
→ ExampleMethod更改了值的副本,原始值不受影响.
从根本上说,这就是"通过价值"的意思.
参考值和值类型之间仍然存在差异,并且在修改值的成员时会发挥作用,而不是变量本身.当面对参考类型按值传递这一事实时,这是让人们吵架的部分.考虑一个不同的ExampleMethod.
static void ExampleMethod(PointClass apc, PointStruct aps) {
apc.x = 2;
aps.x = 2;
}
Run Code Online (Sandbox Code Playgroud)
现在我们在调用方法后观察到以下结果:
pc: {x: 2, y: 1}
ps: {x: 1, y: 1}
Run Code Online (Sandbox Code Playgroud)
→参考对象已更改,而值对象未更改.上图显示了为什么:对于参考对象,即使pc被复制,两者pc和apc引用保持相同的实际值,我们也可以修改它apc.至于ps,我们将实际值本身复制到aps; 原始值不能被触及ExampleMethod.
Guf*_*ffa 22
A struct是值类型,因此它始终作为值传递.
值可以是引用类型(对象)或值类型(结构).什么传递总是一个价值; 对于引用类型,您将引用的值传递给它,对于值类型,您传递值本身.
使用或关键字传递参数时,将使用引用术语.然后,您将传递对包含该值的变量的引用,而不是传递该值.通常,参数始终按值传递.refout
.NET数据类型分为值和引用类型.值类型包括int,byte和struct秒.引用类型包括string和类.
当结构只包含一个或两个值类型时,它们是合适的而不是类(尽管在那里你可能会产生意想不到的副作用).
所以结构确实是通过价值传递的,你所看到的是预期的.
前瞻:C#虽然托管仍然具有由C创建的核心内存习语.内存可以合理地视为一个巨大的数组,其中数组中的索引标记为"内存地址".甲指针是这一阵列又名的存储器的数字索引地址.此数组中的值可以是数据,也可以是指向另一个内存地址的指针.甲常量指针是在一些索引,其存储在该阵列中的值不能改变.内存地址固有地存在并且永远不会改变,但是如果它不是 const ,则位于该地址的值总是可以改变.
类/引用类型(包括字符串)由const指针引用传递.变异将影响此实例的所有用法.您无法更改对象的地址.如果您尝试使用任一分配更改地址,或者new您实际上将创建一个与当前范围中的参数共享同一名称的本地变量.
当从方法,属性返回或作为参数接收时,Primitives/ValueTypes/structs(字符串既不是他们不正当地伪装成它们)也被完全复制.结构的变异永远不会被共享.如果struct包含类成员,则复制的是指针引用.这个成员对象是可变的.
原始人永远不会变异.你不能改变1到2,你可以将当前引用1的内存地址改为当前作用域中的2的内存地址.
需要使用out或ref关键字.
ref将允许您更改指针new对象或分配现有对象.ref还允许您通过它的内存指针传递一个原语/ ValueType/struct,以避免复制该对象.如果指定它,它还允许您将指针替换为其他基元.
out在语义上ref与一个小的差异相同.ref需要初始化out参数,其中允许参数未初始化,因为需要在接受参数的方法中初始化参数.这通常在TryParse方法中显示,并且int x = 0; int.TryParse("5", out x)当x的初始值没有用处时,您无需使用.