调用动作-确定它所属的实例是否为空 - c#

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

我用下面的愚蠢的例子做了一个简单的测试。将被调用方设置为null后,成功调用了Action,并且按预期,当尝试访问null被调用方的属性时,我得到了NullReferenceException。在检查运行时的Action时,什么都没想到,我建议我可以确定其实例是否为空。

我想我可以将Action和实例作为参数传递,并在调用之前测试实例是否为null。是否有可能测试null调用,或者这仅仅是我设计不好的一种情况?

更新:

我加了线

如果(explosion.Target!= null)

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

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完全不关心您从中获取的原始参考变量,它会复制参考值,因此具有自己的参考。

请注意,这还意味着,即使您没有对原始对象的其他引用,只要您仍然有对委托的引用,它仍将不符合垃圾回收的条件。

.Target属性是指应在其上调用委托所引用的方法的实例,基本上是该方法的this“参数”。

因此,要具有null目标,您需要从静态方法获取委托,请在LINQPad中尝试以下操作:

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

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

您可以看到委托使用以下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的委托时发生了什么。

基本上,有两件事是“被抓住的”:

委托代表使用哪种方法
在其上调用该方法的对象实例

您可以将其比作以下代码:

MethodReference = rocketLauncher.BlowStuffUp;
object target = rocketLauncher;
// wrap this into a delegate

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

您对该变量执行的操作不会以任何方式更改该委托的值,它仍指向与以前相同的对象。基本上,它复制了该号码。该号码仍坐在代表内部。

这几乎完全相同:

int a = 10;
int b = a;
a = 0;
// b is still 10

因此,总而言之,委托的.Target属性完全不知道或关心您从其中获取委托的原始变量。将原始变量的参考值复制到委托中,之后对变量的操作完全没有区别。

所以基本上:

实例是对象,它存在于内存中的某个位置
引用基本上是它的地址,您可以将其视为一个数字
该变量是您可以存储该引用的地方

现在,如果您真的想让委托依赖于变量,并在调用它时关心它现在具有的值,该怎么办?

好吧,一种方法是这样做:

bazooka.LockAndLoad(delegate
{
    if (rocketLauncher != null)
        rocketLauncher.BlowStuffUp();
});

这将创建一个匿名方法,该方法将捕获变量本身,然后在该匿名方法内部,您可以在调用委托时显式检查变量具有的值。如果关于匿名方法的这部分没有意义,那么您应该在这里提出另一个问题(最好是在阅读了一些有关匿名方法,捕获变量并在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

LeetCode题解计算机为什么是基于二进制的?

可以是三进制么?二进制有什么好处?题解:为什么叫电子计算机?算盘应该没有二进制

LeetCode题解统计城市的所有灯泡

这个是我刚毕业的时候,一个真实的面试题,这是一个开放题。题目描述:想办法,将一个城市的所有灯泡数量统计出来。题解:费米估算法1、如果某个城市常驻人口有1000万2、假设每5人居住在一套房里,每套房有灯泡5只,那么住宅灯泡共有1000万只3、假设公众场所每10人共享一只灯泡,那么共有100万只4、主要的这两者相加就得出了1100万只当然实际上这是估算的,具体应…

LeetCode题解黑白圆盘

一个圆盘被涂上了黑白二色,两种颜色各占一个半圆。圆盘以一个未知的速度、按一个未知的方向旋转。你有一种特殊的相机可以让你即时观察到圆上的一个点的颜色。你需要多少个相机才能确定圆盘旋转的方向?题解:可以用一个相机即可

LeetCode题解圆上任取三点构成锐角三角形的概率

来自字节跳动的一道几何题题解:1/4

LeetCode题解深度优先遍历和回溯的关系?

深度优先遍历的范围更大还是回溯的范围更大?为什么?题解:我的理解是:dfs是回溯思想的一种体现- 回溯:是在整个搜索空间中搜索出可行解,在搜索过程中不断剪枝回退,这是回溯的思想,这个搜索空间并没有限制于特定的数据结构。- dfs:dfs是指特定的数据结构中如图,树(特殊的图)中搜索答案,范围限制在了特定的数据结构。个人拙见。