C# 获取值类型C的引用#
可能重复:C# 获取值类型C的引用#,c#,unsafe,value-type,C#,Unsafe,Value Type,可能重复: 我想将我的值类型的引用复制到另一个值类型。是否可以不使用不安全的范例 int a = 10; int b = 15; a = b; // BUT I WANT TO SET REFERENCE OF b ! 这是我的真实例子。我创建枚举值类型并通过引用将其作为参数发送: public enum Test { test1, test2, test3 } public class MyTestClass1 { private Test _tes
我想将我的值类型的引用复制到另一个值类型。是否可以不使用
不安全的范例
int a = 10;
int b = 15;
a = b; // BUT I WANT TO SET REFERENCE OF b !
这是我的真实例子。我创建枚举值类型并通过引用将其作为参数发送:
public enum Test
{
test1,
test2,
test3
}
public class MyTestClass1
{
private Test _test;
public MyTestClass1(ref Test test)
{
_test = test;
}
}
public class Content
{
public void Method()
{
Test e = Test.test1;
/*some code*/
var omyTestClass1 = new MyTestClass1(ref e);
/*some code*/
e = Test.test2;
/*some code*/
e = Test.test3;
}
}
如果不使用unsafe
或将值类型包装到结构/类中,则无法执行此操作
发件人:
将一个值类型变量赋值给另一个值类型变量会复制包含的值
如果不使用unsafe
或将值类型包装到结构/类中,则无法执行此操作
发件人:
将一个值类型变量赋值给另一个值类型变量会复制包含的值
在同一方法中,不可能有一个局部变量包含对另一个局部变量的引用。类似的情况可能是,方法参数在调用方法中引用局部变量:
void CallingMethod()
{
int a = 10;
SomeMethod(ref a);
...
}
void SomeMethod(ref int b)
{
// here, the variable b holds a reference to CallingMethod's local variable "a".
请注意,b
可以包含对局部变量以外的对象的引用。例如,您可以像这样调用SomeMethod,在这种情况下,引用将指向堆上对象的字段:
class MyClass
{
private int _a;
public void AnotherCaller()
{
SomeMethod(ref _a);
...
}
}
更新:Eric Lippert偶尔会写一些关于您询问的功能的文章,因为CLR确实支持它,而C#可以支持它。请参阅此答案,例如:在同一方法中,不可能有一个局部变量包含对另一个局部变量的引用。类似的情况可能是,方法参数在调用方法中引用局部变量:
void CallingMethod()
{
int a = 10;
SomeMethod(ref a);
...
}
void SomeMethod(ref int b)
{
// here, the variable b holds a reference to CallingMethod's local variable "a".
请注意,b
可以包含对局部变量以外的对象的引用。例如,您可以像这样调用SomeMethod,在这种情况下,引用将指向堆上对象的字段:
class MyClass
{
private int _a;
public void AnotherCaller()
{
SomeMethod(ref _a);
...
}
}
更新:Eric Lippert偶尔会写一些关于您询问的功能的文章,因为CLR确实支持它,而C#可以支持它。例如,看看这个答案:我不确定我是否完全理解你,但是你可以将你的int
包装在某个引用类型中,比如长度为1
的int[]
,或者一个元组,tuple
因此:
由于元组
是不可变的,因此这是毫无意义的
但随后:
var a = new[] { 10 };
var b = new[] { 15 };
a = b;
// a and b reference the same object, and that'a a mutable object!
b[0] = 17;
// now a and b are still the same object, so also a[0] == 17
注意:这与您使用的ref
关键字无关。您似乎无缘无故地使用了ref
。我不确定是否完全理解您的意思,但您可以将int
封装在某个引用类型中,例如长度为1
的int[]
,或长度为1元组的tuple
因此:
由于元组
是不可变的,因此这是毫无意义的
但随后:
var a = new[] { 10 };
var b = new[] { 15 };
a = b;
// a and b reference the same object, and that'a a mutable object!
b[0] = 17;
// now a and b are still the same object, so also a[0] == 17
注意:这与您使用的ref
关键字无关。您似乎无缘无故地使用了ref
。如果您的方法实际上是:
public MyTestClass1 Method()
{
Test e = Test.test1;
/*some code*/
var omyTestClass1 = new MyTestClass1(ref e);
/*some code*/
e = Test.test2;
/*some code*/
e = Test.test3;
return omyTestClass1;
}
返回的值包含对堆栈上已存在但现在不存在的值类型的引用。现在,如果你尝试访问该字段,你可以得到任何东西
更糟糕的是,如果你写信给那个参考人怎么办?如果该引用存储在实际的调用堆栈中,而不是将其所有时间都花费在寄存器中(我们可以安全地假设,在引用它的类中有一个字段这一事实意味着它必须存在),那么现在有什么呢?它可以是对对象的引用。可能是回信地址。写入它可能会在核心bug上引起一些奇怪的fandango,很可能是在写入之后的一段时间,因此很难调试
我们会失去一些基本的保障。在您写入该值之后,几乎任何地方的任何代码都可能以某种奇怪的方式失败
在这一点上,值得注意的是,C++和.NET本身(也就是说,你可以在.NET中所做的一切,包括在C++中不能使用的)都允许本地参考文件和REF返回值不允许REF字段。
最接近的方法是捕获lambdas和匿名方法。在这里,局部变量不存储在堆栈中,而是存储在堆中,以允许它们与生命捕获的lambda一样生存(并在收集最后一个lambda时被收集)。您当然可以使用它来维护对对象中以局部形式开始的对象的引用
或者,您可以使用完成必要工作的类包装值类型。举个例子,当我有类似的需求时,我使用了一个:
public sealed class SharedInt
{
private int _value;
/// <summary>Creates a new SharedInt with a value of zero.</summary>
public SharedInt(){}
/// <summary>Creates a new SharedInt.</summary>
/// <param name="value">The initial value of the object.</param>
public SharedInt(int value)
{
_value = value;
}
/// <summary>Returns the value of the SharedInt.</summary>
public int Value
{
get { return _value; }
}
/// <summary>Returns the value of the SharedInt.</summary>
/// <param name="ri">The SharedInt to cast.</param>
/// <returns>An integer of the same value as the SharedInt.</returns>
public static implicit operator int(SharedInt ri)
{
return ri._value;
}
/// <summary>Atomically increment the value of the SharedInt by one.</summary>
/// <returns>The new value.</returns>
public int Increment()
{
return Interlocked.Increment(ref _value);
}
/// <summary>Atomically decrement the value of the SharedInt by one.</summary>
/// <returns>The new value.</returns>
public int Decrement()
{
return Interlocked.Decrement(ref _value);
}
/// <summary>Atomically add a value to the SharedInt.</summary>
/// <param name="addend">The number to add to the SharedInt.</param>
/// <returns>The new value.</returns>
public int Add(int addend)
{
return Interlocked.Add(ref _value, addend);
}
/// <summary>Atomically replace the value of the SharedInt, returning the previous value.</summary>
/// <param name="value">The number to set the SharedInt to.</param>
/// <returns>The old value.</returns>
public int Exchange(int value)
{
return Interlocked.Exchange(ref _value, value);
}
/// <summary>Atomically subtract a value from the SharedInt.</summary>
/// <param name="subtrahend">The number to subtract from the SharedInt.</param>
/// <returns>The new value.</returns>
public int Subtract(int subtrahend)
{
return Interlocked.Add(ref _value, -subtrahend);
}
}
公开公开一个字段有很大的缺点(这就是为什么我们通常从不这样做),但大多数缺点不适用于这种情况(我们希望能够从外部完全操纵它)这意味着您甚至可以将值作为ref
或out
参数传递。如果您的方法实际上是:
public MyTestClass1 Method()
{
Test e = Test.test1;
/*some code*/
var omyTestClass1 = new MyTestClass1(ref e);
/*some code*/
e = Test.test2;
/*some code*/
e = Test.test3;
return omyTestClass1;
}
返回的值包含对堆栈上已存在但现在不存在的值类型的引用。现在,如果你尝试访问该字段,你可以得到任何东西
更糟糕的是,如果你写信给那个参考人怎么办?如果该引用存储在实际的调用堆栈中,而不是将其所有时间都花费在寄存器中(我们可以安全地假设,在引用它的类中有一个字段这一事实意味着它必须存在),那么现在有什么呢?它可以是对对象的引用。可能是回信地址。写入它可能会在核心bug上引起一些奇怪的fandango,很可能是在写入之后的一段时间,因此很难调试
我们会失去一些基本的保障。在您写入该值之后,几乎任何地方的任何代码都可能以某种奇怪的方式失败
在这一点上,值得注意的是,C++和.NET本身(也就是说,你可以在.NET中所做的一切,包括在C语言中不能使用的),它们既允许本地引用又允许RUTU。