如何正确重写 ISupportInitialize 实现

发布于 2024-11-05 08:50:07 字数 331 浏览 0 评论 0原文

我陷入了 ISupportInitialize 的困境。

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

问题是,该接口是从微软显式实现的,因此我无法调用基类的 BeginInit()EndInit() 方法,也无法覆盖它。

仅实现新方法就会阻止基类正常工作,因为这些方法不会被调用,不是吗?

任何提示表示赞赏...

I'm stuck at a point with ISupportInitialize.

We use a custom class inherited from System.Windows.Form.BindingSource.
Now we need to enhance the ISupportInitialize implementation from our inherited class to check for controls/components on the form automatically, since manual work should be minimized where possible.

The problem is, that the interface is explicitly implemented from microsoft and so i cannot call the BeginInit() and EndInit() Methods of the base class nor override it.

Just implementing new methods would stop the base class from working as usual since the methods will not get called, will they?

any hint appreciated...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

谎言 2024-11-12 08:50:07

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

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

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]);
            }
        }
    }
}

This is a pretty interesting question!

In my opinion the only way to call the explicitely implemented methods in the base class is to use reflection. Something like this should do the job (untested):

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]);
            }
        }
    }
}
晚风撩人 2024-11-12 08:50:07

您无法覆盖它,并且使用反射器查看源代码告诉我您将无法在这里做太多事情...您可以尝试对所有接口使用装饰器模式,但无论哪种方式您都可能会陷入困境,因为此类由框架使用,并且您无法控制它的用法。

无论如何,如果您想尝试一下,想法如下:

  • 创建一个实现 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)
}

同样,这将依赖所有客户端代码(也在框架中)来遍历接口,这是我不会花钱的。

You can not override it, and looking at the source with reflector tells me you won't be able to do much here... You could try to use the decorator pattern for all the interfaces, but chances are you will get stuck either way, since this class is used by the framework and you don't control it's usage.

Anyway, if you want to try it, here's the idea:

  • create a class that implements all interfaces from BindingSource
  • keep an actual BindingSource instance as a private field
  • implement all interface methods by simply forwarding calls to the private field
  • for the methods you want to extend, add custom logic before or after calling the original method

Something like this:

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)
}

Again, this would rely on all client code (also in the framework) to go through the interfaces, something I would not put money on.

愿得七秒忆 2024-11-12 08:50:07

我使用几个扩展方法来实现此目的:

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()

I use a couple of extension methods to achieve this:

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;
    }
}

In your class, explicitly implement ISupportInitialize and call the appropriate extension method, for example:

public class MyBindingSource
    : BindingSource,
      ISupportInitialize
{
    void ISupportInitialize.BeginInit()
    {
        this.InvokeBaseBeginInit();

        // More begin init logic
    }

    void ISupportInitialize.EndInit()
    {
        this.InvokeBaseEndInit();

        // More end init logic
    }
}

You don't have to explicitly implement both methods, because the base class already implements it, so if you're only interested in adding logic after initialisation, you can leave out BeginInit().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文