我已经读过this similar question,但我不希望看到与OP相同的行为,并且我不太了解他,但是我在派生类中使用了受保护成员。
埃里克·利珀特(Eric Lippert)在Why is the 'this' keyword required to call an extension method from within the extended class中写道:
...如果您使用的是扩展方法 对于该类型中的某个类型,则可以访问源 码。那么为什么要首先使用扩展方法?
...鉴于这两点,负担不再落在语言上 设计师解释为什么不存在该功能。现在落在 您解释为什么应该这样做。功能具有巨大的成本 和他们在一起。
...
所以我将尝试解释为什么我会期望一种行为及其用法示例。
功能:
this
对象的受保护成员。 this
对象的类型派生的类中使用该方法。this
参数对象来调用,该参数对象与调用方方法中的this
关键字可以访问的对象相同。 现实生活中的使用场景:
我正在根据WPFDesigner_XML示例创建一个Visual Studio自定义编辑器。 目前,我正在尝试使用以下签名在课堂上解决问题:
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
实现,这打破了永不重复自己的规则:/
答案 0 :(得分:1)
一个简单的解决方案是创建一个包含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.
}
}
但是您会问:“是否不允许从派生类访问基类成员?”不,这实际上不是规则。规则是,只能通过对派生类的引用来访问派生类中的基类成员,而不能直接从任何基类引用中访问基类成员。有关this here的更多详细信息。您的建议等于允许将这种事情用于更大的类方法(即,其他库作者声明为扩展方法的方法)。埃里克·利珀特(Eric Lippert)过去也曾写过关于此问题(here和here)的文章。由于CLR阻止了跨层次结构的调用,所以我不希望像这样的提议很快就会实现。