更改参考参数中的引用

时间:2013-05-23 05:34:17

标签: c#

我正在尝试将数组传递给方法。该数组包含需要为空的对象。该方法将简单地使循环中的每个对象为空。我需要这个反映在来电者身上。

示例代码(可以忽略代码优度和次要语法问题):

public class ABC
{
  ...
}

private void SomeMethod()
{
  var toBeNulledObj1 = new ABC();
  var toBeNulledObj2 = new ABC();
  var arrayOfNullableObjects = new ABC[]{toBeNulledObj1 ,toBeNulledObj2};
  NullingFunction(arrayOfNullableObjects);
}

private void NullingFunction(ABC[] arrayOfNullableObjects)
{
   for(int i = 0; i< arrayOfNullableObjects.Length ; i++)
     {
        arrayOfNullableObjects[i] = null;
     }
}

返回后,toBeNulledObj1&amp;虽然toBeNulledObj2现在有两个arrayOfNullableObjects个对象,但null不是null但保留其旧值。我意识到ref&amp; out仅适用于collection参数(此处,arrayOfNullableObjects甚至不需要ref)。我尝试将它们作为params而不是集合传递,但这也无济于事(参考和参数不能合并)。

问题:如何更改方法中对象集合中的每个/任何对象,以使调用者可以看到更改?我不是在改变收藏本身。请注意,我没有更改toBeNulledObj1的内容/成员,而是更改引用本身(无论是null还是新对象)。

4 个答案:

答案 0 :(得分:2)

解决方案#1:不安全代码

一种解决方案是使用不安全的代码。在使用它之前你必须要三思而后行,我不知道你是否对我的答案感到满意,但现在就是。

static private void SomeMethod()
{
    ABC toBeNulledObj1 = new ABC();
    ABC toBeNulledObj2 = new ABC();

    IntPtr[] arrayOfNullableObjects = new IntPtr[] { MakeReference(ref toBeNulledObj1), MakeReference(ref toBeNulledObj2) };
    NullingFunction(arrayOfNullableObjects);
}

static private void NullingFunction(IntPtr[] arrayOfNullableObjects)
{
    foreach (IntPtr reference in arrayOfNullableObjects)
        ClearReference(reference);
}

/// <summary>
/// Makes the reference to the reference value of a reference type.
/// </summary>
static unsafe private IntPtr MakeReference<T>(ref T value)
    where T: class
{
    TypedReference reference = __makeref(value);
    return *(IntPtr*)&reference;
}

/// <summary>
/// Clears the reference to a reference type, using a reference to that reference value.
/// </summary>
static unsafe private void ClearReference(IntPtr reference)
{
    if (sizeof(IntPtr) == 4)
        *((int*)reference) = 0;
    else
        *((long*)reference) = 0;
}

解决方案#2:匿名类

第二种解决方案可以通过使用保存数据的匿名类来完成。此匿名类中的字段已清除。缺点是你有第二个类,也应该清除对这个类的引用。 (这可以通过将ref添加到oNullingFunctiono添加到null来完成。)当然,您也可以使用预定义的类,但他的解决方案最接近你的OP中的代码。

public static void SomeMethod()
{
    var container = new
    {
        toBeNulledObj1 = new ABC(),
        toBeNulledObj2 = new ABC(),
    };

    NullingFunction(container);
}

private static void NullingFunction<T>(T container)
    where T : class
{
    if (container == null)
        return;
    foreach(FieldInfo f in container.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
        if (f.FieldType.IsClass)
            f.SetValue(container, null);
}

答案 1 :(得分:1)

当您说要将它们设置为null时,是否意味着要销毁该对象?

C#具有自动垃圾收集功能,因此只要对象超出范围(即没有其他对象引用它),垃圾收集器就会销毁它。

在上面的代码中,标签“tobeNulledObj1”仍然引用一个对象,并且在调用NullingFunction之前,您的数组也指向它。

调用NullingFunction后,仍然有一个指向对象的引用(即tobeNulledObj1)。如果将toNulledObj1设置为null,则垃圾收集器将收集它。

编辑:我第二个cheedep的问题 - 你究竟想要做什么?你希望你的变量在最后得到什么?

答案 2 :(得分:0)

如果函数A包含对变量的引用,即:

  var toBeNulledObj1 = new ABC();
  var toBeNulledObj2 = new ABC();

并没有将其传递给功能B:

private NullingFunction(ABC[] arrayOfNullableObjects)

然后有 nothing ,函数B可以更改toBeNulledObj1 / 2指向的引用。

由于ref不允许与params一起使用(如你所提到的):

 private void NullingFunction(ref params ABC[] arrayOfNullableObjects)
    {
        for (int i = 0; i < arrayOfNullableObjects.Length; i++)
        {
            arrayOfNullableObjects[i] = null;
        }
    }

可用的替代方法是创建重载,例如:

private void SomeMethod()
{
    var toBeNulledObj1 = new ABC();
    var toBeNulledObj2 = new ABC();
    NullingFunction(ref toBeNulledObj1, ref toBeNulledObj2);
    Console.ReadKey();
}

private void NullingFunction(ref ABC one)
{
    one = null;
}

private void NullingFunction(ref ABC one, ref ABC two)
{
    one = null;
    two = null;
}

答案 3 :(得分:0)

包装是否可以接受?

class Wrapped<T> where T : new() {
   private T val = new T();
   ...
   public void Nullify() { val = null; }
}

private void SomeMethod()
{
  var toBeNulledObj1 = new Wrapped<ABC>();
  var toBeNulledObj2 = new Wrapped<ABC>();
  var arrayOfNullableObjects = new Wrapped<ABC>[]{toBeNulledObj1 ,toBeNulledObj2};
  NullingFunction(arrayOfNullableObjects);

  Debug.Assert(toBeNulledObj1.Get() == null);
  // Or...
  Debug.Assert(toBeNulledObj1.IsDefined == false);
  // Or...
  Debug.Assert(toBeNulledObj1.IsNull == true);
}

private void NullingFunction(Wrapped<ABC>[] arrayOfNullableObjects)
{
   for(int i = 0; i< arrayOfNullableObjects.Length ; i++)
     {
        arrayOfNullableObjects[i].Nullify();
     }
}

(免责声明:手工编译的代码:)可能包含错误)

如果您需要它作为一般模式,您可以使用constaint where U: Wrapped<T>

制作NullingFunction参数(T,U)

如果您熟悉C ++,那么想法是为ref类型创建类似于Nullable的东西,或者看起来像智能指针的东西。

因此,包装器可以T Get()(或隐式转换为T)来获取值,IsDefined属性,等等。