Предоставить метод расширения для типов, производных от типа this - PullRequest
0 голосов
/ 15 декабря 2018

Я прочитал этот похожий вопрос Я не ожидаю того же поведения, что и ОП, и я его не совсем понимаю, но у меня есть использование для защищенных членов внутри производных классов.

В Почему ключевое слово "this" требуется для вызова метода расширения из расширенного класса Эрик Липперт писал:

... Если вы находитесь вСценарий, в котором вы используете метод расширения для типа в этом типе, тогда у вас есть доступ к исходному коду.Почему вы сначала используете метод расширения?

... Учитывая эти два момента, бремя больше не ложится на разработчика языка, объясняющего, почему эта функция не существует.Теперь вам приходится объяснять, почему это так.С объектами связаны огромные затраты.

...

Поэтому я попытаюсь объяснить, почему я ожидал поведение и его пример использования.

Функция:

  • Программист может получить доступ к защищенному элементу объекта this внутри метода расширения.
  • Когда защищенный член используется в методе расширения, вы можете использовать метод только внутри классов, производных от типа this объекта.
  • Защищенный метод расширения можно вызывать только с thisАргумент объект, который является тем же объектом, который доступен по ключевому слову this в методе вызывающей стороны.

Сценарий использования в реальной жизни:

Я играю с созданием пользовательского редактора Visual Studio на основе 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);
        }
    }
}

Мне нравится фокусироваться на вещах, которыена самом деле важно, поэтому я написал вспомогательный метод, чтобы упростить такие методы.Например:

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

}

Это хорошо работает, но я также хотел бы разместить метод with внутри VSServiceQueryHelper как расширение, чтобы в любое время я мог бы расширить WindowsPane, я мог бы просто поместить using static com.audionysos.vsix.utils.VSServiceQueryHelper; вверху и использовать метод with, которыйуже реализовано.

Проблема:

Я не могу сделать метод with расширением, поскольку используемый им метод GetService является защищенным членомWindowsPane, который является базовым типом моего класса.Поэтому теперь мне нужно поместить реализацию with в каждый класс, который расширяет WindowPane, и это нарушает правило никогда не повторяться: /

1 Ответ

0 голосов
/ 16 декабря 2018

Простым решением было бы создание базового класса, содержащего метод With.

Если это слишком обременительно, вы также можете реализовать это с помощью Reflection для вызова метода 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.
    }
}

Но вы спрашиваете: «Разве не разрешен доступ к членам базового класса из производного класса?»Нет, это на самом деле не правило.Правило состоит в том, что вы можете получить доступ к членам базового класса из производного класса только через ссылку на производный класс, а не из какой-либо ссылки на базовый класс напрямую.Подробнее об этом здесь .Ваше предложение сводится к тому, чтобы разрешить подобные вещи для еще больших методов класса (т. Е. Методов, которые какой-то другой автор библиотеки объявляет как методы расширения).Эрик Липперт писал об этой проблеме ( здесь и здесь ) и в прошлом.Поскольку вызовы между иерархиями блокируются CLR, я не ожидаю, что подобное предложение будет реализовано в ближайшее время.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...