如何正确取消订阅活动,并确保现在不调用被叫方法?
我的问题在于这种代码:
public class MyClassWithEvent
{
public event EventHandler MyEvent;
public int Field;
}
public class MyMainClass
{
private MyClassWithEvent myClass;
public void Start()
{
myClass.MyEvent += new EventHandler(doSomething);
}
public void Stop()
{
myClass.MyEvent -= new EventHandler(doSomething);
myClass = null;
}
private void doSomething()
{
myClass.Field = 42;
}
}
如果在执行myClass = null
时调用doSomething
,则指令myClass.Field = 42
会引发错误,因为myClass为空。
在设置doSomething
之前,如何确定myClass = null
没有执行?
修改
其他例子:
public void Stop()
{
myClass.MyEvent -= new EventHandler(doSomething);
// Can I add a function here to be sure that doSomething is not running ?
myClass.Field = 101;
}
在这种情况下,我不确定myClass.Field
是42还是101。
EDIT2:
显然我的问题并不像我想象的那么简单。我将解释我的确切案例。
我的代码是:
public class MyMainClass
{
object camera;//can be type uEye.Camera or DirectShowCamera
bool isRunning = false;
public void Start()
{
if (camera is uEye.Camera)
{
camera.EventFrame += new EventHandler(NewFrameArrived);
}
else if (camera is DirectShowCamera)
{
//other init
}
isRunning = true;
}
public void Stop()
{
if (camera is uEye.Camera)
{
camera.EventFrame -= new EventHandler(NewFrameArrived);
camera.exit;
}
else if (camera is DirectShowCamera)
{
//other stop
}
isRunning = false;
}
public void ChangeCamera(object new camera)
{
if (isRunning)
Stop()
camera = new camera();
}
void NewFrameArrived(object sender, EventArgs e)
{
uEye.Camera Camera = sender as uEye.Camera;
Int32 s32MemID;
Camera.Memory.GetActive(out s32MemID);
lock (_frameCameralocker)
{
if (_frameCamera != null)
_frameCamera.Dispose();
_frameCamera = null;
Camera.Memory.ToBitmap(s32MemID, out _frameCamera);
}
Dispatcher.Invoke(new Action(() =>
{
lock (_frameCameralocker)
{
var bitmapData = _frameCamera.LockBits(
new System.Drawing.Rectangle(0, 0, _frameCamera.Width, _frameCamera.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, _frameCamera.PixelFormat);
if (_frameCamera.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
DeviceSource = System.Windows.Media.Imaging.BitmapSource.Create(
bitmapData.Width, bitmapData.Height, 96, 96, System.Windows.Media.PixelFormats.Gray8, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
}
_frameCamera.UnlockBits(bitmapData);
if (OnNewBitmapReady != null)
OnNewBitmapReady(this, null);
}
}));
}
}
当我将相机从uEye更改为directshow时,我在DeviceSource = System.Windows.Media.Imaging.BitmapSource.Create
(方法NewFrameArrived
)中有一个AccessViolationException,因为我尝试从已退出的相机创建BitmapSource
答案 0 :(得分:2)
从您更新的问题中,您唯一需要做的就是锁定与Stop()
Dispatcher.Invoke
操作
public void Stop()
{
lock(_frameCameralocker)
{
if (camera is uEye.Camera)
{
camera.EventFrame -= new EventHandler(NewFrameArrived);
camera.exit;
}
else if (camera is DirectShowCamera)
{
//other stop
}
isRunning = false;
}
}
这将确保在您创建新相机之前所有NewFrameArrived
来电已完成或尚未开始。然后在调度程序内部检查您是否正在运行,以防万一在启动并完成Stop()
调用之前帧已排队。
Dispatcher.Invoke(new Action(() =>
{
lock (_frameCameralocker)
{
if(!isRunning)
return;
var bitmapData = _frameCamera.LockBits(
new System.Drawing.Rectangle(0, 0, _frameCamera.Width, _frameCamera.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, _frameCamera.PixelFormat);
if (_frameCamera.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
DeviceSource = System.Windows.Media.Imaging.BitmapSource.Create(
bitmapData.Width, bitmapData.Height, 96, 96, System.Windows.Media.PixelFormats.Gray8, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
}
_frameCamera.UnlockBits(bitmapData);
if (OnNewBitmapReady != null)
OnNewBitmapReady(this, null);
}
}));
答案 1 :(得分:1)
对Monitor好用吗?
这个想法是你使用一个锁来确保你没有在(几乎)同时两次使用相同的资源:
public class MyClassWithEvent
{
public event EventHandler MyEvent;
public int Field;
}
public class MyMainClass
{
private MyClassWithEvent myClass;
private object mylock;
public void Start()
{
myClass.MyEvent += new EventHandler(doSomething);
}
public void Stop()
{
myClass.MyEvent -= new EventHandler(doSomething);
Monitor.Enter(mylock); //If somebody else already took the lock, we will wait here
myClass = null;
Monitor.Exit(mylock); //We release the lock, so others can access it
}
private void doSomething()
{
Monitor.Enter(mylock);
if myClass != null
{
myClass.Field = 42;
}
Monitor.Exit(mylock);
}
}
修改强>
根据评论,Lock会更好用(actually a short-hand for Monitor):
public class MyClassWithEvent
{
public event EventHandler MyEvent;
public int Field;
}
public class MyMainClass
{
private MyClassWithEvent myClass;
private object mylock;
public void Start()
{
myClass.MyEvent += new EventHandler(doSomething);
}
public void Stop()
{
myClass.MyEvent -= new EventHandler(doSomething);
lock (mylock) //If somebody else already took the lock, we will wait here
{
myClass = null;
} //We release the lock, so others can access it
}
private void doSomething()
{
lock(mylock)
{
if myClass != null
{
myClass.Field = 42;
}
}
}
}
答案 2 :(得分:0)
相反
softwareupdate -l
待办事项
myClass.Field = 42;
答案 3 :(得分:0)
使用C#6 ......
myClass?.Field = 42;