如何正确覆盖ISupportInitialize实现

本文关键字:实现 ISupportInitialize 覆盖 何正确 | 更新日期: 2023-09-27 17:54:45

我被困在ISupportInitialize的一个点上。

我们使用继承自System.Windows.Form.BindingSource的自定义类。现在我们需要从继承类中增强ISupportInitialize实现,以自动检查表单上的控件/组件,因为应该尽可能减少手工工作。

问题是,接口是从微软显式实现的,所以我不能调用基类的BeginInit()EndInit()方法,也不能覆盖它。

仅仅实现新的方法将阻止基类像往常一样工作,因为方法将不会被调用,他们会吗?

如何正确覆盖ISupportInitialize实现

这是一个非常有趣的问题!

在我看来,调用基类中显式实现的方法的唯一方法是使用反射。类似这样的代码应该可以完成工作(未经测试):

public class YourBindingSource : BindingSource, ISupportInitialize
{
    public void BeginInit()
    {
        Type baseType = base.GetType();
        InterfaceMapping interfaceMapping = baseType.GetInterfaceMap(typeof(ISupportInitialize));
        foreach(MethodInfo targetMethod in interfaceMapping.TargetMethods)
        {
            bool isBeginInitMethod = ...; // Could be determined by checking the name..
            if(isBeginInitMethod)
            {
                targetMethod.Invoke(this, new object[0]);
            }
        }
    }
}

您不能重写它,并且使用反射器查看源告诉我您将无法在这里做太多事情…你可以尝试对所有的接口使用装饰器模式,但是你很可能会被卡住,因为这个类是由框架使用的,而你不能控制它的使用。

无论如何,如果你想尝试一下,这是一个想法:

  • 创建一个类来实现BindingSource中的所有接口
  • 保留一个实际的BindingSource实例作为私有字段
  • 通过简单地将调用转发到私有字段
  • 来实现所有接口方法
  • 对于您想要扩展的方法,在调用原始方法之前或之后添加自定义逻辑

像这样:

public class MyBindingSource : ISupportInitialize // + all other interfaces
{
    private BindingSource _original; // to be set in constructor
    public void BeginInit()
    {
        // custom logic goes here
        _original.BeginInit();
    }
    // ... (all other forwarding implementations)
}

同样,这将依赖于所有客户端代码(也在框架中)通过接口,这是我不会投入资金的事情。

我使用两个扩展方法来实现这一点:

public static class ISupportInitializeHelper
{
    const string BEGIN_INIT = "System.ComponentModel.ISupportInitialize.BeginInit",
                 END_INIT   = "System.ComponentModel.ISupportInitialize.EndInit";
    public static void InvokeBaseBeginInit<T>(this T obj)
        where T : ISupportInitialize
    {
        var baseType   = typeof(T).BaseType;
        var methodInfo = GetBeginInitMethodInfo(baseType);
        if (methodInfo != null)
            methodInfo.Invoke(obj, null);
    }
    static Dictionary<Type, MethodInfo> s_beginInitCache = new Dictionary<Type, MethodInfo>();
    private static MethodInfo GetBeginInitMethodInfo(Type type)
    {
        MethodInfo methodInfo;
        if (!s_beginInitCache.TryGetValue(type, out methodInfo))
        {
            methodInfo = type.GetMethod(BEGIN_INIT,
                                        BindingFlags.NonPublic |
                                        BindingFlags.Instance);
            s_beginInitCache[type] = methodInfo;
        }
        return methodInfo;
    }
    public static void InvokeBaseEndInit<T>(this T obj)
        where T : ISupportInitialize
    {
        var baseType   = typeof(T).BaseType;
        var methodInfo = GetEndInitMethodInfo(baseType);
        if (methodInfo != null)
            methodInfo.Invoke(obj, null);
    }
    static Dictionary<Type, MethodInfo> s_endInitCache = new Dictionary<Type, MethodInfo>();
    private static MethodInfo GetEndInitMethodInfo(Type type)
    {
        MethodInfo methodInfo;
        if (!s_endInitCache.TryGetValue(type, out methodInfo))
        {
            methodInfo = type.GetMethod(END_INIT,
                                        BindingFlags.NonPublic |
                                        BindingFlags.Instance);
            s_endInitCache[type] = methodInfo;
        }
        return methodInfo;
    }
}

在你的类中,显式地实现ISupportInitialize并调用适当的扩展方法,例如:

public class MyBindingSource
    : BindingSource,
      ISupportInitialize
{
    void ISupportInitialize.BeginInit()
    {
        this.InvokeBaseBeginInit();
        // More begin init logic
    }
    void ISupportInitialize.EndInit()
    {
        this.InvokeBaseEndInit();
        // More end init logic
    }
}

你不需要显式地实现这两个方法,因为基类已经实现了它,所以如果你只对初始化后添加逻辑感兴趣,你可以省略BeginInit()