代码之家  ›  专栏  ›  技术社区  ›  Paweł Audionysos

为从“this”类型派生的类型提供扩展方法

  •  2
  • Paweł Audionysos  · 技术社区  · 7 年前

    我读过 this similar question 我不期望和OP有相同的行为,我也不太了解他,但是我有一个派生类中受保护成员的用法。

    Why is the 'this' keyword required to call an extension method from within the extended class 埃里克·利珀特写道:

    …如果您在使用扩展方法的场景中 对于该类型中的类型,则您确实可以访问源 代码。那么,为什么首先要使用扩展方法呢?

    …鉴于这两点,负担不再落在语言上。 设计人员解释功能不存在的原因。现在它落在 你来解释一下为什么。功能具有巨大的相关成本 和他们在一起。

    所以我将尝试解释为什么我会期望一个行为,这是一个使用示例。

    特点:

    • 程序员可以访问的受保护成员 this 扩展方法内的对象。
    • 当在扩展方法中使用受保护的成员时,只能在从类型 对象。
    • 只能使用 参数对象,该对象与 调用方方法中的关键字。

    实际使用场景:

    我正在尝试创建一个基于 WPFDesigner_XML 例子。 目前,我正试图用以下签名在课堂上解决问题:

    public sealed class EditorPane : WindowPane, IOleComponent, IVsDeferredDocView, IVsLinkedUndoClient
    {...} 
    

    很多方法都使用这样的服务:

    void RegisterIndependentView(bool subscribe)
    {
        IVsTextManager textManager = (IVsTextManager)GetService(typeof(SVsTextManager));
    
        if (textManager != null)
        {
            if (subscribe)
            {
                textManager.RegisterIndependentView(this, _textBuffer);
            }
            else
            {
                textManager.UnregisterIndependentView(this, _textBuffer);
            }
        }
    }
    

    我喜欢把重点放在实际重要的事情上,所以我编写了helper方法来简化这些方法。例如:

    private void RegisterIndependentView(bool subscribe) {
        if (with(out IVsTextManager tm)) return;
        if (subscribe) tm.RegisterIndependentView(this, _textBuffer);
        else tm.UnregisterIndependentView(this, _textBuffer);
    }
    

    这个 with 方法如下:

    private bool with<T>(out T si) {
        si = (T)GetService(getServiceQueryType<T>());
        return si == null ? true : false;
    }
    

    我放置 getServiceQueryType<T>() 在静态类中:

    public static class VSServiceQueryHelper {
    
        public static Type getServiceQueryType<T>() {
            var t = typeof(T);
            if (!serviceQueryTypesMap.ContainsKey(t)) throw new Exception($@"No query type was mapped in ""{nameof(serviceQueryTypesMap)}"" for the ""{t.FullName}"" interface.");
            return serviceQueryTypesMap[t];
        }
    
        private static Dictionary<Type, Type> serviceQueryTypesMap = new Dictionary<Type, Type>() {
            { typeof(IVsUIShellOpenDocument), typeof(SVsUIShellOpenDocument) },
            { typeof(IVsWindowFrame), typeof(SVsWindowFrame) },
            { typeof(IVsResourceManager), typeof(SVsResourceManager) },
            { typeof(IVsRunningDocumentTable), typeof(SVsRunningDocumentTable) },
            { typeof(IMenuCommandService), typeof(IMenuCommandService) },
            { typeof(IVsTextManager), typeof(SVsTextManager) },
        };
    
    }
    

    这很好,但我也希望 具有 内部方法 VSServiceQueryHelper 作为延期,所以任何时候我都会延期 WindowsPane 我可以把 using static com.audionysos.vsix.utils.VSServiceQueryHelper; 在顶部使用 具有 已实现的方法。

    问题:

    我做不到 具有 方法扩展,因为 GetService 它使用的方法是 窗玻璃 这是我班的基本类型。所以现在我需要 具有 在每个扩展的类中实现 WindowPane 这打破了永不重复的原则:/

    1 回复  |  直到 7 年前
        1
  •  1
  •   Mike Zboray    7 年前

    一个简单的解决方案是创建一个包含with方法的基类。

    如果这太繁琐,那么您还可以使用反射来从扩展方法调用GetService方法来实现这一点。实际上,我们可以为它创建一个委托,以确保多次调用的开销最小。

    internal static class WindowPaneExtensions
    {
        private static readonly Func<WindowPane, Type, object> WindowPaneGetService = CreateWindowPaneGetService();
    
        public static bool With<T>(this WindowPane pane, out T service)
        {
            service = (T)WindowPaneGetService(pane, GetServiceQueryType<T>());
            return service != null;
        }
    
        private static Func<WindowPane, Type, object> CreateWindowPaneGetService()
        {
            var method = typeof(WindowPane).GetMethod("GetService", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(Type) }, null);
            var del = (Func<WindowPane, Type, object>)method.CreateDelegate(typeof(Func<WindowPane, Type, object>));
            return del;
        }
    }
    

    我认为你允许某些扩展方法访问受保护的成员的建议是不可取的。例如,不允许以下内容:

    public class MyPane : WindowPane
    {
        public static void Test(WindowPane p)
        {
             var service = p.GetService(typeof(Service));
             // etc.
        }
    }
    

    但您会问,“它不允许访问派生类中的基类成员吗?”不,这不是规则。规则是,只能通过对派生类的引用访问派生类中的基类成员,而不能直接从任何基类引用访问。更多细节 this here . 您的建议相当于允许对更大的类方法(即其他库作者声明为扩展方法的方法)使用这种方法。埃里克·利珀特写过这个问题( here here )过去也是。由于跨层次结构调用被clr阻塞,因此我不希望类似于此建议的内容很快得到实现。