获取值类型C#的引用

时间:2012-09-04 14:44:02

标签: c# unsafe value-type

  

可能重复:
  How do I assign by “reference” to a class field in c#?

我想将我的值类型的引用复制到另一个值类型。是否可以不使用unsafe范例?

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;
    }
}

4 个答案:

答案 0 :(得分:1)

如果不使用unsafe或将值类型包装在struct / class中,则无法执行此操作。

来自MSDN

  

将一个值类型变量分配给另一个值复制包含的值。

答案 1 :(得分:1)

在同一方法中,不可能有一个包含对另一个局部变量的引用的局部变量。 可能的类似情况是方法参数在调用方法中引用局部变量:

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#可能支持它。请参阅此答案,例如:https://stackoverflow.com/a/4923772/385844

答案 2 :(得分:1)

我不确定我是否完全了解你,但您可以将int包含在某个引用类型中,例如长度为int[]的{​​{1}}或1-tuple, 1

所以:

Tuple<int>

由于var a = Tuple.Create(10); var b = Tuple.Create(15); a = b; 不可变,所以这是毫无意义的。

但随后:

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

答案 3 :(得分:1)

如果你的方法实际上是这样的:

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;
}

返回的值包含对堆栈中已存在的值类型的引用,但现在不是。现在,如果您尝试访问该字段,则可以获得任何内容。

更糟糕的是,如果你写这个参考怎么办?如果该引用已存储在实际的调用堆栈中,而不是将所有时间都花在寄存器中(我们可能会安全地假设类中有一个字段指的是它必须是那里有什么呢?它可以是对象的引用。它可能是一个返回地址。写入它可能会导致一些奇怪的fandango-on-the-core错误,很可能在写入后一段时间,因此很难调试。

我们失去了一些基本保障。在您写入该值之后,任何地方的任何代码都可能以某种奇怪的方式失败。

此时值得注意的是,C ++和.NET本身(也就是说,您可以在.NET中执行的所有操作,包括一些您不能在C#中执行的操作),它们都允许本地引用并返回ref don'的值t允许参考字段。

你最接近的就是使用lambdas和匿名方法进行捕获。这里的本地人不是存储在堆栈中,而是存储在堆中,只要它们被生命捕获的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);
  }
}

我需要一些你可能没有的原子性保证,同样可能不需要你需要的一些东西,但它完全可以作为一种能够处理相同的{{1来自不同类的值。

如果您不介意每次都浏览一个属性,可以使用更通用的版本。

int

公开曝光一个字段有相当大的缺点(这就是为什么我们通常不会这样做),但大多数不适用于这种情况(我们希望能够完全操纵它外部)这意味着您甚至可以将public class TypedBox<T> where T : struct { public T Value; } 作为Valueref参数传递。