1

可能重复:
如何通过“引用”分配给 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

4 回答 4

1

unsafe如果不使用或将值类型包装在结构/类中,您将无法做到这一点。

来自MSDN

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

于 2012-09-04T14:45:06.117 回答
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

于 2012-09-04T14:47:33.967 回答
1

我不确定我是否完全理解你,但你可以将你的引用类型包含int在一些引用类型中,比如一个int[]length1或一个 1-tuple, Tuple<int>

所以:

var a = Tuple.Create(10);
var b = Tuple.Create(15);
a = b;

由于Tuple<>is immutable,因此这是毫无意义的。

但随后:

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无缘无故地使用。

于 2012-09-04T15:29:03.743 回答
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 的返回值不允许 ref字段。

最接近的方法是使用 lambdas 和匿名方法进行捕获。在这里,locals 不是存储在堆栈中,而是存储在堆中,以允许它们与生命捕获的 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);
  }
}

我需要一些原子性保证,你可能不需要,同样可能不需要一些你确实需要的东西,但它可以很好地作为一种能够处理int不同类中相同值的方法。

如果你不介意每次都检查一个属性,你可以做一个更通用的版本。

public class TypedBox<T> where T : struct
{
  public T Value;
}

公开一个字段有相当大的缺点(这就是为什么我们通常从不这样做),但大多数都不适用于这种情况(我们希望能够从外部完全操纵它),这意味着你甚至可以通过作为Value一个refout参数。

于 2012-09-04T23:40:04.263 回答