为从“this”类型派生的类型提供扩展方法
Provide extension method for types derived from `this` type
我读过 this similar question 我不希望有与 OP 相同的行为,我也不是很理解他,但我在派生的 classes 中有一个受保护成员的用法.
在 Why is the 'this' keyword required to call an extension method from within the extended class 中,Eric Lippert 写道:
... If you are in the scenario where you are using an extension method
for a type within that type then you do have access to the source
code. Why are you using an extension method in the first place then?
... Given those two points, the burden no longer falls on the language
designer to explain why the feature does not exist. It now falls on
you to explain why it should. Features have enormous costs associated
with them.
...
所以我将尝试解释为什么我会期望一个行为及其用法示例。
特征:
- 程序员可以在扩展方法中访问
this
对象的受保护成员。
- 当在扩展方法中使用受保护成员时,您只能使用从
this
对象类型派生的 classes 中的方法。
- 受保护的扩展方法只能使用
this
参数对象调用,该对象与调用方方法中的 this
关键字可访问的对象相同。
实际使用场景:
我正在尝试根据 WPFDesigner_XML 示例创建 Visual Studio 自定义编辑器。
目前,我正在尝试使用以下签名在 class 中解决问题:
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>()
放在静态 class:
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
的受保护成员,WindowsPane
是我的 [=76] 的基本类型=].所以现在我需要在扩展 WindowPane
的每个 class 中放置 with
实现,这打破了永不重复自己的规则:/
一个简单的解决方案是创建一个包含 With 方法的基 class。
如果这样太繁琐,那么您也可以使用反射从扩展方法中调用 GetService 方法来实现。事实上,我们可以为它创建一个委托,以确保多次调用 With 的开销最小。
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.
}
}
但是你问,"Isn't it allowed to access base class members from a derived class?"不,这实际上不是规则。规则是您只能通过对派生 class 的引用访问派生 class 中的基 class 成员,而不是直接从任何基 class 引用访问。过去也有关于 this here. Your proposal amounts to allowing this kind of thing for an even larger class methods (i.e. methods that some other library author declares to be extension methods). Eric Lippert has written about this issue (here and here) 的更多详细信息。由于跨层次调用被 CLR 阻止,我不希望像这个提议那样的东西很快得到实施。
我读过 this similar question 我不希望有与 OP 相同的行为,我也不是很理解他,但我在派生的 classes 中有一个受保护成员的用法.
在 Why is the 'this' keyword required to call an extension method from within the extended class 中,Eric Lippert 写道:
... If you are in the scenario where you are using an extension method for a type within that type then you do have access to the source code. Why are you using an extension method in the first place then?
... Given those two points, the burden no longer falls on the language designer to explain why the feature does not exist. It now falls on you to explain why it should. Features have enormous costs associated with them.
...
所以我将尝试解释为什么我会期望一个行为及其用法示例。
特征:
- 程序员可以在扩展方法中访问
this
对象的受保护成员。 - 当在扩展方法中使用受保护成员时,您只能使用从
this
对象类型派生的 classes 中的方法。 - 受保护的扩展方法只能使用
this
参数对象调用,该对象与调用方方法中的this
关键字可访问的对象相同。
实际使用场景:
我正在尝试根据 WPFDesigner_XML 示例创建 Visual Studio 自定义编辑器。 目前,我正在尝试使用以下签名在 class 中解决问题:
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>()
放在静态 class:
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
的受保护成员,WindowsPane
是我的 [=76] 的基本类型=].所以现在我需要在扩展 WindowPane
的每个 class 中放置 with
实现,这打破了永不重复自己的规则:/
一个简单的解决方案是创建一个包含 With 方法的基 class。
如果这样太繁琐,那么您也可以使用反射从扩展方法中调用 GetService 方法来实现。事实上,我们可以为它创建一个委托,以确保多次调用 With 的开销最小。
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.
}
}
但是你问,"Isn't it allowed to access base class members from a derived class?"不,这实际上不是规则。规则是您只能通过对派生 class 的引用访问派生 class 中的基 class 成员,而不是直接从任何基 class 引用访问。过去也有关于 this here. Your proposal amounts to allowing this kind of thing for an even larger class methods (i.e. methods that some other library author declares to be extension methods). Eric Lippert has written about this issue (here and here) 的更多详细信息。由于跨层次调用被 CLR 阻止,我不希望像这个提议那样的东西很快得到实施。