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

C# 调用操作-确定它所属的实例是否为空,c#,.net,delegates,C#,.net,Delegates,我有一个将动作作为参数的方法。操作存储在队列中,并在特定资源可用时执行。在调用操作之前,我想检查它所属的实例是否为null 我用下面这个愚蠢的例子做了一个简单的测试。该操作在将invokee设置为null后成功调用,正如预期的那样,我在尝试访问null invokee上的属性时遇到了NullReferenceException。在运行时检查该操作时,没有任何提示我可以确定其实例是否为null的提示 我想我可以将动作和实例作为参数传入,并在调用之前测试实例是否为null。是否有可能测试一个空的被调

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

我用下面这个愚蠢的例子做了一个简单的测试。该操作在将invokee设置为null后成功调用,正如预期的那样,我在尝试访问null invokee上的属性时遇到了NullReferenceException。在运行时检查该操作时,没有任何提示我可以确定其实例是否为null的提示

我想我可以将动作和实例作为参数传入,并在调用之前测试实例是否为null。是否有可能测试一个空的被调用者,或者这只是我设计糟糕的一个例子

更新:

我加了一句话

如果(explosion.Target!=null)

,以检查空目标,但在我的示例中它仍在调用委托

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();
        }
    }
public void LetsDoThis()
{
var bazooka=新的bazooka();
var rocketLauncher=新rocketLauncher();
火箭筒。锁和装载(火箭发射器。爆炸);
rocketLauncher=null;
火箭筒火灾;
bool wastthis completelyawesome=火箭发射器;
}
公共级火箭发射器
{
公共空间爆炸()
{
bool-stuffIsBlowingUp=true;
}
公共场所这是令人担忧的
{
得到
{
返回true;
}
}
}
公共级火箭筒
{
私有列表=新列表();
公共无效锁定和加载(操作加载它)
{
this.exploations.Add(loadIt);
}
公共场所火灾()
{
foreach(爆炸中的动作爆炸)
if(explosion.Target!=null)
explosion.Invoke();
}
}

使用
目标
属性检查:

if(yourAction.Target != null) {
  //...
}
任何
委托
类型都有一个名为
目标
的属性,因此您也可以将此属性用于其他类型的委托

更新:事实上,当您使用
操作
包装对象的某个方法时,该对象永远不会被释放,这意味着在这种情况下不能抛出
NullReferenceException
除非包装另一个对象的另一个方法,并且该方法与null对象有关。

这不起作用

操作
根本不关心从中获得它的原始引用变量,它复制了引用值,因此有自己的引用

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

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

因此,要获得
null
目标,您需要从静态方法获取委托,请尝试以下方法:

您可以看到,委托使用以下代码携带自己的实例:

这里的输出将是

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
属性根本不知道或不关心从中获取委托的原始变量。将原始变量中的引用值复制到委托中,之后对该变量所做的操作没有任何区别

所以基本上:

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

现在,如果您真的想让委托依赖于变量,并关心它现在拥有的值,当您开始调用它时,该怎么办
MethodReference = rocketLauncher.BlowStuffUp;
object target = rocketLauncher;
// wrap this into a delegate
int a = 10;
int b = a;
a = 0;
// b is still 10
bazooka.LockAndLoad(delegate
{
    if (rocketLauncher != null)
        rocketLauncher.BlowStuffUp();
});
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
bazooka.Unload(rocketLauncher.BlowStuffUp);
rocketLauncher = null;
public void Unload(Action unloadIt)
{
    if (explosions.Contains(unloadIt))
        explosions.Remove(unloadIt);
}