2

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

示例代码(代码的优点和小的语法问题可以忽略):

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&toBeNulledObj2不为空,但保留它们的旧值,尽管arrayOfNullableObjects现在有两个null对象。我意识到 ref & out 仅适用于集合参数(在这里,arrayOfNullableObjects它甚至不需要 ref)。我尝试将它们作为params集合而不是集合传递,但这也无济于事(参考和参数不能组合)。

问题:如何更改方法内对象集合中的每个/任何对象,以便调用者可以看到更改?我不会改变收藏本身。请注意,我不会更改toBeNulledObj1引用本身的内容/成员(更改为 null 或新对象)。

4

4 回答 4

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:匿名类

第二种解决方案可以通过使用保存您的数据的匿名类来完成。此匿名类中的字段被清除。一个缺点是你有第二个类,并且对这个类的引用也应该被清除。(这可以通过添加reftooNullingFunctionset oto 来完成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);
}
于 2013-05-23T06:52:36.620 回答
1

当您说要将它们设置为null时,您的意思是要销毁对象吗?

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

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

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

编辑:我第二个 cheedep 的问题 - 你到底想做什么?你希望你的变量最后保持什么?

于 2013-05-23T05:47:08.307 回答
0

如果函数 A 持有对变量的引用,即:

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

并且不会将其传递给函数 B:

private NullingFunction(ABC[] arrayOfNullableObjects)

那么函数 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;
}
于 2013-05-23T05:49:03.020 回答
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();
     }
}

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

如果您需要它作为一般模式,您可以使 NullingFunction 参数化(T,U),具有约束where U: Wrapped<T>

Nullable如果你熟悉 C++,这个想法是制作类似于for ref 类型的东西,或者看起来像智能指针的东西。

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

于 2013-05-24T06:54:05.273 回答