0

私は再びこの他の質問ArrayListで説明したこの問題を解決しようとしています...違いは、今回は他の外部ファイルを使用して回避するなどの代替ソリューションを探していないことです本当の問題、私は本当にMy.SettingsArrayListを使用してこれを修正したいと思います。ここでMy.Settingsについて何が起こっているのかを理解したいと思います!.

問題は、次のように設定を設定した場合です。

ここに画像の説明を入力

次に、設定に対して実行された変更は、次のアプリの実行時に保持されます。このコードは問題を示しています。

Public Class Test

   Private Sub Test_Handler() Handles MyBase.Shown

       ' Create a temporal predefined ArrayList.
       Dim tmpArrayList As New ArrayList(capacity:=10I)
       With tmpArrayList
           .Add({"Item0", 0.0F})
           .Add({"Item1", 0.5F})
       End With

       ' Check the setting status.
       If My.Settings.MRU Is Nothing Then
           Debug.WriteLine("MRU setting is null.")
           Debug.WriteLine("Initializing the Setting...")
           My.Settings.MRU = New ArrayList(capacity:=10I)

       ElseIf My.Settings.MRU.Count = 0 Then
           Debug.WriteLine("MRU is not null but the ArrayList is empty.")
           Debug.WriteLine("Adding some items...")
           My.Settings.MRU = tmpArrayList.Clone

       ElseIf My.Settings.MRU.Count > 0 Then ' This part of the block  will never thrown.
           Debug.WriteLine("MRU setting is OK.")
           Debug.WriteLine("Item Count: " & CStr(My.Settings.MRU.Count))
           Threading.Thread.Sleep(Integer.MaxValue)

       End If

       Debug.WriteLine("Saving any changes")
       My.Settings.Save()

       Debug.WriteLine("Updating any changes")
       My.Settings.Reload()

       Debug.WriteLine(String.Empty)
       Debug.WriteLine("****************************************")
       Debug.WriteLine("Checking again the MRU setting status in...")
       For Count As Integer = 1 To 3
           Debug.WriteLine(CStr(Count) & New String("."c, Count))
           Threading.Thread.Sleep(TimeSpan.FromSeconds(1))
       Next
       Debug.WriteLine("****************************************")
       Debug.WriteLine(String.Empty)

       Me.Test_Handler()

   End Sub

End Class

誰かが ArrayList が実際に保存されない理由を理解するように教えてくれたり、この問題を解決する方法を教えてくれませんか?.

アップデート

この問題を解決するために、このシリアル化可能なクラスを作成しました。

''' <summary>
''' A Class intended to use it as an Item for a MRU item collection that stores the item filepath, with additional info.
''' </summary>
<Serializable()>
Public Class MostRecentUsedItem

    ''' <summary>
    ''' Gets or sets the item filepath.
    ''' </summary>
    ''' <value>The file path.</value>
    Public Property FilePath As String

    ''' <summary>
    ''' (Optionally) Gets or sets the item index.
    ''' </summary>
    ''' <value>The index.</value>
    Public Property Index As Integer

    ''' <summary>
    ''' (Optionally) Gets or sets the item image.
    ''' </summary>
    ''' <value>The image.</value>
    Public Property Img As Bitmap

    ''' <summary>
    ''' (Optionally) Gets or sets the item last-time open date.
    ''' </summary>
    ''' <value>The index.</value>
    Public Property [Date] As Date

    ''' <summary>
    ''' (Optionally) Gets or sets the item tag.
    ''' </summary>
    ''' <value>The tag object.</value>
    Public Property Tag As Object

End Class

また、この問題を解決するために、次のヘルパー関数を作成しました。

''' <summary>
''' Determines whether an object can be XML serialized.
''' </summary>
''' <param name="Object">The object.</param>
''' <returns><c>true</c> if object is XML serializable; otherwise, <c>false</c>.</returns>
Private Function IsObjectSerializable(ByVal [Object] As Object) As Boolean

    Using fs As New IO.FileStream(IO.Path.GetTempFileName, IO.FileMode.Create)

        Dim Serializer As New Xml.Serialization.XmlSerializer([Object].GetType)

        Try
            Serializer.Serialize(fs, [Object])
            Return True

        Catch ex As InvalidOperationException
            Return False

        End Try

    End Using

End Function

このように設定を初期化した時点で、シリアル化可能です。

My.Settings.MRU = New ArrayList

文字列を追加した時点では、まだシリアライズ可能です。

My.Settings.MRU.Add("test string")

しかし、シリアル化可能なクラス、または のような他の種類のデータ型を追加しようとするとString()、ArrayList は次のように非シリアル化を開始します。

My.Settings.MRU.Add({"Collection", "Of", "Strings"})

または、このように:

Dim MRUItem As New MostRecentUsedItem
MRUItem.FilePath = "C:\Test.ext"
My.Settings.MRU.Add(MRUItem)

...したがって、ArrayList の内容は次回の実行時に保持されず、シリアル化できません。

また、設定タイプをSystem.Collections.ArrayListからSystem.Object(必死に) に変更しようとしたので、これを実行できるようになりましたが、問題は解決しません。次のアプリの実行時にコレクションが保存されないことを意味します。

My.Settings.MRU = New List(Of MostRecentUsedItem)
Dim MRUItem As New MostRecentUsedItem
MRUItem.FilePath = "C:\Test.ext"
My.Settings.MRU.Add(MRUItem)
4

3 に答える 3

2

なぜあなたは言うのですか?Integerを のように扱うからですBoolean

プロパティが を返さないため、次の行は常にtrue (条件が満たされている) になります。Count-1

ElseIf Not My.Settings.MRU.Count Then

そのため、この線に到達することはありません。

ElseIf My.Settings.MRU.Count Then 

あなたがすべきことは、あなたのコードをこれに置き換えることです:

ElseIf My.Settings.MRU.Count = 0 Then
Else

そして、いつものように、 に設定Option StrictOnます。


簡単なテスト

For i As Integer = -2 To 2
    Debug.Write(i.ToString())
    If (Not i) Then
        Debug.Write(", Not i")
    ElseIf (i) Then
        Debug.Write(", i")
    End If
    Debug.Write(Environment.NewLine)
Next

結果:

-2, Not i
-1, i
 0, Not i
 1, Not i
 2, Not i
于 2014-08-02T07:39:08.753 に答える
2

アプリケーション設定は複雑な型を XML としてシリアル化するため、特定の型を保存する前に XML としてシリアル化できることを確認する必要があります。

次の方法でデータ型をテストできます。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim tmpArrayList As New ArrayList()
    With tmpArrayList
        .Add({"Item0", 0.0F})
        .Add({"Item1", 0.5F})
    End With

    Dim XmlSerializer1 As New XmlSerializer(tmpArrayList.GetType)
    XmlSerializer1.Serialize(Console.Out, tmpArrayList)
End Sub

このコードは ArrayList のシリアル化に失敗し、次のメッセージを返します。

タイプ 'System.InvalidOperationException' の未処理の例外が System.Xml.dll で発生しました 追加情報: XML ドキュメントの生成中にエラーが発生しました。

ただし、単純なデータ型を ArrayList に格納しようとすると、シリアル化は成功します

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Dim tmpArrayList As New ArrayList()
    With tmpArrayList
        .Add("Item0")
        .Add("Item1")
    End With

    Dim XmlSerializer1 As New XmlSerializer(tmpArrayList.GetType)
    XmlSerializer1.Serialize(Console.Out, tmpArrayList)
End Sub

アプリケーション設定にデータを保存するときも同じことが起こりますが、違いはエラーを返さないことです。

便利なリンク:

編集

DataTable を使用した実装

新しい Windows フォーム プロジェクトを作成し、データ型が System.Data.DataTable のアプリケーション設定に NewMRU という新しい設定を追加して、次のコードを試してください。

Imports System.IO
Imports System.Xml.Serialization
Imports System.Drawing.Imaging

Public Class Form1

    Dim DataTable1 As New DataTable("MySettingsDataTable")

    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        DataTable1.Columns.Add("FilePath", GetType(String))
        DataTable1.Columns.Add("Index", GetType(Integer))
        DataTable1.Columns.Add("Img", GetType(Byte()))
        DataTable1.Columns.Add("Date", GetType(DateTime))
    End Sub

    Private Sub button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        AddPropertyRow("C:\Temp", 1, GetBytesFromBitmap(Me.Icon.ToBitmap), Now)
        AddPropertyRow("C:\Windows", 2, GetBytesFromBitmap(Me.Icon.ToBitmap), Now)

        My.Settings.NewMRU = DataTable1
        My.Settings.Save()

        'MsgBox(IsObjectSerializable(DataTable1))
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If Not My.Settings.NewMRU Is Nothing Then
            Dim i As Integer

            For i = 0 To My.Settings.NewMRU.Rows.Count - 1
                Debug.WriteLine("Row - " & i + 1)
                Debug.WriteLine("FilePath = " & My.Settings.NewMRU.Rows(i).Item("FilePath"))
                Debug.WriteLine("Index = " & My.Settings.NewMRU.Rows(i).Item("Index"))
                Debug.WriteLine("Img bytes count = " & TryCast(My.Settings.NewMRU.Rows(i).Item("Img"), Byte()).Length)
                Debug.WriteLine("Date = " & My.Settings.NewMRU.Rows(i).Item("Date"))
                Debug.WriteLine("")
            Next
        End If

        'PictureBox1.Image = GetBitmapFromBytes(TryCast(My.Settings.NewMRU.Rows(0).Item("Img"), Byte()))
    End Sub

    Private Sub AddPropertyRow(ByVal FilePath As String, ByVal Index As Integer,
                               ByVal Img() As Byte, ByVal [Date] As Date)
        DataTable1.Rows.Add(DataTable1.NewRow)
        Dim RowIndex As Integer = DataTable1.Rows.Count - 1
        DataTable1.Rows(RowIndex).Item("FilePath") = FilePath
        DataTable1.Rows(RowIndex).Item("Index") = Index
        DataTable1.Rows(RowIndex).Item("Img") = Img
        DataTable1.Rows(RowIndex).Item("Date") = [Date]
    End Sub

    Private Function IsObjectSerializable(ByVal [Object] As Object) As Boolean
        Using fs As New IO.FileStream(IO.Path.GetTempFileName, IO.FileMode.Create)
            Dim Serializer As New Xml.Serialization.XmlSerializer([Object].GetType)

            Try
                Serializer.Serialize(fs, [Object])
                Return True
            Catch ex As InvalidOperationException
                Return False
            End Try
        End Using
    End Function

    Private Function GetBytesFromBitmap(ByVal Bitmap1 As Bitmap) As Byte()
        Dim BitmapBytes() As Byte

        Using MemoryStream1 As New MemoryStream()
            Bitmap1.Save(MemoryStream1, ImageFormat.Bmp)
            BitmapBytes = MemoryStream1.GetBuffer()
        End Using

        Return BitmapBytes
    End Function

    Private Function GetBitmapFromBytes(ByVal Bytes As Byte()) As Bitmap
        Dim Bitmap1 As Bitmap = Nothing

        Using MemoryStream1 As New MemoryStream(Bytes, 0, Bytes.Length)
            Bitmap1 = Image.FromStream(MemoryStream1)
        End Using

        Return Bitmap1
    End Function

End Class

Tag プロパティ (シリアル化できないため、コードから省略しました) を使用する場合は、その値 (Item、Value) を DataTable の列として分割する必要があります。

于 2014-08-02T09:37:32.363 に答える
1

これは、My.Settings の ArrayList では機能しません。

.Add({"Item0", 0.0F})
My.Settings.MRU.Add({"Collection", "Of", "Strings"})

何らかの理由で、各要素がデータの配列である場合、 My.Settings はシリアル化できません/できません。最初の例では、寄与する可能性のある型が混在しています。単純なデータと AddRange を使用すると、次のようになります。

.AddRange("Foo1", "Bar2", "FooBar")

またArrayList、MostReccentItem などのオブジェクトをシリアライズしません。理由はわかりませんが、どちらの場合も、予想される My.Settings や内部での処理方法に対してグラフが複雑すぎるようです。

やろうとしていること、またはやろうとしている方法が、My.Settings には複雑すぎることを受け入れる必要があると思います。シリアライザーをいじっている場合は、単純に自分で行うことからの小さな一歩です。

あなたはすでにこれを持っています:

<Serializable()>
Public Class MostRecentUsedItem

    Public Property FilePath As String

    Public Property Index As Integer

    Public Property Img As Bitmap

    Public Property [Date] As Date

    Public Property Tag As Object

End Class

リストを追加して My.Settings コンテナーを置き換え、ファイル名変数を追加します。

Private SettingsFile As String = "C:\Temp\MRUSetTest.bin"
Private MRUList As New List(Of MostRecentUsedItem)

シリアル化コードを保存用に次のように変更します (私は BF を使用しています)。

Dim bf As New BinaryFormatter
Using fs As New FileStream(SettingsFile , FileMode.OpenOrCreate)
    bf.Serialize(fs, MRUList)
End Using

データをロードする別の小さなブロック:

' ToDo: add am If Exists line for the very first time the app runs.

Dim bf As New BinaryFormatter
Using fs As New FileStream(SettingsFile , FileMode.Open)
    MRUList = CType(bf.Deserialize(fs), List(Of MostRecentUsedItem))
End Using

My.Settings には、それを機能させるために全力を尽くすだけの価値がある魔法のようなものは何もありません。上記はリストを保存/ロードしますが、これは重要なポイントです。

より複雑なアプリではMRUList、ユーザー オプションなどの他の設定を保持する別のクラスのメンバーを作成し、その大きなクラスを単純にシリアル化するのは非常に簡単です。

代わりに XMLSerializer を使用できますが、いくつかの制限が追加されます。BinaryFormatter を使用すると、ユーザーは設定ファイルを見つけることができず、簡単にいじることができません。

于 2014-08-02T12:58:50.027 に答える