游戏大厅 从基础开始(4)-通过L2X用配置文件反射组装程序(VB only)
很久没更新了。前一阵工作比较充实(就是比较劳累拉~),加上向老赵学习努力瘦身,精神愈发痛苦,难免就懒惰下来。通用倒了,项目突然死亡,On bench了一个月,越发的无力。
好消息也是有的,在m$ msdn论坛努力回答问题,总算当上了个版主。虽然7月申请mvp失败 但是鄙人生性好显摆,好听一点就是爱分享,所以一个咕噜爬起来,又是条好汉。
-----------------------闲话到此为止-----------------------
通过配置来制定程序
说起通过配置来制定程序,不得不提及asp.net
还记得asp.net membership 在web.config中的声明么?
<membership defaultProvider="Main_SqlMemberShipProvider" userIsOnlineTimeWindow="20">
<providers>
<add connectionStringName="GTSCOM_DATABASE_DATAConnectionString1" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" passwordFormat="Clear" applicationName="GTSCOM_Customer" name="Main_SqlMemberShipProvider" type="System.Web.Security.SqlMembershipProvider"/>
</providers>
</membership>
实现不同工厂产生了相同接口的实体类型 MembershipUser
Web.config 就是这么神奇,动不动什么东西就加入进来了~ 编译器往往是根据web.config生成临时cs/vb代码,将其编译结果作为网站的基础 。
编译config提升的性能我可是非常羡慕阿,但是改配置就重新编译这样的事情也经常被诟病。改一个连接字符串整个网站编译5分钟的抱怨,偶尔也会听到。
咱们的游戏大厅,无论是做web game 还是做联众,都不适合
另一种办法就是反射了。
l 大厅组装要求
n 动态加载需要的类库(多种游戏逻辑、房间逻辑、副本类型的工厂载入)
n 策略配置 (策略模式的策略类的载入)
n 通过reload config 修改工厂类和策略类 实现无重启的“下一场游戏生效”配置更新。
n 性能不能受到过多影响
Okay,反射都能胜任。
如果反射得到的都是实现主程序引用中IFactory接口的工厂,返回的都是符合IProductClass 接口的对象,访问成员都通过访问接口进行,初始化以外性能上也不会受影响。看起来十分可行!
另一方面:
l 配置文件要求
n 让我这个懒蛋比较容易读(架构清晰)
n 让我这个懒蛋比较方便写 (编写智能提示)
n 写程序的时候比较舒坦 (编程智能提示)
恩,眼睛里冒贼光了。
VB+L2X, 十分诱惑的选择。
咱们看看下面的xsd文件
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
WGSConfig.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="config">
<xs:complexType>
<xs:all>
<xs:element name="Assemblies" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="Assembly" minOccurs="1">
<xs:complexType>
<xs:attribute name="relativepath" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="LinkListeners" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="LinkListener">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="param" type="KeyValuePair">
</xs:element>
</xs:sequence>
<xs:attribute name="namespace" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Construction" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="Services">
<xs:complexType>
<xs:sequence>
<xs:element name="Service" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Createparam" maxOccurs="unbounded" type="KeyValuePair">
</xs:element>
<xs:element name="ServiceConfig" type="xs:anyType" />
</xs:sequence>
<xs:attribute name="builderNamespace" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="DataAccess" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="compoment">
<xs:complexType>
<xs:sequence>
<xs:element name="ConnectionString" type="xs:string" />
</xs:sequence>
<xs:attribute name="namespace" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Membership" maxOccurs="1" minOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="UserFactory">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="param" type="KeyValuePair">
</xs:element>
</xs:sequence>
<xs:attribute name="namespace" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ChatChannels" maxOccurs="1" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="ChatChannel">
<xs:complexType>
<xs:sequence>
<xs:element name="param" type="KeyValuePair" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
<xs:complexType name="KeyValuePair">
<xs:attribute name="key" type="xs:string" use="required" />
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
</xs:schema>
是不是没什么感觉?
换用vs2005打开,感觉又不一样了.
![](https://images.cnblogs.com/cnblogs_com/waynebaby/configxsd.JPG)
怎么样 是不是很清晰呢?
*vs2008的xsd编辑不太方便, 我们可以用sql2005 带的 vs2005 设置
看看我们的要求表
配置文件要求
n 让我这个懒蛋比较容易读(架构清晰) checked!
n 让我这个懒蛋比较方便写 (编写智能提示)
n 写程序的时候比较舒坦 (编程智能提示)
有了xsd文件,我们就可以更方便得建立自己的XML 配置文件了
在工程中添加一个xml文件 在文件的属性窗口中选择视图(schemas)
![](https://images.cnblogs.com/cnblogs_com/waynebaby/xmlscm.JPG)
把刚才的xsd 选中
Oh yeah~~
我们在编辑xml的时候 可以得到及时的智能提示了@_@
看看我们的要求表
配置文件要求
n 让我这个懒蛋比较容易读(架构清晰) checked!
n 让我这个懒蛋比较方便写 (编写智能提示)checked!
n 写程序的时候比较舒坦 (编程智能提示)
恩...距离完美的解放生产力还差一点点,我们看看编成集成提示支持得怎样
写一条linq 查询所有的factory吧
![](https://images.cnblogs.com/cnblogs_com/waynebaby/lxmlig.JPG)
嗯,,,效果不错. 看来vb 编译器体贴的预编译了我们的xsd文件.
。。。。。。c#啥时候能集成这个阿,哭啊。。。公司项目不让用vb阿。。。。。
看看我们的要求表
配置文件要求
n 让我这个懒蛋比较容易读(架构清晰) checked!
n 让我这个懒蛋比较方便写 (编写智能提示)checked!
n 写程序的时候比较舒坦 (编程智能提示)checked!
于是几乎在不停的智能提示中~~ 我完成了配置类...
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Happy end
Imports System.Collections.Generic
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
Namespace StrategiesNamespace Strategies
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public Class GlobalConfigClass GlobalConfig
Implements IGlobalConfig
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property Config()Property Config() As Xml.Linq.XDocument Implements IGlobalConfig.Config
Get
Return _ConfigFile
End Get
End Property
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Protected _LinkListeners As New List(Of ILinkListener)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property LinkListeners()Property LinkListeners() As System.Collections.Generic.IEnumerable(Of Communicate.ILinkListener) Implements IGlobalConfig.LinkListeners
Get
Return _LinkListeners
End Get
End Property
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Private Shared _Instance As GlobalConfig
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public Shared Property Current()Property Current() As GlobalConfig
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Get
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
If _Instance Is Nothing Then
_Instance = New GlobalConfig
End If
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Return _Instance
End Get
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Set(ByVal value As GlobalConfig)
_Instance = value
End Set
End Property
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Private Shared _ConfigFile As XDocument
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Private Sub New()Sub New()
_Instance = Me
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Dim path As String = System.Diagnostics.Process.GetCurrentProcess.MainModule.FileName
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), "WGSConfig.xml")
If System.IO.File.Exists(path) Then
_ConfigFile = XDocument.Load(path)
Else
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Throw New Exception(WayneGameSolution.Resources.CurrentCurlture.FileLoad_ConigLoad_ErrorMessage)
End If
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Dim tmps = From n In Config.<config>.<Assemblies>.<Assembly> _
Select n.@relativepath
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
For Each s As String In tmps
AttachTypes(s)
Next
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Dim listeners = From xn As XElement In Config.<config>.<LinkListeners>.<LinkListener> _
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Select lk = CType(CreateObject(xn.@Namespace ,namespace, xn), ILinkListener) Where Not lk Is Nothing
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
_LinkListeners.AddRange(listeners)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
'For Each xn As XElement In Config.<config>.<Construction>.<Services>.<Service>
' _Serivces.Add(CreateObject(xn.@builderNamespace, xn))
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
'Next
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Dim services = From xn As XElement In Config.<config>.<Construction>.<Services>.<Service> _
Select builder = CType(CreateObject(xn.@builderNamespace, _
xn), IGameServiceBuilder) Where Not builder Is Nothing _
Select service = builder.CreateService()
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
_Serivces.AddRange(services)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
_UserFactory = CreateObject(Config.<config>.<Membership>.<UserFactory>.@Namespace ,namespace, _
Config.<config>.<Membership>.<UserFactory>.First)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
_PackFlagEnumType = GetTypeFrom(Config.<config>.<PackEnum>.@Namespace )namespace)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
'Sub New(ByVal id As String, ByVal name As String, ByVal timeoutSecond As Int32, ByVal type As IChatChannel.ChannelType)
Dim channelsQuery = From elm As XElement In Config.<config>.<ChatChannels>.<ChatChannel> Select New ChatChannel(elm.@channelID, elm.@channelName, CInt(elm.@timeout), CInt(elm.@type))
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
For Each chn As Chat.IChatChannel In channelsQuery
Me.ChatChannels.Add(chn.ID, chn)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Next
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
End Sub
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public Function params2Dic()Function params2Dic(ByVal nodes As IEnumerable(Of XElement)) As Dictionary(Of String, String)
Dim dic As New Sync.SyncDictionary(Of String, String)
For Each xn As XElement In nodes
dic.Add(xn.@key, xn.@value)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Next
Return dic
End Function
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public Shared Function GetTypeFrom()Function GetTypeFrom(ByVal name_space As String) As Type
Dim T As Type = Nothing
T = Type.GetType(name_space, False, True)
If T Is Nothing Then _ReflectTypes.TryGetValue(name_space, T)
Return T
End Function
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public Shared Function CreateObject_Instance()Function CreateObject_Instance(ByVal name_space As String, ByVal ParamArray parms() As Object)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Dim T As Type = GetTypeFrom(name_space)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
If T Is Nothing Then Return Nothing
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Return Activator.CreateInstance(T, parms)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
End Function
Private Shared _ReflectTypes As New Sync.SyncDictionary(Of String, Type)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property ReflectTypes()Property ReflectTypes() As System.Collections.Generic.IDictionary(Of String, System.Type) Implements IGlobalConfig.ReflectTypes
Get
Return _ReflectTypes
End Get
End Property
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public Function AttachTypes()Function AttachTypes(ByVal relativePath As String) As Boolean Implements IGlobalConfig.AttachTypes
Dim path As String = System.Diagnostics.Process.GetCurrentProcess.MainModule.FileName
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), relativePath)
Dim asm As System.Reflection.Assembly = Nothing
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
asm = System.Reflection.Assembly.LoadFrom(path)
If asm Is Nothing Then
Return False
Else
Dim tps As Type() = asm.GetTypes()
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
For Each T As Type In tps
Dim FullName As String = T.Namespace & "." & T.Name
If Not _ReflectTypes.ContainsKey(FullName) Then _ReflectTypes.Add(FullName, T)
Next
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
End If
Return True
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
End Function
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public Function CreateObject()Function CreateObject(ByVal name_space As String, ByVal ParamArray parms() As Object) As Object Implements IGlobalConfig.CreateObject
Return CreateObject_Instance(name_space, parms)
End Function
Private _ChatChannels As New Generic.Sync.SyncDictionary(Of String, Chat.IChatChannel)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property ChatChannels()Property ChatChannels() As System.Collections.Generic.IDictionary(Of String, Chat.IChatChannel) Implements IGlobalConfig.ChatChannels
Get
Return _ChatChannels
End Get
End Property
Private _LogedUsers As New Generic.Sync.SyncDictionary(Of String, IUser)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property LogedUsers()Property LogedUsers() As System.Collections.Generic.IDictionary(Of String, Membership.IUser) Implements IGlobalConfig.LogedUsers
Get
Return _LogedUsers
End Get
End Property
Private _MountedAreas As New Generic.Sync.SyncDictionary(Of String, IArea)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property MountedAreas()Property MountedAreas() As System.Collections.Generic.IDictionary(Of String, Construct.IArea) Implements IGlobalConfig.MountedAreas
Get
Return _MountedAreas
End Get
End Property
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Private _PackFlagEnumType As Type
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property PackFlagEnumType()Property PackFlagEnumType() As System.Type Implements IGlobalConfig.PackFlagEnumType
Get
Return _PackFlagEnumType
End Get
End Property
Friend _Serivces As New List(Of Services.IGameService)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property Serivces()Property Serivces() As System.Collections.Generic.IEnumerable(Of Services.IGameService) Implements IGlobalConfig.Serivces
Get
Return _Serivces
End Get
End Property
Protected _AreaFactory As Construct.IAreaFactory
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property AreaFactory()Property AreaFactory() As Construct.IAreaFactory Implements IGlobalConfig.AreaFactory
Get
Return _AreaFactory
End Get
End Property
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Protected _UserFactory As Membership.IUserFactory
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Public ReadOnly Property UserFactory()Property UserFactory() As Membership.IUserFactory Implements IGlobalConfig.UserFactory
Get
Return _UserFactory
End Get
End Property
End Class
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
End Namespace
这一章没有什么技术含量 但是懒惰驱动的工作方式已经深入我的人格。 这也是分享嘛