Как правильно структурировать видимость этого класса?
Я пытаюсь улучшить старый класс, который я написал для управления файлом INI, класс содержит 3 подкласса (File
, Key
, Section
) для разделения и организации процедур (процедур для ini в целом, процедур для управления ключами / значениями и процедур для управления именами разделов).
Ну, у меня проблема в том, что в старом классе все члены были общими (props / vars / objects / method) и, очевидно, это могло привести к разногласиям, тогда я хотел бы попытаться улучшить видимость членов, и вот где я застрял.
Текущее использование класса выглядит следующим образом:
INIFileManager.FilePath = "ini filepath"
dim iniexist as boolean = INIFileManager.File.Exist
И использование, которое я хотел бы, должно быть таким:
dim ini as new inifilemanager("ini filepath", textencoding)
dim iniexist as boolean = ini.file.exist
dim another_ini as new inifilemanager("another ini filepath without any kind of conflict with the first instance", textencoding)
dim another_iniexist as boolean = another_ini.file.exist
Ниже приведен соответствующий код для этого примера, я застрял на Exist
метод File
класс, потому что я не могу получить доступ к FilePath
переменная, которая находится в классе верхнего уровня, так как я не устанавливаю эту переменную и Exist
метод как Shared
как я делал на моей старой версии класса...
... Так как я могу улучшить это?
ПРИМЕЧАНИЕ. Пожалуйста, имейте в виду, что у двух других подклассов должен быть метод с именем Exist
и другие методы с такими же именами, как "[Get]
", не только в File
Класс (я не знаю, может ли это быть проблемой, которая может потребовать больше ретуши).
''' <summary>
''' Manages an INI file and it's sections to load/save values.
''' </summary>
Public Class INIFileManager
#Region " Properties "
''' <summary>
''' Indicates the initialization file location.
''' </summary>
Private Property FilePath As String = String.Empty
''' <summary>
''' Indicates the initialization file encoding to read/write.
''' </summary>
Private Property TextEncoding As System.Text.Encoding = System.Text.Encoding.Default
#End Region
#Region " Constructors "
''' <summary>
''' Initializes a new instance of the <see cref="INIFileManager" /> class.
''' </summary>
''' <param name="IniFile">
''' Indicates the initialization file location.
''' </param>
''' <param name="TextEncoding">Indicates a textencoding to read/write the iniinitialization file.</param>
Public Sub New(Optional ByVal IniFile As String = Nothing,
Optional ByVal TextEncoding As System.Text.Encoding = Nothing)
If Not String.IsNullOrEmpty(IniFile) Then
Me.FilePath = IniFile
Else
Me.FilePath = IO.Path.Combine(Application.StartupPath,
Process.GetCurrentProcess().ProcessName & ".ini")
End If
If Not TextEncoding Is Nothing Then
Me.TextEncoding = TextEncoding
End If
End Sub
#End Region
''' <summary>
''' Contains a set of procedures to manage the INI file in a general way.
''' </summary>
Private Class [File]
''' <summary>
''' Checks whether the initialization file exist.
''' </summary>
''' <returns>True if initialization file exist, otherwise False.</returns>
Public Function Exist() As Boolean
Return IO.File.Exists(MyBase.FilePath)
End Function
' More irrelevant methods here that need to access to props and vars of the top-level class...
End Class
' another class here...
' and another class here...
End Class
1 ответ
Я думаю, что часть проблемы в том, что вы раскрываете, а не обрабатываете слишком много деталей иерархии через класс. Другая часть цепляется за 16-битную технологию Windows 1980-х годов, когда сегодня существуют гораздо более надежные механизмы.
У вас есть 4 класса (INIManager, File, Section, Key) для управления 2 битами информации (Key и Value). Поскольку INIManager предназначен только для "размещения" остальных, его можно объединить с File, тем более что операций на уровне файлов не так много. Вы, вероятно, даже не нуждаетесь в [Разделах]. Они существовали только для того, чтобы вы могли хранить повторяющиеся атрибуты для похожих предметов, таких как:
[MainDB]
Path =
File =
Foo =
[Report DB]
Path =
File =
Foo =
Они были предназначены для обеспечения значений INItialization таким образом, чтобы вы могли циклически проходить через набор строк, таких как FILE1, FILE2... и читать несколько разделов в цикле. Для простого набора настроек просто используйте один раздел [Default], чтобы упростить класс и его использование. Тогда вы просто до IniManager и ключей. Преднамеренное разоблачение лежащей в основе иерархии не поддается юзабилити, ИМО.
Таким образом, чтобы сделать то, что вы хотите, вам нужно свойство SECTIONS на INIManager
который выставляет раздел, связанный с материалом. Чтобы поддержать это, вам нужно INISection
класс (в основном методы для разделов) И INISectionS
Класс коллекции. (Некоторые комментарии заставляют меня предположить, что вы хотите загружать все разделы все время и все их ключи, чтобы их можно было удалять и т. Д.).
Если вы действительно хотите что-то вроде Sections().Keys().Method
, вам нужно будет добавить Key
класс и Keys collection class
на IniSection
, Что принесло бы общее количество 5 классам, чтобы управлять 2 частями информации. Конечно, это можно сделать с половиной кода и 1 классом. Большая часть лишнего пуха существует для того, чтобы раскрыть внутреннюю работу, как вы упомянули. Вам также будет весело с Public vs Friend, чтобы не показывать то, что вы не хотите.
У меня нет ничего в коде, чтобы сделать PInvokes; Вопрос касается классового строительства, а не управления INI. Большинство методов пустые и существуют только для того, чтобы увидеть, как они оказываются для плохого пользователя.
Public Class INIManager
' all the gory PInvokes go here
Friend cfgFile As String
Public Property INIExists As Boolean
' this is the bit you seemed to be missing
' A Collection property exposed at the IniMgr level
' containing a collection of Sections. Like matryoshka dolls, inside
' each is a collection of Keys and Values
Public Property Sections As IniSections
' no reason for INI mgr to even exist without a file
Public Sub New(iniFile As String)
cfgFile = iniFile
_INIExists = System.IO.File.Exists(cfgFile)
_Sections = New IniSections(cfgFile)
End Sub
' only worthwhile thing I can think of that a "File"
' class would ever do.
Public Sub RemoveFile()
End Sub
Public Sub Save()
' i think you need to delete the file first so any
' deleted sections disappear. of course sections the code
' does ask for doesnt do any harm either
' iterate IniSections to call a Save there,
' which iterates the keys one by one to save them
Sections.Save(cfgFile)
End Sub
' ****** INISections Class Collection
Public Class IniSections
'Inherits Collection(Of IniSection)
Private Items As Collection(Of IniSection)
Private cfgFile As String
Friend Sub New(file As String)
cfgFile = file
' I am assuming from some comments that you are probably
' loading the entire file to manage it. for that:
If System.IO.File.Exists(cfgFile) Then
' load from GetPrivateProfileSectionNames into the collection
' mybase.Items.Add(section_name)...then
For Each s As IniSection In Items
s.LoadKeyValues(cfgFile)
Next
End If
End Sub
' FRIEND!
Friend Sub Save(cfgfile As String)
For Each s As IniSection In Items
' instruct each section to write the kvps
s.Save(cfgfile)
Next
End Sub
' I dont know why an empty accessor is showing up in Intellisense
Default Public ReadOnly Property Item(name As String) As IniSection
Get
If IndexOfSection(name) = -1 Then
Items.Add(New IniSection(name))
End If
Return Items(IndexOfSection(name))
End Get
End Property
' add a section
Public Function Add(name As String) As IniSection
Dim sec As New IniSection(name)
Items.Add(sec)
Return sec
End Function
' remove a section
Public Sub Remove(name As String)
Items.RemoveAt(IndexOfSection(name))
' the only real way to remove a section is to rewrite the file!
' so to support this method we have to load all sections and all keys
' all the time even if we dont need them so that we can write the
' out the whole file omitting removed keys and sections.
'
' Seriously sir, this kind of junk went to the dustbin with Rubik's Cubes
End Sub
Public Function Exists(secName As String)
Return IndexOfSection(secName) <> -1
End Function
Private Function IndexOfSection(name As String) As Integer
For n As Integer = 0 To Items.Count - 1
' s/b ToLowerInvariant - that makes the screen scroll
If Items(n).SectionName.ToLower = name.ToLower Then
Return n
End If
Next
Return -1
End Function
End Class
End Class
' ************** INISection item class
Public Class IniSection
' mostly methods go here for sections,
' but is the "host" for the keys collections
Private myKeys As Dictionary(Of String, String)
' for a .Keys collection (WHY would the calling code WANT to
' mess with the whole collection???), change to add a Key Class
' and Keys Collection
' interface for Keys
Public Property Keys(name As String) As String
Get
If myKeys.ContainsKey(name) Then
Return myKeys(name)
Else
Return ""
End If
End Get
Set(value As String)
If myKeys.ContainsKey(value) Then
myKeys(value) = value
Else
myKeys.Add(value, value)
End If
End Set
End Property
Public Property SectionName As String
Public Sub New(name As String)
SectionName = name
myKeys = New Dictionary(Of String, String)
End Sub
Public Sub RemoveKey(name As String)
If myKeys.ContainsKey(name) Then
myKeys.Remove(name)
End If
End Sub
Friend Sub Save(inifile As String)
' iterate keys writitng the kvps to the ini
End Sub
' note FRIEND called by the INISection class not the user
Friend Function LoadKeyValues(inifile As String) As Integer
' presumably call GetPrivateProfileSection
' for this SectionName and parse it to
' get the current key=value pairs into myKeys
Return myKeys.Count
End Function
End Class
Пример синтаксиса:
ini = New INIManager("C:\Temp\Ziggy.INI")
Dim foo As String = ini.Sections("foo").Keys("bar")
ini.Sections("ziggy").Keys("foo") = "zoey"
ini.Sections("ziggy").RemoveKey("zacky")
Они не совпадают синтаксически, потому что я не создал класс Key и класс коллекции Keys (5 классов для 2 битов информации безумны). Чтобы изменить его так, чтобы установщик соответствовал, удалите средство доступа Keys и добавьте .ReadKey()
а также SetKey
таким образом, он соответствует синтаксису и сохраняет внутреннюю коллекцию ключей. В итоге вы получите:
ini.Sections("ziggy").RemoveKey("zacky")
ini.Sections("ziggy").ReadKey("ziggy")
ini.Sections("ziggy").SetKey(keyName, "zoey")
По крайней мере, они совпадают синтаксически
ini.Sections.Add("ziggy")
ini.Sections.Remove("zoey")
If ini.Sections.Exists("zacky") Then
Console.Beep()
End If
' causes a cascade from INI -> Sections -> keys to save
ini.Save()