Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从静态方法(扩展方法)内的匿名事件处理程序取消订阅_C#_Event Handling_Extension Methods_Static Methods - Fatal编程技术网

C# 从静态方法(扩展方法)内的匿名事件处理程序取消订阅

C# 从静态方法(扩展方法)内的匿名事件处理程序取消订阅,c#,event-handling,extension-methods,static-methods,C#,Event Handling,Extension Methods,Static Methods,我有一个扩展方法来订阅实现INotifyPropertyChanged的对象的PropertyChanged事件 我希望事件只触发一次。不多了 这是我的方法 public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action) { if (target == null) { return;

我有一个扩展方法来订阅实现
INotifyPropertyChanged
的对象的
PropertyChanged
事件

我希望事件只触发一次。不多了

这是我的方法

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    PropertyChangedEventHandler handler = (obj, e) =>
    {

        if (propertyName == e.PropertyName)
        {
            action();
        }

    };


    target.PropertyChanged -= handler;
    target.PropertyChanged += handler;

}
公共静态void OnPropertyChanged(此INotifyPropertyChanged目标,字符串propertyName,操作)
{
if(target==null)
{
返回;
}
PropertyChangedEventHandler处理程序=(对象,e)=>
{
if(propertyName==e.propertyName)
{
动作();
}
};
target.PropertyChanged-=处理程序;
target.PropertyChanged+=处理程序;
}
但它不起作用。我无法删除事件处理程序,因此每次调用此方法时都会触发事件

我尝试了一种不同的方法。与其使用注释性方法,不如使用更传统的方法,如:

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    target.PropertyChanged -= target_PropertyChanged;
    target.PropertyChanged += target_PropertyChanged;

}

static void target_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //do stuff here
    }
class DelegateContainer
{
    public DelegateContainer(Action theAction, string propName)
    {
         TheAction = theAction;
         PopertyName = propName;
    }

    public Action TheAction { get; private set; }
    public string PropertyName { get; private set; }

    public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        if(PropertyName == e.PropertyName)
            TheAction();
    }
}
private static DelegateContainer currentContainer;

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
   if (target == null)
   {
       return;
   }

   if(currentContainer != null)         
       target.PropertyChanged -= currentContainer.PropertyChangedHandler;

   currentContainer = new DelegateContainer(action, propertyName);
   target.PropertyChanged += currentContainer.PropertyChangedHandler;
}
公共静态void OnPropertyChanged(此INotifyPropertyChanged目标,字符串propertyName,操作)
{
if(target==null)
{
返回;
}
target.PropertyChanged-=target\u PropertyChanged;
target.PropertyChanged+=target\u PropertyChanged;
}
静态无效目标\u PropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
//在这里做事
}
而且效果很好。事件只触发一次,但我还需要Action参数。我不能用这种方法

有解决这个问题的方法吗?静态方法中的匿名方法有什么奇怪的地方吗


提前感谢。

这是将匿名方法用作事件处理程序的限制。它们不能像普通方法(技术上是通过方法组转换自动创建的委托实例)那样被删除,因为匿名方法被编译到编译器生成的容器类中,并且每次都会创建该类的新实例

为了保留action参数,可以创建一个容器类,其中包含事件处理程序的委托。该类可以在您正在使用的另一个类的内部声明为private,也可以在“Helpers”命名空间中声明为内部。它看起来像这样:

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    target.PropertyChanged -= target_PropertyChanged;
    target.PropertyChanged += target_PropertyChanged;

}

static void target_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //do stuff here
    }
class DelegateContainer
{
    public DelegateContainer(Action theAction, string propName)
    {
         TheAction = theAction;
         PopertyName = propName;
    }

    public Action TheAction { get; private set; }
    public string PropertyName { get; private set; }

    public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        if(PropertyName == e.PropertyName)
            TheAction();
    }
}
private static DelegateContainer currentContainer;

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
   if (target == null)
   {
       return;
   }

   if(currentContainer != null)         
       target.PropertyChanged -= currentContainer.PropertyChangedHandler;

   currentContainer = new DelegateContainer(action, propertyName);
   target.PropertyChanged += currentContainer.PropertyChangedHandler;
}
然后,在类中创建并存储对容器的引用。您可以创建一个静态成员
currentContainer
,然后按如下方式设置处理程序:

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    target.PropertyChanged -= target_PropertyChanged;
    target.PropertyChanged += target_PropertyChanged;

}

static void target_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //do stuff here
    }
class DelegateContainer
{
    public DelegateContainer(Action theAction, string propName)
    {
         TheAction = theAction;
         PopertyName = propName;
    }

    public Action TheAction { get; private set; }
    public string PropertyName { get; private set; }

    public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        if(PropertyName == e.PropertyName)
            TheAction();
    }
}
private static DelegateContainer currentContainer;

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
   if (target == null)
   {
       return;
   }

   if(currentContainer != null)         
       target.PropertyChanged -= currentContainer.PropertyChangedHandler;

   currentContainer = new DelegateContainer(action, propertyName);
   target.PropertyChanged += currentContainer.PropertyChangedHandler;
}
私有静态委托容器currentContainer;
公共静态void OnPropertyChanged(此INotifyPropertyChanged目标,字符串propertyName,操作)
{
if(target==null)
{
返回;
}
如果(currentContainer!=null)
target.PropertyChanged-=currentContainer.PropertyChangedHandler;
currentContainer=新的DelegateContainer(操作,属性名称);
target.PropertyChanged+=currentContainer.PropertyChangedHandler;
}

从技术上讲,这与您尝试取消订阅的匿名方法不同。NET每次调用
OnPropertyChanged
时都会创建该方法的新实例。这就是退订不起作用的原因

如果从事件处理程序本身中取消订阅,则可以让第一个示例正常工作

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    // Declare the handler first, in order to create
    // a concrete reference that you can use from within
    // the delegate
    PropertyChangedEventHandler handler = null;  
    handler = (obj, e) =>
    {
        if (propertyName == e.PropertyName)
        {
            obj.PropertyChanged -= handler; //un-register yourself
            action();
        }

    };
    target.PropertyChanged += handler;
}
公共静态void OnPropertyChanged(此INotifyPropertyChanged目标,字符串propertyName,操作)
{
if(target==null)
{
返回;
}
//首先声明处理程序,以便创建
//可以从内部使用的具体参考
//代表
PropertyChangedEventHandler处理程序=null;
处理程序=(对象,e)=>
{
if(propertyName==e.propertyName)
{
obj.PropertyChanged-=handler;//取消自己的注册
动作();
}
};
target.PropertyChanged+=处理程序;
}
上面的代码充当“一次完成”事件处理程序。您可以注册无限数量的这些文件,每个文件在注销之前只执行一次


请记住,如果在短时间内连续跨多个线程引发事件,则这些处理程序中的一个可能会执行多次。为了防止这种情况,您可能需要创建一个静态映射对象实例到“锁定对象”,并添加一些sentry代码以确保处理程序只执行一次。但是,这些实现细节似乎有点超出了您当前编写的问题的范围。

这是不同的行为。这只允许触发一次,而OP的代码试图确保只有一个处理程序(但可以多次触发)。哦,请注意,如果在短时间间隔内从多个线程触发此事件,则它可能会运行多次。所有优点都是好的。可能需要对象的静态字典->处理程序,以将对象的每个实例限制为单个事件处理程序。如果PropertyChanged是从多个线程触发的,则您会遇到更大的问题。重新阅读原始问题,OP想要的仍然是一个“一次完成”的事件处理程序,它会自动取消订阅。可能需要一个将对象实例映射到“锁定对象”的静态字典,以确保处理程序只在多个线程中执行一次,还需要一些“哨兵”代码来检查处理程序是否已经执行。从技术上讲,这不一定是一种不同的方法。如果同一
propertyName
的不同
string
实例出于任何原因被使用,那么它必须是一个不同的方法。+1对于容器类的概念,但我认为这是不必要的复杂:一个
Dictionary
可以为每个属性名保存一个委托。(是的,这是一种不同的容器类。)是的,我只是想介绍一个通用的解决方案。我想暗示的是,带有闭包的匿名方法会生成一个containe