检查几个程序集中的internal成员
2009-09-13 23:17 Jeffrey Zhao 阅读(12253) 评论(19) 编辑 收藏 举报两个星期前我写了一篇文章谈到一个现象(或是感觉):我发现“类中的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来修饰。
您的看法如何?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
2006-09-13 Sys.ScriptLoader与JS加载进度条的实现