检查几个程序集中的internal成员
2009-09-13 23:17 by 老赵, 12525 visits两个星期前我写了一篇文章谈到一个现象(或是感觉):我发现“类中的internal成员可能是一种坏味道”,原因在于违反了“单一职责”原则。然后谈了一般情况下遇到这种情况时一种可用的重构方式之一。结果自然是有人同意有人反对。不过刚才我忽然想到,不如检查一下微软的框架中internal成员的情况吧。微软最近几个框架都公开的源代码,社区反响不错,应该较为值得参考。
首先我准备了这样一段代码:
foreach (var m in Detect(typeof(Route).Assembly)) { Console.WriteLine(m.DeclaringType.Name + " - " + m.Name); }
这段代码会输出所有满足条件的方法,条件有三项:
- 属于公开类(即GetExprotedTypes方法获得的类型)。
- 直接定义在类中的方法(而不是通过继承得来的)。
- 修饰符为internal(即代码中的IsAssembly条件)。
首先,我们来查看ASP.NET Routing(System.Web.Routing.dll)中的internal成员:
foreach (var m in Detect(typeof(Route).Assembly)) { Console.WriteLine(m.Name); }
其结果是:
RequestContext - set_HttpContext RequestContext - set_RouteData
也就是说,除了RequestContext中的两个属性的set方法,没有其他internal的成员。但是从.NET Reflector的代码分析来看,这两个方法根本没有被RequestContext以外的类型使用过:
也就是说,即使我们把这两个成员设为private也没有任何问题。
接下来看一个大一些的框架:ASP.NET MVC(System.Web.Mvc.dll)。结果如下:
ValidateAntiForgeryTokenAttribute - get_Serializer ValidateAntiForgeryTokenAttribute - set_Serializer AjaxOptions - ToJavascriptString HtmlHelper - get_Serializer HtmlHelper - set_Serializer HtmlHelper - EvalString HtmlHelper - EvalBoolean HtmlHelper - GetModelStateValue HtmlHelper - RenderPartialInternal ValueProviderDictionary - get_Dictionary ReflectedActionDescriptor - get_DispatcherCache ReflectedActionDescriptor - set_DispatcherCache DefaultModelBinder - BindComplexElementalModel DefaultModelBinder - BindComplexModel DefaultModelBinder - BindSimpleModel DefaultModelBinder - UpdateCollection DefaultModelBinder - UpdateDictionary OutputCacheAttribute - get_CacheSettings Controller - get_RouteCollection Controller - set_RouteCollection ControllerActionInvoker - get_DescriptorCache ControllerActionInvoker - set_DescriptorCache MultiSelectList - GetListItems RedirectToRouteResult - get_Routes RedirectToRouteResult - set_Routes DefaultControllerFactory - get_BuildManager DefaultControllerFactory - set_BuildManager DefaultControllerFactory - get_ControllerBuilder DefaultControllerFactory - set_ControllerBuilder DefaultControllerFactory - get_ControllerTypeCache DefaultControllerFactory - set_ControllerTypeCache MvcHandler - get_ControllerBuilder MvcHandler - set_ControllerBuilder ViewMasterPage - get_ViewPage ViewUserControl - get_ViewPage WebFormView - get_BuildManager WebFormView - set_BuildManager WebFormViewEngine - get_BuildManager WebFormViewEngine - set_BuildManager
哗,好长一个列表。不过看到这里我觉得似乎有些不太对劲儿,为什么基本上都是些属性?因为一般来说,属性不代表一个类的“行为”,所以我打算忽略掉所有的属性。因此,我把Detect方法修改为:
public static IEnumerable<MethodInfo> Detect(Assembly assembly) { foreach (var type in assembly.GetExportedTypes()) { var methods = type.GetMethods( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.DeclaredOnly); foreach (var m in methods) { if (m.IsAssembly && !m.IsSpecialName) { yield return m; } } } }
于是剩下的方法还有:
AjaxOptions - ToJavascriptString HtmlHelper - EvalString HtmlHelper - EvalBoolean HtmlHelper - GetModelStateValue HtmlHelper - RenderPartialInternal DefaultModelBinder - BindComplexElementalModel DefaultModelBinder - BindComplexModel DefaultModelBinder - BindSimpleModel DefaultModelBinder - UpdateCollection DefaultModelBinder - UpdateDictionary MultiSelectList - GetListItems
再对ASP.NET AJAX(System.Web.Extensions.dll)运行一下:
DataPager - GetQueryStringNavigateUrl DataPagerField - SetDirty DataPagerField - SetDataPager LinqDataSourceView - ReleaseSelectContexts ListViewDeletedEventArgs - SetKeys ListViewDeletedEventArgs - SetValues ListViewInsertedEventArgs - SetValues ListViewUpdatedEventArgs - SetKeys ListViewUpdatedEventArgs - SetNewValues ListViewUpdatedEventArgs - SetOldValues ConvertersCollection - CreateConverters JavaScriptSerializer - ConverterExistsForType JavaScriptSerializer - Serialize UpdatePanelTrigger - SetOwner ScriptDescriptor - RegisterDisposeForDescriptor ScriptComponentDescriptor - RegisterDisposeForDescriptor ScriptManager - AddFrameworkScripts ScriptManager - AddScriptCollections ScriptManager - CreateUniqueScriptKey ScriptManager - GetScriptResourceUrl ScriptManager - RegisterClientScriptBlockInternal ScriptManager - RegisterClientScriptIncludeInternal ScriptManager - RegisterStartupScriptInternal ScriptManagerProxy - CollectScripts ScriptManagerProxy - RegisterServices ScriptReference - DetermineCulture ScriptReference - GetAssembly ScriptReference - GetPath ScriptReference - GetResourceName ScriptReference - ShouldUseDebugScript ServiceReference - Register UpdatePanel - ClearContent UpdatePanel - SetAsyncPostBackMode UpdatePanelTriggerCollection - HasTriggered UpdatePanelTriggerCollection - Initialize
我从这些数据中得出的结论是:
- public类中的internal方法的确不多。
- 使用internal来修饰属性是常见做法。
- 一个方法是否属于某个类,由职责确定。
- 如果某个方法不需要对外公开,则使用internal来修饰。
您的看法如何?
佩服你的执着和对技术的追求