gpt4 book ai didi

c# - 调用一个 Action - 确定它所属的实例是否为空

转载 作者:行者123 更新时间:2023-12-03 19:09:35 25 4
gpt4 key购买 nike

我有一个将 Action 作为参数的方法。 Action 存储在队列中,并在特定资源可用时执行。在我调用一个 Action 之前,我想检查它所属的实例是否为空。

我用下面这个愚蠢的例子做了一个简单的测试。将被调用者设置为 null 后,Action 调用成功,正如预期的那样,在尝试访问 null 被调用者上的属性时,我得到了 NullReferenceException。在运行时检查 Action 时,我没有任何 react ,这表明我可以确定它的实例是否为空。

我想我可以将 Action 和实例作为参数传递,并在调用之前测试实例是否为空。是否可以测试空调用者,或者这只是我设计不良的一个案例?

更新:

我加了一行,

if (explosion.Target != null)

到 Bazooka.Fire(),检查空目标,但在我的示例中它仍在调用委托(delegate)。

public void LetsDoThis()
{
var bazooka = new Bazooka();
var rocketLauncher = new RocketLauncher();

bazooka.LockAndLoad(rocketLauncher.BlowStuffUp);

rocketLauncher = null;

bazooka.Fire();

bool wasThisCompletelyAwesome = rocketLauncher.ThisIsAwesome;
}

public class RocketLauncher
{
public void BlowStuffUp()
{
bool stuffIsBlowingUp = true;
}

public bool ThisIsAwesome
{
get
{
return true;
}
}
}

public class Bazooka
{
private List<Action> explosions = new List<Action>();

public void LockAndLoad(Action loadIt)
{
this.explosions.Add(loadIt);
}

public void Fire()
{
foreach (Action explosion in explosions)
if (explosion.Target != null)
explosion.Invoke();
}
}

最佳答案

这行不通。
Action绝不关心您从中获得它的原始引用变量,它会复制引用值,因此有自己的引用。

请注意,这也意味着只要您仍然有对委托(delegate)的引用,即使您没有对原始对象的其他引用,它仍然没有资格进行垃圾回收。
.Target属性是指应该调用委托(delegate)所指方法的实例,基本上是 this该方法的“参数”。

因此,有一个 null目标您需要从静态方法中获取委托(delegate),请在 LINQPad 中尝试此操作:

void Main()
{
Action a = Static.StaticMethod;
(a.Target == null).Dump();
}

public static class Static
{
public static void StaticMethod() { }
}

您可以看到委托(delegate)携带自己的实例与此 LINQPad代码:
void Main()
{
Dummy d = new Dummy { Name = "A" };
Action a = d.Method;

d = new Dummy { Name = "B" };
Action b = d.Method;

d = null;

a();
b();
}

public class Dummy
{
public string Name { get; set; }
public void Method()
{
Debug.WriteLine("Name=" + Name);
}
}

这里的输出将是
Name=A
Name=B

根据要求,让我澄清实例、引用和变量之间的区别。

当你构造一个对象实例时,像这样:
var rocketLauncher = new RocketLauncher();

你正在做的是调用一个称为构造函数的方法。此构造函数的返回值是对新构造对象的引用。基本上,它是一个指针,表示该对象现在所在的内存中的内存地址。如果它更容易理解这个答案的其余部分,你可以认为它只是一个数字。

此外,您声明了一个变量 rocketLauncher ,持有这个引用,这个数字。

请注意,对象与变量是分开的,它们是两个不同的项目。在内存的一个地方你有一个对象,在另一个地方你有一个包含对该对象的引用的变量,它是地址,那个数字。

所以当你这样做时:
bazooka.LockAndLoad(rocketLauncher.BlowStuffUp);

让我们稍微简化一下:
Action a = rocketLauncher.BlowStuffUp;
// bazooka.LockAndLoad(a);

让我们忘记调用 LockAndLoad 方法的部分,看看当我们“转换”方法时发生了什么 BlowStuffUp成为 Action 类型的委托(delegate).

基本上,有两件事被“捕获”:
  • 委托(delegate)的方法引用
  • 调用该方法的对象实例

  • 你可以把它比作下面的代码:
    MethodReference = rocketLauncher.BlowStuffUp;
    object target = rocketLauncher;
    // wrap this into a delegate

    这意味着您有两个对该对象的引用,一个位于 rocketLauncher 中。变量,另一个现在位于委托(delegate)内。

    您对该变量所做的操作不会以任何方式更改该委托(delegate)的值,它仍然指向与以前相同的对象。基本上它复制了那个号码。这个数字仍然在委托(delegate)内部。

    这与此几乎完全相同:
    int a = 10;
    int b = a;
    a = 0;
    // b is still 10

    因此,总而言之, .Target委托(delegate)的属性无论如何都不知道或关心您从中获得委托(delegate)的原始变量。将原始变量中的引用值复制到委托(delegate)中,之后对变量执行的操作完全没有区别。

    所以基本上:
  • 实例就是对象,它存在于内存中的某处
  • 引用基本上是它的地址,你可以把它看成一个数字
  • 该变量是您可以存储该引用的一个地方


  • 现在,如果你真的想让委托(delegate)依赖于变量,并关心它现在拥有的值,当你开始调用它时怎么办?

    好吧,一种方法是这样做:
    bazooka.LockAndLoad(delegate
    {
    if (rocketLauncher != null)
    rocketLauncher.BlowStuffUp();
    });

    这将创建一个匿名方法,该方法将捕获变量本身,然后在该匿名方法中,您可以在调用委托(delegate)时显式检查变量的值。如果关于匿名方法的这一部分没有意义,您应该在这里问另一个问题(理想情况下,在阅读一些关于匿名方法、捕获的变量并查看一些关于 SO 的现有问题之后)。

    要测试匿名方法,请在 LINQPad 中测试以下代码:
    void Main()
    {
    object dummy = new object();
    Action a = delegate
    {
    if (dummy != null)
    Debug.WriteLine("not null");
    else
    Debug.WriteLine("null");
    };

    a();
    dummy = null;
    a();
    }

    它将打印出:
    not null
    null

    关于c# - 调用一个 Action - 确定它所属的实例是否为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20163326/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com